group_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: group_cmd.cpp 24568 2012-10-01 19:43:10Z frosch $ */
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 IsUniqueGroupName(const char *name)
00344 {
00345   const Group *g;
00346 
00347   FOR_ALL_GROUPS(g) {
00348     if (g->name != NULL && 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 (!IsUniqueGroupName(text)) 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 }