/** * Converts all trains to the new subtype format introduced in savegame 16.2 * It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found */ void ConvertOldMultiheadToNew() { Train *t; FOR_ALL_TRAINS(t) SetBit(t->subtype, 7); // indicates that it's the old format and needs to be converted in the next loop FOR_ALL_TRAINS(t) { if (HasBit(t->subtype, 7) && ((t->subtype & ~0x80) == 0 || (t->subtype & ~0x80) == 4)) { for (Train *u = t; u != NULL; u = u->Next()) { const RailVehicleInfo *rvi = RailVehInfo(u->engine_type); ClrBit(u->subtype, 7); switch (u->subtype) { case 0: // TS_Front_Engine if (rvi->railveh_type == RAILVEH_MULTIHEAD) u->SetMultiheaded(); u->SetFrontEngine(); u->SetEngine(); break; case 1: // TS_Artic_Part u->subtype = 0; u->SetArticulatedPart(); break; case 2: // TS_Not_First u->subtype = 0; if (rvi->railveh_type == RAILVEH_WAGON) { /* normal wagon */ u->SetWagon(); break; } if (rvi->railveh_type == RAILVEH_MULTIHEAD && rvi->image_index == u->spritenum - 1) { /* rear end of a multiheaded engine */ u->SetMultiheaded(); break; } if (rvi->railveh_type == RAILVEH_MULTIHEAD) u->SetMultiheaded(); u->SetEngine(); break; case 4: // TS_Free_Car u->subtype = 0; u->SetWagon(); u->SetFreeWagon(); break; default: NOT_REACHED(); } } } } }
/* * Link front and rear multiheaded engines to each other * This is done when loading a savegame */ void ConnectMultiheadedTrains() { Train *v; FOR_ALL_TRAINS(v) { v->other_multiheaded_part = NULL; } FOR_ALL_TRAINS(v) { if (v->IsFrontEngine() || v->IsFreeWagon()) { /* Two ways to associate multiheaded parts to each other: * sequential-matching: Trains shall be arranged to look like <..>..<..>..<..>.. * bracket-matching: Free vehicle chains shall be arranged to look like ..<..<..>..<..>..>.. * * Note: Old savegames might contain chains which do not comply with these rules, e.g. * - the front and read parts have invalid orders * - different engine types might be combined * - there might be different amounts of front and rear parts. * * Note: The multiheaded parts need to be matched exactly like they are matched on the server, else desyncs will occur. * This is why two matching strategies are needed. */ bool sequential_matching = v->IsFrontEngine(); for (Train *u = v; u != NULL; u = u->GetNextVehicle()) { if (u->other_multiheaded_part != NULL) continue; // we already linked this one if (u->IsMultiheaded()) { if (!u->IsEngine()) { /* we got a rear car without a front car. We will convert it to a front one */ u->SetEngine(); u->spritenum--; } /* Find a matching back part */ EngineID eid = u->engine_type; Train *w; if (sequential_matching) { for (w = u->GetNextVehicle(); w != NULL; w = w->GetNextVehicle()) { if (w->engine_type != eid || w->other_multiheaded_part != NULL || !w->IsMultiheaded()) continue; /* we found a car to partner with this engine. Now we will make sure it face the right way */ if (w->IsEngine()) { w->ClearEngine(); w->spritenum++; } break; } } else { uint stack_pos = 0; for (w = u->GetNextVehicle(); w != NULL; w = w->GetNextVehicle()) { if (w->engine_type != eid || w->other_multiheaded_part != NULL || !w->IsMultiheaded()) continue; if (w->IsEngine()) { stack_pos++; } else { if (stack_pos == 0) break; stack_pos--; } } } if (w != NULL) { w->other_multiheaded_part = u; u->other_multiheaded_part = w; } else { /* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */ u->ClearMultiheaded(); } } } } } }