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 "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
00055 free(this->num_engines);
00056 this->num_engines = CallocT<uint16>(Engine::GetPoolSize());
00057 }
00058
00066 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 GroupStatistics &GroupStatistics::Get(const Vehicle *v)
00087 {
00088 return GroupStatistics::Get(v->owner, v->group_id, v->type);
00089 }
00090
00096 GroupStatistics &GroupStatistics::GetAllGroup(const Vehicle *v)
00097 {
00098 return GroupStatistics::Get(v->owner, ALL_GROUP, v->type);
00099 }
00100
00104 void GroupStatistics::UpdateAfterLoad()
00105 {
00106
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
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 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 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 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 void GroupStatistics::UpdateProfits()
00187 {
00188
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
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 void GroupStatistics::UpdateAutoreplace(CompanyID company)
00214 {
00215
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
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
00251 GroupStatistics::Get(v->owner, old_g, v->type).num_engines[v->engine_type]--;
00252
00253
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
00317 DoCommand(0, p1, 0, flags, CMD_REMOVE_ALL_VEHICLES_GROUP);
00318
00319 if (flags & DC_EXEC) {
00320
00321 OrderBackup::ClearGroup(g->index);
00322
00323
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
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
00381 free(g->name);
00382
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
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
00464
00465 FOR_ALL_VEHICLES(v) {
00466 if (v->type == type && v->IsPrimaryVehicle()) {
00467 if (v->group_id != id_g) continue;
00468
00469
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
00504 FOR_ALL_VEHICLES(v) {
00505 if (v->IsPrimaryVehicle()) {
00506 if (v->group_id != old_g) continue;
00507
00508
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
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
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 }