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 "player_func.h"
00023
00024 #include "table/strings.h"
00025
00033 static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
00034 {
00035 if (old_g != new_g) {
00036
00037 if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
00038
00039
00040 if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
00041 }
00042 }
00043
00044
00045 DEFINE_OLD_POOL_GENERIC(Group, Group)
00046
00047
00048 Group::Group(PlayerID owner)
00049 {
00050 this->owner = owner;
00051 }
00052
00053 Group::~Group()
00054 {
00055 free(this->name);
00056 this->owner = INVALID_PLAYER;
00057 }
00058
00059 bool Group::IsValid() const
00060 {
00061 return this->owner != INVALID_PLAYER;
00062 }
00063
00064 void InitializeGroup(void)
00065 {
00066 _Group_pool.CleanPool();
00067 _Group_pool.AddBlockToPool();
00068 }
00069
00070
00071 static WindowClass GetWCForVT(VehicleType vt)
00072 {
00073 switch (vt) {
00074 default:
00075 case VEH_TRAIN: return WC_TRAINS_LIST;
00076 case VEH_ROAD: return WC_ROADVEH_LIST;
00077 case VEH_SHIP: return WC_SHIPS_LIST;
00078 case VEH_AIRCRAFT: return WC_AIRCRAFT_LIST;
00079 }
00080 }
00081
00082
00089 CommandCost CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00090 {
00091 VehicleType vt = (VehicleType)p1;
00092 if (!IsPlayerBuildableVehicleType(vt)) return CMD_ERROR;
00093
00094 if (!Group::CanAllocateItem()) return CMD_ERROR;
00095
00096 if (flags & DC_EXEC) {
00097 Group *g = new Group(_current_player);
00098 g->replace_protection = false;
00099 g->vehicle_type = vt;
00100
00101 InvalidateWindowData(GetWCForVT(vt), (vt << 11) | VLW_GROUP_LIST | _current_player);
00102 }
00103
00104 return CommandCost();
00105 }
00106
00107
00115 CommandCost CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00116 {
00117 if (!IsValidGroupID(p1)) return CMD_ERROR;
00118
00119 Group *g = GetGroup(p1);
00120 if (g->owner != _current_player) return CMD_ERROR;
00121
00122 if (flags & DC_EXEC) {
00123 Vehicle *v;
00124
00125
00126 FOR_ALL_VEHICLES(v) {
00127 if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
00128 }
00129
00130
00131 if (_backup_orders_data.group == g->index) _backup_orders_data.group = DEFAULT_GROUP;
00132
00133
00134 if (_current_player < MAX_PLAYERS) {
00135 Player *p;
00136 EngineRenew *er;
00137
00138 p = GetPlayer(_current_player);
00139 FOR_ALL_ENGINE_RENEWS(er) {
00140 if (er->group_id == g->index) RemoveEngineReplacementForPlayer(p, er->from, g->index, flags);
00141 }
00142 }
00143
00144 VehicleType vt = g->vehicle_type;
00145
00146
00147 DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
00148 delete g;
00149
00150 InvalidateWindowData(GetWCForVT(vt), (vt << 11) | VLW_GROUP_LIST | _current_player);
00151 }
00152
00153 return CommandCost();
00154 }
00155
00156 static bool IsUniqueGroupName(const char *name)
00157 {
00158 const Group *g;
00159 char buf[512];
00160
00161 FOR_ALL_GROUPS(g) {
00162 SetDParam(0, g->index);
00163 GetString(buf, STR_GROUP_NAME, lastof(buf));
00164 if (strcmp(buf, name) == 0) return false;
00165 }
00166
00167 return true;
00168 }
00169
00177 CommandCost CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00178 {
00179 if (!IsValidGroupID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
00180
00181 Group *g = GetGroup(p1);
00182 if (g->owner != _current_player) return CMD_ERROR;
00183
00184 if (!IsUniqueGroupName(_cmd_text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
00185
00186 if (flags & DC_EXEC) {
00187
00188 free(g->name);
00189
00190 g->name = strdup(_cmd_text);
00191
00192 InvalidateWindowData(GetWCForVT(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_player);
00193 }
00194
00195 return CommandCost();
00196 }
00197
00198
00207 CommandCost CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00208 {
00209 GroupID new_g = p1;
00210
00211 if (!IsValidVehicleID(p2) || (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g))) return CMD_ERROR;
00212
00213 Vehicle *v = GetVehicle(p2);
00214
00215 if (IsValidGroupID(new_g)) {
00216 Group *g = GetGroup(new_g);
00217 if (g->owner != _current_player || g->vehicle_type != v->type) return CMD_ERROR;
00218 }
00219
00220 if (v->owner != _current_player || !v->IsPrimaryVehicle()) return CMD_ERROR;
00221
00222 if (flags & DC_EXEC) {
00223 DecreaseGroupNumVehicle(v->group_id);
00224 IncreaseGroupNumVehicle(new_g);
00225
00226 switch (v->type) {
00227 default: NOT_REACHED();
00228 case VEH_TRAIN:
00229 SetTrainGroupID(v, new_g);
00230 break;
00231 case VEH_ROAD:
00232 case VEH_SHIP:
00233 case VEH_AIRCRAFT:
00234 if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
00235 v->group_id = new_g;
00236 break;
00237 }
00238
00239
00240 InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
00241 InvalidateWindowData(GetWCForVT(v->type), (v->type << 11) | VLW_GROUP_LIST | _current_player);
00242 }
00243
00244 return CommandCost();
00245 }
00246
00254 CommandCost CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00255 {
00256 VehicleType type = (VehicleType)p2;
00257 if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
00258
00259 if (flags & DC_EXEC) {
00260 Vehicle *v;
00261 VehicleType type = (VehicleType)p2;
00262 GroupID id_g = p1;
00263
00264
00265
00266 FOR_ALL_VEHICLES(v) {
00267 if (v->type == type && v->IsPrimaryVehicle()) {
00268 if (v->group_id != id_g) continue;
00269
00270
00271 for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) {
00272 if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
00273 }
00274 }
00275 }
00276
00277 InvalidateWindowData(GetWCForVT(type), (type << 11) | VLW_GROUP_LIST | _current_player);
00278 }
00279
00280 return CommandCost();
00281 }
00282
00283
00291 CommandCost CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00292 {
00293 VehicleType type = (VehicleType)p2;
00294 if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
00295
00296 Group *g = GetGroup(p1);
00297 if (g->owner != _current_player) return CMD_ERROR;
00298
00299 if (flags & DC_EXEC) {
00300 GroupID old_g = p1;
00301 Vehicle *v;
00302
00303
00304 FOR_ALL_VEHICLES(v) {
00305 if (v->type == type && v->IsPrimaryVehicle()) {
00306 if (v->group_id != old_g) continue;
00307
00308
00309 CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
00310 }
00311 }
00312
00313 InvalidateWindowData(GetWCForVT(type), (type << 11) | VLW_GROUP_LIST | _current_player);
00314 }
00315
00316 return CommandCost();
00317 }
00318
00319
00328 CommandCost CmdSetGroupReplaceProtection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00329 {
00330 if (!IsValidGroupID(p1)) return CMD_ERROR;
00331
00332 Group *g = GetGroup(p1);
00333 if (g->owner != _current_player) return CMD_ERROR;
00334
00335 if (flags & DC_EXEC) {
00336 g->replace_protection = HasBit(p2, 0);
00337
00338 InvalidateWindowData(GetWCForVT(g->vehicle_type), (g->vehicle_type << 11) | VLW_GROUP_LIST | _current_player);
00339 }
00340
00341 return CommandCost();
00342 }
00343
00349 void RemoveVehicleFromGroup(const Vehicle *v)
00350 {
00351 if (!v->IsValid() || !v->IsPrimaryVehicle()) return;
00352
00353 if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
00354 }
00355
00356
00363 void SetTrainGroupID(Vehicle *v, GroupID new_g)
00364 {
00365 if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
00366
00367 assert(v->IsValid() && v->type == VEH_TRAIN && IsFrontEngine(v));
00368
00369 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00370 if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
00371
00372 u->group_id = new_g;
00373 }
00374
00375
00376 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
00377 }
00378
00379
00387 void UpdateTrainGroupID(Vehicle *v)
00388 {
00389 assert(v->IsValid() && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
00390
00391 GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
00392 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00393 if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
00394
00395 u->group_id = new_g;
00396 }
00397
00398
00399 InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
00400 }
00401
00402 uint GetGroupNumEngines(PlayerID p, GroupID id_g, EngineID id_e)
00403 {
00404 if (IsValidGroupID(id_g)) return GetGroup(id_g)->num_engines[id_e];
00405
00406 uint num = GetPlayer(p)->num_engines[id_e];
00407 if (!IsDefaultGroupID(id_g)) return num;
00408
00409 const Group *g;
00410 FOR_ALL_GROUPS(g) {
00411 if (g->owner == p) num -= g->num_engines[id_e];
00412 }
00413 return num;
00414 }
00415
00416 void RemoveAllGroupsForPlayer(const PlayerID p)
00417 {
00418 Group *g;
00419
00420 FOR_ALL_GROUPS(g) {
00421 if (p == g->owner) delete g;
00422 }
00423 }
00424
00425
00426 static const SaveLoad _group_desc[] = {
00427 SLE_CONDVAR(Group, name, SLE_NAME, 0, 83),
00428 SLE_CONDSTR(Group, name, SLE_STR, 0, 84, SL_MAX_VERSION),
00429 SLE_VAR(Group, num_vehicle, SLE_UINT16),
00430 SLE_VAR(Group, owner, SLE_UINT8),
00431 SLE_VAR(Group, vehicle_type, SLE_UINT8),
00432 SLE_VAR(Group, replace_protection, SLE_BOOL),
00433 SLE_END()
00434 };
00435
00436
00437 static void Save_GROUP(void)
00438 {
00439 Group *g;
00440
00441 FOR_ALL_GROUPS(g) {
00442 SlSetArrayIndex(g->index);
00443 SlObject(g, _group_desc);
00444 }
00445 }
00446
00447
00448 static void Load_GROUP(void)
00449 {
00450 int index;
00451
00452 while ((index = SlIterateArray()) != -1) {
00453 Group *g = new (index) Group();
00454 SlObject(g, _group_desc);
00455 }
00456 }
00457
00458 extern const ChunkHandler _group_chunk_handlers[] = {
00459 { 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
00460 };