00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "variables.h"
00008 #include "command_func.h"
00009 #include "saveload.h"
00010 #include "debug.h"
00011 #include "group.h"
00012 #include "train.h"
00013 #include "aircraft.h"
00014 #include "vehicle_gui.h"
00015 #include "strings_func.h"
00016 #include "functions.h"
00017 #include "window_func.h"
00018 #include "vehicle_func.h"
00019 #include "autoreplace_base.h"
00020 #include "autoreplace_func.h"
00021 #include "string_func.h"
00022 #include "company_func.h"
00023 #include "order_func.h"
00024 #include "oldpool_func.h"
00025 #include "core/alloc_func.hpp"
00026
00027 #include "table/strings.h"
00028
00036 static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
00037 {
00038 if (old_g != new_g) {
00039
00040 if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
00041
00042
00043 if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
00044 }
00045 }
00046
00047
00048 DEFINE_OLD_POOL_GENERIC(Group, Group)
00049
00050
00051 Group::Group(Owner owner)
00052 {
00053 this->owner = owner;
00054
00055 if (this->IsValid()) this->num_engines = CallocT<uint16>(GetEnginePoolSize());
00056 }
00057
00058 Group::~Group()
00059 {
00060 free(this->name);
00061 this->owner = INVALID_OWNER;
00062 free(this->num_engines);
00063 }
00064
00065 bool Group::IsValid() const
00066 {
00067 return this->owner != INVALID_OWNER;
00068 }
00069
00070 void InitializeGroup(void)
00071 {
00072 _Group_pool.CleanPool();
00073 _Group_pool.AddBlockToPool();
00074 }
00075
00076
00083 CommandCost CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00084 {
00085 VehicleType vt = (VehicleType)p1;
00086 if (!IsCompanyBuildableVehicleType(vt)) return CMD_ERROR;
00087
00088 if (!Group::CanAllocateItem()) return CMD_ERROR;
00089
00090 if (flags & DC_EXEC) {
00091 Group *g = new Group(_current_company);
00092 g->replace_protection = false;
00093 g->vehicle_type = vt;
00094
00095 InvalidateWindowData(GetWindowClassForVehicleType(vt), (vt << 11) | VLW_GROUP_LIST | _current_company);
00096 }
00097
00098 return CommandCost();
00099 }
00100
00101
00109 CommandCost CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00110 {
00111 if (!IsValidGroupID(p1)) return CMD_ERROR;
00112
00113 Group *g = GetGroup(p1);
00114 if (g->owner != _current_company) return CMD_ERROR;
00115
00116 if (flags & DC_EXEC) {
00117 Vehicle *v;
00118
00119
00120 FOR_ALL_VEHICLES(v) {
00121 if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
00122 }
00123
00124
00125 if (_backup_orders_data.group == g->index) _backup_orders_data.group = DEFAULT_GROUP;
00126
00127
00128 if (_current_company < MAX_COMPANIES) {
00129 Company *c;
00130 EngineRenew *er;
00131
00132 c = GetCompany(_current_company);
00133 FOR_ALL_ENGINE_RENEWS(er) {
00134 if (er->group_id == g->index) RemoveEngineReplacementForCompany(c, er->from, g->index, flags);
00135 }
00136 }
00137
00138 VehicleType vt = g->vehicle_type;
00139
00140
00141 DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
00142 delete g;
00143
00144 InvalidateWindowData(GetWindowClassForVehicleType(vt), (vt << 11) | VLW_GROUP_LIST | _current_company);
00145 }
00146
00147 return CommandCost();
00148 }
00149
00150 static bool IsUniqueGroupName(const char *name)
00151 {
00152 const Group *g;
00153 char buf[512];
00154
00155 FOR_ALL_GROUPS(g) {
00156 SetDParam(0, g->index);
00157 GetString(buf, STR_GROUP_NAME, lastof(buf));
00158 if (strcmp(buf, name) == 0) return false;
00159 }
00160
00161 return true;
00162 }
00163
00171 CommandCost CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00172 {
00173 if (!IsValidGroupID(p1)) return CMD_ERROR;
00174
00175 Group *g = GetGroup(p1);
00176 if (g->owner != _current_company) return CMD_ERROR;
00177
00178 bool reset = StrEmpty(_cmd_text);
00179
00180 if (!reset) {
00181 if (strlen(_cmd_text) >= MAX_LENGTH_GROUP_NAME_BYTES) return CMD_ERROR;
00182 if (!IsUniqueGroupName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
00183 }
00184
00185 if (flags & DC_EXEC) {
00186
00187 free(g->name);
00188
00189 g->name = reset ? NULL : strdup(_cmd_text);
00190
00191 InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_company);
00192 }
00193
00194 return CommandCost();
00195 }
00196
00197
00206 CommandCost CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00207 {
00208 GroupID new_g = p1;
00209
00210 if (!IsValidVehicleID(p2) || (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g))) return CMD_ERROR;
00211
00212 Vehicle *v = GetVehicle(p2);
00213
00214 if (IsValidGroupID(new_g)) {
00215 Group *g = GetGroup(new_g);
00216 if (g->owner != _current_company || g->vehicle_type != v->type) return CMD_ERROR;
00217 }
00218
00219 if (v->owner != _current_company || !v->IsPrimaryVehicle()) return CMD_ERROR;
00220
00221 if (flags & DC_EXEC) {
00222 DecreaseGroupNumVehicle(v->group_id);
00223 IncreaseGroupNumVehicle(new_g);
00224
00225 switch (v->type) {
00226 default: NOT_REACHED();
00227 case VEH_TRAIN:
00228 SetTrainGroupID(v, new_g);
00229 break;
00230 case VEH_ROAD:
00231 case VEH_SHIP:
00232 case VEH_AIRCRAFT:
00233 if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
00234 v->group_id = new_g;
00235 break;
00236 }
00237
00238
00239 InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
00240 InvalidateWindowData(GetWindowClassForVehicleType(v->type), (v->type << 11) | VLW_GROUP_LIST | _current_company);
00241 }
00242
00243 return CommandCost();
00244 }
00245
00253 CommandCost CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00254 {
00255 VehicleType type = (VehicleType)p2;
00256 if (!IsValidGroupID(p1) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;
00257
00258 if (flags & DC_EXEC) {
00259 Vehicle *v;
00260 VehicleType type = (VehicleType)p2;
00261 GroupID id_g = p1;
00262
00263
00264
00265 FOR_ALL_VEHICLES(v) {
00266 if (v->type == type && v->IsPrimaryVehicle()) {
00267 if (v->group_id != id_g) continue;
00268
00269
00270 for (Vehicle *v2 = v->FirstShared(); v2 != NULL; v2 = v2->NextShared()) {
00271 if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
00272 }
00273 }
00274 }
00275
00276 InvalidateWindowData(GetWindowClassForVehicleType(type), (type << 11) | VLW_GROUP_LIST | _current_company);
00277 }
00278
00279 return CommandCost();
00280 }
00281
00282
00290 CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00291 {
00292 VehicleType type = (VehicleType)p2;
00293 if (!IsValidGroupID(p1) || !IsCompanyBuildableVehicleType(type)) return CMD_ERROR;
00294
00295 Group *g = GetGroup(p1);
00296 if (g->owner != _current_company) return CMD_ERROR;
00297
00298 if (flags & DC_EXEC) {
00299 GroupID old_g = p1;
00300 Vehicle *v;
00301
00302
00303 FOR_ALL_VEHICLES(v) {
00304 if (v->type == type && v->IsPrimaryVehicle()) {
00305 if (v->group_id != old_g) continue;
00306
00307
00308 CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
00309 }
00310 }
00311
00312 InvalidateWindowData(GetWindowClassForVehicleType(type), (type << 11) | VLW_GROUP_LIST | _current_company);
00313 }
00314
00315 return CommandCost();
00316 }
00317
00318
00327 CommandCost CmdSetGroupReplaceProtection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00328 {
00329 if (!IsValidGroupID(p1)) return CMD_ERROR;
00330
00331 Group *g = GetGroup(p1);
00332 if (g->owner != _current_company) return CMD_ERROR;
00333
00334 if (flags & DC_EXEC) {
00335 g->replace_protection = HasBit(p2, 0);
00336
00337 InvalidateWindowData(GetWindowClassForVehicleType(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_company);
00338 }
00339
00340 return CommandCost();
00341 }
00342
00348 void RemoveVehicleFromGroup(const Vehicle *v)
00349 {
00350 if (!v->IsValid() || !v->IsPrimaryVehicle()) return;
00351
00352 if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
00353 }
00354
00355
00362 void SetTrainGroupID(Vehicle *v, GroupID new_g)
00363 {
00364 if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
00365
00366 assert(v->IsValid() && v->type == VEH_TRAIN && IsFrontEngine(v));
00367
00368 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00369 if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
00370
00371 u->group_id = new_g;
00372 }
00373
00374
00375 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
00376 }
00377
00378
00386 void UpdateTrainGroupID(Vehicle *v)
00387 {
00388 assert(v->IsValid() && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
00389
00390 GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
00391 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00392 if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
00393
00394 u->group_id = new_g;
00395 }
00396
00397
00398 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
00399 }
00400
00401 uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
00402 {
00403 if (IsValidGroupID(id_g)) return GetGroup(id_g)->num_engines[id_e];
00404
00405 uint num = GetCompany(company)->num_engines[id_e];
00406 if (!IsDefaultGroupID(id_g)) return num;
00407
00408 const Group *g;
00409 FOR_ALL_GROUPS(g) {
00410 if (g->owner == company) num -= g->num_engines[id_e];
00411 }
00412 return num;
00413 }
00414
00415 void RemoveAllGroupsForCompany(const CompanyID company)
00416 {
00417 Group *g;
00418
00419 FOR_ALL_GROUPS(g) {
00420 if (company == g->owner) delete g;
00421 }
00422 }
00423
00424
00425 static const SaveLoad _group_desc[] = {
00426 SLE_CONDVAR(Group, name, SLE_NAME, 0, 83),
00427 SLE_CONDSTR(Group, name, SLE_STR, 0, 84, SL_MAX_VERSION),
00428 SLE_VAR(Group, num_vehicle, SLE_UINT16),
00429 SLE_VAR(Group, owner, SLE_UINT8),
00430 SLE_VAR(Group, vehicle_type, SLE_UINT8),
00431 SLE_VAR(Group, replace_protection, SLE_BOOL),
00432 SLE_END()
00433 };
00434
00435
00436 static void Save_GROUP(void)
00437 {
00438 Group *g;
00439
00440 FOR_ALL_GROUPS(g) {
00441 SlSetArrayIndex(g->index);
00442 SlObject(g, _group_desc);
00443 }
00444 }
00445
00446
00447 static void Load_GROUP(void)
00448 {
00449 int index;
00450
00451 while ((index = SlIterateArray()) != -1) {
00452 Group *g = new (index) Group();
00453 SlObject(g, _group_desc);
00454 }
00455 }
00456
00457 extern const ChunkHandler _group_chunk_handlers[] = {
00458 { 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
00459 };