00001
00002
00003
00004
00005
00006
00007
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
00052 free(this->num_engines);
00053 this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
00054 }
00055
00063 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 GroupStatistics &GroupStatistics::Get(const Vehicle *v)
00084 {
00085 return GroupStatistics::Get(v->owner, v->group_id, v->type);
00086 }
00087
00093 GroupStatistics &GroupStatistics::GetAllGroup(const Vehicle *v)
00094 {
00095 return GroupStatistics::Get(v->owner, ALL_GROUP, v->type);
00096 }
00097
00101 void GroupStatistics::UpdateAfterLoad()
00102 {
00103
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
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 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 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 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 void GroupStatistics::UpdateProfits()
00184 {
00185
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
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 void GroupStatistics::UpdateAutoreplace(CompanyID company)
00211 {
00212
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
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
00248 GroupStatistics::Get(v->owner, old_g, v->type).num_engines[v->engine_type]--;
00249
00250
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
00314 DoCommand(0, p1, 0, flags, CMD_REMOVE_ALL_VEHICLES_GROUP);
00315
00316 if (flags & DC_EXEC) {
00317
00318 OrderBackup::ClearGroup(g->index);
00319
00320
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
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
00378 free(g->name);
00379
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
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
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
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
00490
00491 FOR_ALL_VEHICLES(v) {
00492 if (v->type == type && v->IsPrimaryVehicle()) {
00493 if (v->group_id != id_g) continue;
00494
00495
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
00530 FOR_ALL_VEHICLES(v) {
00531 if (v->IsPrimaryVehicle()) {
00532 if (v->group_id != old_g) continue;
00533
00534
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
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
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 }