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 "group.h"
00016 #include "train.h"
00017 #include "vehicle_gui.h"
00018 #include "vehiclelist.h"
00019 #include "window_func.h"
00020 #include "vehicle_func.h"
00021 #include "autoreplace_base.h"
00022 #include "autoreplace_func.h"
00023 #include "string_func.h"
00024 #include "company_func.h"
00025 #include "core/pool_func.hpp"
00026 #include "order_backup.h"
00027 
00028 #include "table/strings.h"
00029 
00030 GroupID _new_group_id;
00031 
00032 GroupPool _group_pool("Group");
00033 INSTANTIATE_POOL_METHODS(Group)
00034 
00035 GroupStatistics::GroupStatistics()
00036 {
00037   this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
00038 }
00039 
00040 GroupStatistics::~GroupStatistics()
00041 {
00042   free(this->num_engines);
00043 }
00044 
00048 void GroupStatistics::Clear()
00049 {
00050   this->num_vehicle = 0;
00051   this->num_profit_vehicle = 0;
00052   this->profit_last_year = 0;
00053 
00054   /* This is also called when NewGRF change. So the number of engines might have changed. Reallocate. */
00055   free(this->num_engines);
00056   this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
00057 }
00058 
00066 /* static */ GroupStatistics &GroupStatistics::Get(CompanyID company, GroupID id_g, VehicleType type)
00067 {
00068   if (Group::IsValidID(id_g)) {
00069     Group *g = Group::Get(id_g);
00070     assert(g->owner == company);
00071     assert(g->vehicle_type == type);
00072     return g->statistics;
00073   }
00074 
00075   if (IsDefaultGroupID(id_g)) return Company::Get(company)->group_default[type];
00076   if (IsAllGroupID(id_g)) return Company::Get(company)->group_all[type];
00077 
00078   NOT_REACHED();
00079 }
00080 
00086 /* static */ GroupStatistics &GroupStatistics::Get(const Vehicle *v)
00087 {
00088   return GroupStatistics::Get(v->owner, v->group_id, v->type);
00089 }
00090 
00096 /* static */ GroupStatistics &GroupStatistics::GetAllGroup(const Vehicle *v)
00097 {
00098   return GroupStatistics::Get(v->owner, ALL_GROUP, v->type);
00099 }
00100 
00104 /* static */ void GroupStatistics::UpdateAfterLoad()
00105 {
00106   /* Set up the engine count for all companies */
00107   Company *c;
00108   FOR_ALL_COMPANIES(c) {
00109     for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
00110       c->group_all[type].Clear();
00111       c->group_default[type].Clear();
00112     }
00113   }
00114 
00115   /* Recalculate */
00116   Group *g;
00117   FOR_ALL_GROUPS(g) {
00118     g->statistics.Clear();
00119   }
00120 
00121   const Vehicle *v;
00122   FOR_ALL_VEHICLES(v) {
00123     if (!v->IsEngineCountable()) continue;
00124 
00125     GroupStatistics::CountEngine(v, 1);
00126     if (v->IsPrimaryVehicle()) GroupStatistics::CountVehicle(v, 1);
00127   }
00128 
00129   FOR_ALL_COMPANIES(c) {
00130     GroupStatistics::UpdateAutoreplace(c->index);
00131   }
00132 }
00133 
00139 /* static */ void GroupStatistics::CountVehicle(const Vehicle *v, int delta)
00140 {
00141   assert(delta == 1 || delta == -1);
00142 
00143   GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
00144   GroupStatistics &stats = GroupStatistics::Get(v);
00145 
00146   stats_all.num_vehicle += delta;
00147   stats.num_vehicle += delta;
00148 
00149   if (v->age > VEHICLE_PROFIT_MIN_AGE) {
00150     stats_all.num_profit_vehicle += delta;
00151     stats_all.profit_last_year += v->GetDisplayProfitLastYear() * delta;
00152     stats.num_profit_vehicle += delta;
00153     stats.profit_last_year += v->GetDisplayProfitLastYear() * delta;
00154   }
00155 }
00156 
00162 /* static */ void GroupStatistics::CountEngine(const Vehicle *v, int delta)
00163 {
00164   assert(delta == 1 || delta == -1);
00165   GroupStatistics::GetAllGroup(v).num_engines[v->engine_type] += delta;
00166   GroupStatistics::Get(v).num_engines[v->engine_type] += delta;
00167 }
00168 
00172 /* static */ void GroupStatistics::VehicleReachedProfitAge(const Vehicle *v)
00173 {
00174   GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v);
00175   GroupStatistics &stats = GroupStatistics::Get(v);
00176 
00177   stats_all.num_profit_vehicle++;
00178   stats_all.profit_last_year += v->GetDisplayProfitLastYear();
00179   stats.num_profit_vehicle++;
00180   stats.profit_last_year += v->GetDisplayProfitLastYear();
00181 }
00182 
00186 /* static */ void GroupStatistics::UpdateProfits()
00187 {
00188   /* Set up the engine count for all companies */
00189   Company *c;
00190   FOR_ALL_COMPANIES(c) {
00191     for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
00192       c->group_all[type].ClearProfits();
00193       c->group_default[type].ClearProfits();
00194     }
00195   }
00196 
00197   /* Recalculate */
00198   Group *g;
00199   FOR_ALL_GROUPS(g) {
00200     g->statistics.ClearProfits();
00201   }
00202 
00203   const Vehicle *v;
00204   FOR_ALL_VEHICLES(v) {
00205     if (v->IsPrimaryVehicle() && v->age > VEHICLE_PROFIT_MIN_AGE) GroupStatistics::VehicleReachedProfitAge(v);
00206   }
00207 }
00208 
00213 /* static */ void GroupStatistics::UpdateAutoreplace(CompanyID company)
00214 {
00215   /* Set up the engine count for all companies */
00216   Company *c = Company::Get(company);
00217   for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
00218     c->group_all[type].ClearAutoreplace();
00219     c->group_default[type].ClearAutoreplace();
00220   }
00221 
00222   /* Recalculate */
00223   Group *g;
00224   FOR_ALL_GROUPS(g) {
00225     if (g->owner != company) continue;
00226     g->statistics.ClearAutoreplace();
00227   }
00228 
00229   for (EngineRenewList erl = c->engine_renew_list; erl != NULL; erl = erl->next) {
00230     const Engine *e = Engine::Get(erl->from);
00231     GroupStatistics &stats = GroupStatistics::Get(company, erl->group_id, e->type);
00232     if (!stats.autoreplace_defined) {
00233       stats.autoreplace_defined = true;
00234       stats.autoreplace_finished = true;
00235     }
00236     if (stats.num_engines[erl->from] > 0) stats.autoreplace_finished = false;
00237   }
00238 }
00239 
00247 static inline void UpdateNumEngineGroup(const Vehicle *v, GroupID old_g, GroupID new_g)
00248 {
00249   if (old_g != new_g) {
00250     /* Decrease the num engines in the old group */
00251     GroupStatistics::Get(v->owner, old_g, v->type).num_engines[v->engine_type]--;
00252 
00253     /* Increase the num engines in the new group */
00254     GroupStatistics::Get(v->owner, new_g, v->type).num_engines[v->engine_type]++;
00255   }
00256 }
00257 
00258 
00259 
00260 Group::Group(Owner owner)
00261 {
00262   this->owner = owner;
00263 }
00264 
00265 Group::~Group()
00266 {
00267   free(this->name);
00268 }
00269 
00270 
00280 CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00281 {
00282   VehicleType vt = Extract<VehicleType, 0, 3>(p1);
00283   if (!IsCompanyBuildableVehicleType(vt)) return CMD_ERROR;
00284 
00285   if (!Group::CanAllocateItem()) return CMD_ERROR;
00286 
00287   if (flags & DC_EXEC) {
00288     Group *g = new Group(_current_company);
00289     g->replace_protection = false;
00290     g->vehicle_type = vt;
00291 
00292     _new_group_id = g->index;
00293 
00294     InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
00295   }
00296 
00297   return CommandCost();
00298 }
00299 
00300 
00311 CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00312 {
00313   Group *g = Group::GetIfValid(p1);
00314   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
00315 
00316   /* Remove all vehicles from the group */
00317   DoCommand(0, p1, 0, flags, CMD_REMOVE_ALL_VEHICLES_GROUP);
00318 
00319   if (flags & DC_EXEC) {
00320     /* Update backupped orders if needed */
00321     OrderBackup::ClearGroup(g->index);
00322 
00323     /* If we set an autoreplace for the group we delete, remove it. */
00324     if (_current_company < MAX_COMPANIES) {
00325       Company *c;
00326       EngineRenew *er;
00327 
00328       c = Company::Get(_current_company);
00329       FOR_ALL_ENGINE_RENEWS(er) {
00330         if (er->group_id == g->index) RemoveEngineReplacementForCompany(c, er->from, g->index, flags);
00331       }
00332     }
00333 
00334     VehicleType vt = g->vehicle_type;
00335 
00336     /* Delete the Replace Vehicle Windows */
00337     DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
00338     delete g;
00339 
00340     InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
00341   }
00342 
00343   return CommandCost();
00344 }
00345 
00346 static bool IsUniqueGroupName(const char *name)
00347 {
00348   const Group *g;
00349 
00350   FOR_ALL_GROUPS(g) {
00351     if (g->name != NULL && strcmp(g->name, name) == 0) return false;
00352   }
00353 
00354   return true;
00355 }
00356 
00367 CommandCost CmdRenameGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00368 {
00369   Group *g = Group::GetIfValid(p1);
00370   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
00371 
00372   bool reset = StrEmpty(text);
00373 
00374   if (!reset) {
00375     if (Utf8StringLength(text) >= MAX_LENGTH_GROUP_NAME_CHARS) return CMD_ERROR;
00376     if (!IsUniqueGroupName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
00377   }
00378 
00379   if (flags & DC_EXEC) {
00380     /* Delete the old name */
00381     free(g->name);
00382     /* Assign the new one */
00383     g->name = reset ? NULL : strdup(text);
00384 
00385     InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
00386   }
00387 
00388   return CommandCost();
00389 }
00390 
00391 
00403 CommandCost CmdAddVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00404 {
00405   Vehicle *v = Vehicle::GetIfValid(p2);
00406   GroupID new_g = p1;
00407 
00408   if (v == NULL || (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g))) return CMD_ERROR;
00409 
00410   if (Group::IsValidID(new_g)) {
00411     Group *g = Group::Get(new_g);
00412     if (g->owner != _current_company || g->vehicle_type != v->type) return CMD_ERROR;
00413   }
00414 
00415   if (v->owner != _current_company || !v->IsPrimaryVehicle()) return CMD_ERROR;
00416 
00417   if (flags & DC_EXEC) {
00418     GroupStatistics::CountVehicle(v, -1);
00419 
00420     switch (v->type) {
00421       default: NOT_REACHED();
00422       case VEH_TRAIN:
00423         SetTrainGroupID(Train::From(v), new_g);
00424         break;
00425       case VEH_ROAD:
00426       case VEH_SHIP:
00427       case VEH_AIRCRAFT:
00428         if (v->IsEngineCountable()) UpdateNumEngineGroup(v, v->group_id, new_g);
00429         v->group_id = new_g;
00430         break;
00431     }
00432 
00433     GroupStatistics::CountVehicle(v, 1);
00434     GroupStatistics::UpdateAutoreplace(v->owner);
00435 
00436     /* Update the Replace Vehicle Windows */
00437     SetWindowDirty(WC_REPLACE_VEHICLE, v->type);
00438     InvalidateWindowData(GetWindowClassForVehicleType(v->type), VehicleListIdentifier(VL_GROUP_LIST, v->type, _current_company).Pack());
00439   }
00440 
00441   return CommandCost();
00442 }
00443 
00454 CommandCost CmdAddSharedVehicleGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00455 {
00456   VehicleType type = Extract<VehicleType, 0, 3>(p2);
00457   GroupID id_g = p1;
00458   if (!Group::IsValidID(id_g) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;
00459 
00460   if (flags & DC_EXEC) {
00461     Vehicle *v;
00462 
00463     /* Find the first front engine which belong to the group id_g
00464      * then add all shared vehicles of this front engine to the group id_g */
00465     FOR_ALL_VEHICLES(v) {
00466       if (v->type == type && v->IsPrimaryVehicle()) {
00467         if (v->group_id != id_g) continue;
00468 
00469         /* For each shared vehicles add it to the group */
00470         for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00471           if (v2->group_id != id_g) DoCommand(tile, id_g, v2->index, flags, CMD_ADD_VEHICLE_GROUP, text);
00472         }
00473       }
00474     }
00475 
00476     InvalidateWindowData(GetWindowClassForVehicleType(type), VehicleListIdentifier(VL_GROUP_LIST, type, _current_company).Pack());
00477   }
00478 
00479   return CommandCost();
00480 }
00481 
00482 
00493 CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00494 {
00495   GroupID old_g = p1;
00496   Group *g = Group::GetIfValid(old_g);
00497 
00498   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
00499 
00500   if (flags & DC_EXEC) {
00501     Vehicle *v;
00502 
00503     /* Find each Vehicle that belongs to the group old_g and add it to the default group */
00504     FOR_ALL_VEHICLES(v) {
00505       if (v->IsPrimaryVehicle()) {
00506         if (v->group_id != old_g) continue;
00507 
00508         /* Add The Vehicle to the default group */
00509         DoCommand(tile, DEFAULT_GROUP, v->index, flags, CMD_ADD_VEHICLE_GROUP, text);
00510       }
00511     }
00512 
00513     InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
00514   }
00515 
00516   return CommandCost();
00517 }
00518 
00519 
00531 CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00532 {
00533   Group *g = Group::GetIfValid(p1);
00534   if (g == NULL || g->owner != _current_company) return CMD_ERROR;
00535 
00536   if (flags & DC_EXEC) {
00537     g->replace_protection = HasBit(p2, 0);
00538 
00539     InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack());
00540     InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type);
00541   }
00542 
00543   return CommandCost();
00544 }
00545 
00551 void RemoveVehicleFromGroup(const Vehicle *v)
00552 {
00553   if (!v->IsPrimaryVehicle()) return;
00554 
00555   if (!IsDefaultGroupID(v->group_id)) GroupStatistics::CountVehicle(v, -1);
00556 }
00557 
00558 
00565 void SetTrainGroupID(Train *v, GroupID new_g)
00566 {
00567   if (!Group::IsValidID(new_g) && !IsDefaultGroupID(new_g)) return;
00568 
00569   assert(v->IsFrontEngine() || IsDefaultGroupID(new_g));
00570 
00571   for (Vehicle *u = v; u != NULL; u = u->Next()) {
00572     if (u->IsEngineCountable()) UpdateNumEngineGroup(u, u->group_id, new_g);
00573 
00574     u->group_id = new_g;
00575   }
00576 
00577   /* Update the Replace Vehicle Windows */
00578   GroupStatistics::UpdateAutoreplace(v->owner);
00579   SetWindowDirty(WC_REPLACE_VEHICLE, VEH_TRAIN);
00580 }
00581 
00582 
00590 void UpdateTrainGroupID(Train *v)
00591 {
00592   assert(v->IsFrontEngine() || v->IsFreeWagon());
00593 
00594   GroupID new_g = v->IsFrontEngine() ? v->group_id : (GroupID)DEFAULT_GROUP;
00595   for (Vehicle *u = v; u != NULL; u = u->Next()) {
00596     if (u->IsEngineCountable()) UpdateNumEngineGroup(u, u->group_id, new_g);
00597 
00598     u->group_id = new_g;
00599   }
00600 
00601   /* Update the Replace Vehicle Windows */
00602   GroupStatistics::UpdateAutoreplace(v->owner);
00603   SetWindowDirty(WC_REPLACE_VEHICLE, VEH_TRAIN);
00604 }
00605 
00614 uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
00615 {
00616   const Engine *e = Engine::Get(id_e);
00617   return GroupStatistics::Get(company, id_g, e->type).num_engines[id_e];
00618 }
00619 
00620 void RemoveAllGroupsForCompany(const CompanyID company)
00621 {
00622   Group *g;
00623 
00624   FOR_ALL_GROUPS(g) {
00625     if (company == g->owner) delete g;
00626   }
00627 }