/* * 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(); } } } } } }
/** * Autoreplaces a vehicle * Trains are replaced as a whole chain, free wagons in depot are replaced on their own * @param tile not used * @param flags type of operation * @param p1 Index of vehicle * @param p2 not used * @param text unused * @return the cost of this operation or an error */ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Vehicle *v = Vehicle::GetIfValid(p1); if (v == NULL) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; if (!v->IsInDepot()) return CMD_ERROR; if (v->vehstatus & VS_CRASHED) return CMD_ERROR; bool free_wagon = false; if (v->type == VEH_TRAIN) { Train *t = Train::From(v); if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR; free_wagon = !t->IsFrontEngine(); if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR; } else { if (!v->IsPrimaryVehicle()) return CMD_ERROR; } const Company *c = Company::Get(_current_company); bool wagon_removal = c->settings.renew_keep_length; /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */ Vehicle *w = v; bool any_replacements = false; while (w != NULL) { EngineID e; CommandCost cost = GetNewEngineType(w, c, e); if (cost.Failed()) return cost; any_replacements |= (e != INVALID_ENGINE); w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : NULL); } CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0); bool nothing_to_do = true; if (any_replacements) { bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0); /* Stop the vehicle */ if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, true)); if (cost.Failed()) return cost; assert(v->IsStoppedInDepot()); /* We have to construct the new vehicle chain to test whether it is valid. * Vehicle construction needs random bits, so we have to save the random seeds * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ SavedRandomSeeds saved_seeds; SaveRandomSeeds(&saved_seeds); if (free_wagon) { cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do)); } else { cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do)); } RestoreRandomSeeds(saved_seeds); if (cost.Succeeded() && (flags & DC_EXEC) != 0) { CommandCost ret; if (free_wagon) { ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do); } else { ret = ReplaceChain(&v, flags, wagon_removal, ¬hing_to_do); } assert(ret.Succeeded() && ret.GetCost() == cost.GetCost()); } /* Restart the vehicle */ if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, false)); } if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO); return cost; }