# group_cmd.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 "cmd_helper.h"
    00014 #include "command_func.h"
    00015 #include "train.h"
    00016 #include "vehiclelist.h"
    00017 #include "vehicle_func.h"
    00018 #include "autoreplace_base.h"
    00019 #include "autoreplace_func.h"
    00020 #include "string_func.h"
    00021 #include "company_func.h"
    00022 #include "core/pool_func.hpp"
    00023 #include "order_backup.h"
    00024
    00025 #include "table/strings.h"
    00026
    00027 GroupID _new_group_id;
    00028
    00029 GroupPool _group_pool("Group");
    00030 INSTANTIATE_POOL_METHODS(Group)
    00031
    00032 GroupStatistics::GroupStatistics()
    00033 {
    00034   this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
    00035 }
    00036
    00037 GroupStatistics::~GroupStatistics()
    00038 {
    00039   free(this->num_engines);
    00040 }
    00041
    00045 void GroupStatistics::Clear()
    00046 {
    00047   this->num_vehicle = 0;
    00048   this->num_profit_vehicle = 0;
    00049   this->profit_last_year = 0;
    00050
    00051   /* This is also called when NewGRF change. So the number of engines might have changed. Reallocate. */
    00052   free(this->num_engines);
    00053   this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
    00054 }
    00055
    00063 /* static */ GroupStatistics &GroupStatistics::Get(CompanyID company, GroupID id_g, VehicleType type)
    00064 {
    00065   if (Group::IsValidID(id_g)) {
    00066     Group *g = Group::Get(id_g);
    00067     assert(g->owner == company);
    00068     assert(g->vehicle_type == type);
    00069     return g->statistics;
    00070   }
    00071
    00072   if (IsDefaultGroupID(id_g)) return Company::Get(company)->group_default[type];
    00073   if (IsAllGroupID(id_g)) return Company::Get(company)->group_all[type];
    00074
    00075   NOT_REACHED();
    00076 }
    00077
    00083 /* static */ GroupStatistics &GroupStatistics::Get(const Vehicle *v)
    00084 {
    00085   return GroupStatistics::Get(v->owner, v->group_id, v->type);
    00086 }
    00087
    00093 /* static */ GroupStatistics &GroupStatistics::GetAllGroup(const Vehicle *v)
    00094 {
    00095   return GroupStatistics::Get(v->owner, ALL_GROUP, v->type);
    00096 }
    00097
    00101 /* static */ void GroupStatistics::UpdateAfterLoad()
    00102 {
    00103   /* Set up the engine count for all companies */
    00104   Company *c;
    00105   FOR_ALL_COMPANIES(c) {
    00106     for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
    00107       c->group_all[type].Clear();
    00108       c->group_default[type].Clear();
    00109     }
    00110   }
    00111
    00112   /* Recalculate */
    00113   Group *g;
    00114   FOR_ALL_GROUPS(g) {
    00115     g->statistics.Clear();
    00116   }
    00117
    00118   const Vehicle *v;
    00119   FOR_ALL_VEHICLES(v) {
    00120     if (!v->IsEngineCountable()) continue;
    00121
    00122     GroupStatistics::CountEngine(v, 1);
    00123     if (v->IsPrimaryVehicle()) GroupStatistics::CountVehicle(v, 1);
    00124   }
    00125
    00126   FOR_ALL_COMPANIES(c) {
    00127     GroupStatistics::UpdateAutoreplace(c->index);
    00128   }
    00129 }
    00130
    00136 /* static */ void GroupStatistics::CountVehicle(const Vehicle *v, int delta)
    00137 {
    00138   assert(delta == 1 || delta == -1);
    00139
    00140   GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
    00141   GroupStatistics &stats = GroupStatistics::Get(v);
    00142
    00143   stats_all.num_vehicle += delta;
    00144   stats.num_vehicle += delta;
    00145
    00146   if (v->age > VEHICLE_PROFIT_MIN_AGE) {
    00147     stats_all.num_profit_vehicle += delta;
    00148     stats_all.profit_last_year += v->GetDisplayProfitLastYear() * delta;
    00149     stats.num_profit_vehicle += delta;
    00150     stats.profit_last_year += v->GetDisplayProfitLastYear() * delta;
    00151   }
    00152 }
    00153
    00159 /* static */ void GroupStatistics::CountEngine(const Vehicle *v, int delta)
    00160 {
    00161   assert(delta == 1 || delta == -1);
    00162   GroupStatistics::GetAllGroup(v).num_engines[v->engine_type] += delta;
    00163   GroupStatistics::Get(v).num_engines[v->engine_type] += delta;
    00164 }
    00165
    00169 /* static */ void GroupStatistics::VehicleReachedProfitAge(const Vehicle *v)
    00170 {
    00171   GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
    00172   GroupStatistics &stats = GroupStatistics::Get(v);
    00173
    00174   stats_all.num_profit_vehicle++;
    00175   stats_all.profit_last_year += v->GetDisplayProfitLastYear();
    00176   stats.num_profit_vehicle++;
    00177   stats.profit_last_year += v->GetDisplayProfitLastYear();
    00178 }
    00179
    00183 /* static */ void GroupStatistics::UpdateProfits()
    00184 {
    00185   /* Set up the engine count for all companies */
    00186   Company *c;
    00187   FOR_ALL_COMPANIES(c) {
    00188     for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
    00189       c->group_all[type].ClearProfits();
    00190       c->group_default[type].ClearProfits();
    00191     }
    00192   }
    00193
    00194   /* Recalculate */
    00195   Group *g;
    00196   FOR_ALL_GROUPS(g) {
    00197     g->statistics.ClearProfits();
    00198   }
    00199
    00200   const Vehicle *v;
    00201   FOR_ALL_VEHICLES(v) {
    00202     if (v->IsPrimaryVehicle() && v->age > VEHICLE_PROFIT_MIN_AGE) GroupStatistics::VehicleReachedProfitAge(v);
    00203   }
    00204 }
    00205
    00210 /* static */ void GroupStatistics::UpdateAutoreplace(CompanyID company)
    00211 {
    00212   /* Set up the engine count for all companies */
    00213   Company *c = Company::Get(company);
    00214   for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
    00215     c->group_all[type].ClearAutoreplace();
    00216     c->group_default[type].ClearAutoreplace();
    00217   }
    00218
    00219   /* Recalculate */
    00220   Group *g;
    00221   FOR_ALL_GROUPS(g) {
    00222     if (g->owner != company) continue;
    00223     g->statistics.ClearAutoreplace();
    00224   }
    00225
    00226   for (EngineRenewList erl = c->engine_renew_list; erl != NULL; erl = erl->next) {
    00227     const Engine *e = Engine::Get(erl->from);
    00228     GroupStatistics &stats = GroupStatistics::Get(company, erl->group_id, e->type);
    00229     if (!stats.autoreplace_defined) {
    00230       stats.autoreplace_defined = true;
    00231       stats.autoreplace_finished = true;
    00232     }
    00233     if (stats.num_engines[erl->from] > 0) stats.autoreplace_finished = false;
    00234   }
    00235 }
    00236
    00244 static inline void UpdateNumEngineGroup(const Vehicle *v, GroupID old_g, GroupID new_g)
    00245 {
    00246   if (old_g != new_g) {
    00247     /* Decrease the num engines in the old group */
    00248     GroupStatistics::Get(v->owner, old_g, v->type).num_engines[v->engine_type]--;
    00249
    00250     /* Increase the num engines in the new group */
    00251     GroupStatistics::Get(v->owner, new_g, v->type).num_engines[v->engine_type]++;
    00252   }
    00253 }
    00254
    00255
    00256
    00257 Group::Group(Owner owner)
    00258 {
    00259   this->owner = owner;
    00260 }
    00261
    00262 Group::~Group()
    00263 {
    00264   free(this->name);
    00265 }
    00266
    00267
    00277 CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00278 {
    00279   VehicleType vt = Extract<VehicleType, 0, 3>(p1);
    00280   if (!IsCompanyBuildableVehicleType(vt)) return CMD_ERROR;
    00281
    00282   if (!Group::CanAllocateItem()) return CMD_ERROR;
    00283
    00284   if (flags & DC_EXEC) {
    00285     Group *g = new Group(_current_company);
    00286     g->replace_protection = false;
    00287     g->vehicle_type = vt;
    00288
    00289     _new_group_id = g->index;
    00290
    00291     InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
    00292   }
    00293
    00294   return CommandCost();
    00295 }
    00296
    00297
    00308 CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00309 {
    00310   Group *g = Group::GetIfValid(p1);
    00311   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
    00312
    00313   /* Remove all vehicles from the group */
    00314   DoCommand(0, p1, 0, flags, CMD_REMOVE_ALL_VEHICLES_GROUP);
    00315
    00316   if (flags & DC_EXEC) {
    00317     /* Update backupped orders if needed */
    00318     OrderBackup::ClearGroup(g->index);
    00319
    00320     /* If we set an autoreplace for the group we delete, remove it. */
    00321     if (_current_company < MAX_COMPANIES) {
    00322       Company *c;
    00323       EngineRenew *er;
    00324
    00325       c = Company::Get(_current_company);
    00326       FOR_ALL_ENGINE_RENEWS(er) {
    00327         if (er->group_id == g->index) RemoveEngineReplacementForCompany(c, er->from, g->index, flags);
    00328       }
    00329     }
    00330
    00331     VehicleType vt = g->vehicle_type;
    00332
    00333     /* Delete the Replace Vehicle Windows */
    00334     DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
    00335     delete g;
    00336
    00337     InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
    00338   }
    00339
    00340   return CommandCost();
    00341 }
    00342
    00343 static bool IsUniqueGroupNameForVehicleType(const char *name, VehicleType type)
    00344 {
    00345   const Group *g;
    00346
    00347   FOR_ALL_GROUPS(g) {
    00348     if (g->name != NULL && g->vehicle_type == type && strcmp(g->name, name) == 0) return false;
    00349   }
    00350
    00351   return true;
    00352 }
    00353
    00364 CommandCost CmdRenameGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00365 {
    00366   Group *g = Group::GetIfValid(p1);
    00367   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
    00368
    00369   bool reset = StrEmpty(text);
    00370
    00371   if (!reset) {
    00372     if (Utf8StringLength(text) >= MAX_LENGTH_GROUP_NAME_CHARS) return CMD_ERROR;
    00373     if (!IsUniqueGroupNameForVehicleType(text, g->vehicle_type)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
    00374   }
    00375
    00376   if (flags & DC_EXEC) {
    00377     /* Delete the old name */
    00378     free(g->name);
    00379     /* Assign the new one */
    00380     g->name = reset ? NULL : strdup(text);
    00381
    00382     SetWindowDirty(WC_REPLACE_VEHICLE, g->vehicle_type);
    00383     InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
    00384   }
    00385
    00386   return CommandCost();
    00387 }
    00388
    00389
    00395 static void AddVehicleToGroup(Vehicle *v, GroupID new_g)
    00396 {
    00397   GroupStatistics::CountVehicle(v, -1);
    00398
    00399   switch (v->type) {
    00400     default: NOT_REACHED();
    00401     case VEH_TRAIN:
    00402       SetTrainGroupID(Train::From(v), new_g);
    00403       break;
    00404
    00405     case VEH_ROAD:
    00406     case VEH_SHIP:
    00407     case VEH_AIRCRAFT:
    00408       if (v->IsEngineCountable()) UpdateNumEngineGroup(v, v->group_id, new_g);
    00409       v->group_id = new_g;
    00410       break;
    00411   }
    00412
    00413   GroupStatistics::CountVehicle(v, 1);
    00414 }
    00415
    00428 CommandCost CmdAddVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00429 {
    00430   Vehicle *v = Vehicle::GetIfValid(GB(p2, 0, 20));
    00431   GroupID new_g = p1;
    00432
    00433   if (v == NULL || (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g) && new_g != NEW_GROUP)) return CMD_ERROR;
    00434
    00435   if (Group::IsValidID(new_g)) {
    00436     Group *g = Group::Get(new_g);
    00437     if (g->owner != _current_company || g->vehicle_type != v->type) return CMD_ERROR;
    00438   }
    00439
    00440   if (v->owner != _current_company || !v->IsPrimaryVehicle()) return CMD_ERROR;
    00441
    00442   if (new_g == NEW_GROUP) {
    00443     /* Create new group. */
    00444     CommandCost ret = CmdCreateGroup(0, flags, v->type, 0, NULL);
    00445     if (ret.Failed()) return ret;
    00446
    00447     new_g = _new_group_id;
    00448   }
    00449
    00450   if (flags & DC_EXEC) {
    00451     AddVehicleToGroup(v, new_g);
    00452
    00453     if (HasBit(p2, 31)) {
    00454       /* Add vehicles in the shared order list as well. */
    00455       for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
    00456         if (v2->group_id != new_g) AddVehicleToGroup(v2, new_g);
    00457       }
    00458     }
    00459
    00460     GroupStatistics::UpdateAutoreplace(v->owner);
    00461
    00462     /* Update the Replace Vehicle Windows */
    00463     SetWindowDirty(WC_REPLACE_VEHICLE, v->type);
    00464     InvalidateWindowData(GetWindowClassForVehicleType(v->type), VehicleListIdentifier(VL_GROUP_LIST, v->type, _current_company).Pack());
    00465   }
    00466
    00467   return CommandCost();
    00468 }
    00469
    00480 CommandCost CmdAddSharedVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00481 {
    00482   VehicleType type = Extract<VehicleType, 0, 3>(p2);
    00483   GroupID id_g = p1;
    00484   if (!Group::IsValidID(id_g) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;
    00485
    00486   if (flags & DC_EXEC) {
    00487     Vehicle *v;
    00488
    00489     /* Find the first front engine which belong to the group id_g
    00490      * then add all shared vehicles of this front engine to the group id_g */
    00491     FOR_ALL_VEHICLES(v) {
    00492       if (v->type == type && v->IsPrimaryVehicle()) {
    00493         if (v->group_id != id_g) continue;
    00494
    00495         /* For each shared vehicles add it to the group */
    00496         for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
    00497           if (v2->group_id != id_g) DoCommand(tile, id_g, v2->index, flags, CMD_ADD_VEHICLE_GROUP, text);
    00498         }
    00499       }
    00500     }
    00501
    00502     InvalidateWindowData(GetWindowClassForVehicleType(type), VehicleListIdentifier(VL_GROUP_LIST, type, _current_company).Pack());
    00503   }
    00504
    00505   return CommandCost();
    00506 }
    00507
    00508
    00519 CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00520 {
    00521   GroupID old_g = p1;
    00522   Group *g = Group::GetIfValid(old_g);
    00523
    00524   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
    00525
    00526   if (flags & DC_EXEC) {
    00527     Vehicle *v;
    00528
    00529     /* Find each Vehicle that belongs to the group old_g and add it to the default group */
    00530     FOR_ALL_VEHICLES(v) {
    00531       if (v->IsPrimaryVehicle()) {
    00532         if (v->group_id != old_g) continue;
    00533
    00534         /* Add The Vehicle to the default group */
    00535         DoCommand(tile, DEFAULT_GROUP, v->index, flags, CMD_ADD_VEHICLE_GROUP, text);
    00536       }
    00537     }
    00538
    00539     InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
    00540   }
    00541
    00542   return CommandCost();
    00543 }
    00544
    00545
    00557 CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00558 {
    00559   Group *g = Group::GetIfValid(p1);
    00560   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
    00561
    00562   if (flags & DC_EXEC) {
    00563     g->replace_protection = HasBit(p2, 0);
    00564
    00565     SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
    00566     InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type);
    00567   }
    00568
    00569   return CommandCost();
    00570 }
    00571
    00577 void RemoveVehicleFromGroup(const Vehicle *v)
    00578 {
    00579   if (!v->IsPrimaryVehicle()) return;
    00580
    00581   if (!IsDefaultGroupID(v->group_id)) GroupStatistics::CountVehicle(v, -1);
    00582 }
    00583
    00584
    00591 void SetTrainGroupID(Train *v, GroupID new_g)
    00592 {
    00593   if (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g)) return;
    00594
    00595   assert(v->IsFrontEngine() || IsDefaultGroupID(new_g));
    00596
    00597   for (Vehicle *u = v; u != NULL; u = u->Next()) {
    00598     if (u->IsEngineCountable()) UpdateNumEngineGroup(u, u->group_id, new_g);
    00599
    00600     u->group_id = new_g;
    00601   }
    00602
    00603   /* Update the Replace Vehicle Windows */
    00604   GroupStatistics::UpdateAutoreplace(v->owner);
    00605   SetWindowDirty(WC_REPLACE_VEHICLE, VEH_TRAIN);
    00606 }
    00607
    00608
    00616 void UpdateTrainGroupID(Train *v)
    00617 {
    00618   assert(v->IsFrontEngine() || v->IsFreeWagon());
    00619
    00620   GroupID new_g = v->IsFrontEngine() ? v->group_id : (GroupID)DEFAULT_GROUP;
    00621   for (Vehicle *u = v; u != NULL; u = u->Next()) {
    00622     if (u->IsEngineCountable()) UpdateNumEngineGroup(u, u->group_id, new_g);
    00623
    00624     u->group_id = new_g;
    00625   }
    00626
    00627   /* Update the Replace Vehicle Windows */
    00628   GroupStatistics::UpdateAutoreplace(v->owner);
    00629   SetWindowDirty(WC_REPLACE_VEHICLE, VEH_TRAIN);
    00630 }
    00631
    00640 uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
    00641 {
    00642   const Engine *e = Engine::Get(id_e);
    00643   return GroupStatistics::Get(company, id_g, e->type).num_engines[id_e];
    00644 }
    00645
    00646 void RemoveAllGroupsForCompany(const CompanyID company)
    00647 {
    00648   Group *g;
    00649
    00650   FOR_ALL_GROUPS(g) {
    00651     if (company == g->owner) delete g;
    00652   }
    00653 }