diff --git a/src/aircraft.h b/src/aircraft.h --- a/src/aircraft.h +++ b/src/aircraft.h @@ -102,6 +102,7 @@ struct Aircraft : public SpecializedVehi bool IsInDepot() const { return (this->vehstatus & VS_HIDDEN) != 0 && IsHangarTile(this->tile); } bool Tick(); void OnNewDay(); + uint Crash(bool flooded = false); TileIndex GetOrderStationLocation(StationID station); bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse); diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -1238,19 +1238,21 @@ void Aircraft::MarkDirty() if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this); } + +uint Aircraft::Crash(bool flooded) +{ + uint pass = Vehicle::Crash(flooded) + 2; // pilots + this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded + + return pass; +} + static void CrashAirplane(Aircraft *v) { - v->vehstatus |= VS_CRASHED; - v->crashed_counter = 0; - CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); - v->MarkDirty(); - SetWindowDirty(WC_VEHICLE_VIEW, v->index); - - uint amt = 2; - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count(); - SetDParam(0, amt); + uint pass = v->Crash(); + SetDParam(0, pass); v->cargo.Truncate(0); v->Next()->cargo.Truncate(0); diff --git a/src/disaster_cmd.cpp b/src/disaster_cmd.cpp --- a/src/disaster_cmd.cpp +++ b/src/disaster_cmd.cpp @@ -340,18 +340,13 @@ static bool DisasterTick_Ufo(DisasterVeh if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) { v->age++; if (u->crashed_ctr == 0) { - u->crashed_ctr++; + u->Crash(); AddVehicleNewsItem(STR_NEWS_DISASTER_SMALL_UFO, NS_ACCIDENT, u->index); // delete the news, when the roadvehicle is gone AI::NewEvent(u->owner, new AIEventVehicleCrashed(u->index, u->tile, AIEventVehicleCrashed::CRASH_RV_UFO)); - - for (Vehicle *w = u; w != NULL; w = w->Next()) { - w->vehstatus |= VS_CRASHED; - MarkSingleVehicleDirty(w); - } } } diff --git a/src/roadveh.h b/src/roadveh.h --- a/src/roadveh.h +++ b/src/roadveh.h @@ -125,6 +125,7 @@ struct RoadVehicle : public SpecializedV bool IsStoppedInDepot() const; bool Tick(); void OnNewDay(); + uint Crash(bool flooded = false); Trackdir GetVehicleTrackdir() const; TileIndex GetOrderStationLocation(StationID station); bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -537,21 +537,17 @@ static Vehicle *EnumCheckRoadVehCrashTra v : NULL; } +uint RoadVehicle::Crash(bool flooded) +{ + uint pass = Vehicle::Crash(flooded); + if (this->IsRoadVehFront()) pass += 1; // driver + this->crashed_ctr = flooded ? 2000 : 1; // max 2220, disappear pretty fast when flooded + return pass; +} + static void RoadVehCrash(RoadVehicle *v) { - uint16 pass = 1; - - v->crashed_ctr++; - - for (Vehicle *u = v; u != NULL; u = u->Next()) { - if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count(); - - u->vehstatus |= VS_CRASHED; - - MarkSingleVehicleDirty(u); - } - - SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); + uint pass = v->Crash(); AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING)); diff --git a/src/train.h b/src/train.h --- a/src/train.h +++ b/src/train.h @@ -136,6 +136,7 @@ struct Train : public SpecializedVehicle bool IsStoppedInDepot() const { return CheckTrainStoppedInDepot(this) >= 0; } bool Tick(); void OnNewDay(); + uint Crash(bool flooded = false); Trackdir GetVehicleTrackdir() const; TileIndex GetOrderStationLocation(StationID station); bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3188,56 +3188,34 @@ void Train::ReserveTrackUnderConsist() c } } -static void SetVehicleCrashed(Train *v) +uint Train::Crash(bool flooded) { - if (v->crash_anim_pos != 0) return; - - if (v->IsFrontEngine()) { + uint pass = 0; + if (this->IsFrontEngine()) { + pass += 4; // driver + /* Remove the reserved path in front of the train if it is not stuck. * Also clear all reserved tracks the train is currently on. */ - if (!HasBit(v->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(v); - for (const Train *u = v; u != NULL; u = u->Next()) { - ClearPathReservation(u, u->tile, u->GetVehicleTrackdir()); - if (IsTileType(u->tile, MP_TUNNELBRIDGE)) { + if (!HasBit(this->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(this); + for (const Train *v = this; v != NULL; v = v->Next()) { + ClearPathReservation(v, v->tile, v->GetVehicleTrackdir()); + if (IsTileType(v->tile, MP_TUNNELBRIDGE)) { /* ClearPathReservation will not free the wormhole exit * if the train has just entered the wormhole. */ - SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(u->tile), false); + SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(v->tile), false); } } - } - - /* we may need to update crossing we were approaching */ - TileIndex crossing = TrainApproachingCrossingTile(v); - - v->crash_anim_pos++; - - SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); - SetWindowDirty(WC_VEHICLE_DETAILS, v->index); - - if (v->track == TRACK_BIT_DEPOT) { - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + + /* we may need to update crossing we were approaching, + * but must be updated after the train has been marked crashed */ + TileIndex crossing = TrainApproachingCrossingTile(this); + if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing); } - InvalidateWindowClassesData(WC_TRAINS_LIST, 0); - - for (; v != NULL; v = v->Next()) { - v->vehstatus |= VS_CRASHED; - MarkSingleVehicleDirty(v); - } - - /* must be updated after the train has been marked crashed */ - if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing); -} - -static uint CountPassengersInTrain(const Train *v) -{ - uint num = 0; - - for (; v != NULL; v = v->Next()) { - if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count(); - } - - return num; + pass += Vehicle::Crash(flooded); + + this->crash_anim_pos = flooded ? 4000 : 1; // max 4440, disappear pretty fast when flooded + return pass; } /** @@ -3252,10 +3230,7 @@ static uint TrainCrashed(Train *v) /* do not crash train twice */ if (!(v->vehstatus & VS_CRASHED)) { - /* two drivers + passengers */ - num = 2 + CountPassengersInTrain(v); - - SetVehicleCrashed(v); + num = v->Crash(); AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_TRAIN)); } diff --git a/src/vehicle.cpp b/src/vehicle.cpp --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -112,6 +112,29 @@ bool Vehicle::NeedsAutomaticServicing() return NeedsServicing(); } +uint Vehicle::Crash(bool flooded) +{ + assert((this->vehstatus & VS_CRASHED) == 0); + assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains + + uint pass = 0; + /* crash all wagons, and count passengers */ + for (Vehicle *v = this; v != NULL; v = v->Next()) { + if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count(); + v->vehstatus |= VS_CRASHED; + MarkSingleVehicleDirty(v); + } + + /* Dirty some windows */ + InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0); + SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH); + SetWindowDirty(WC_VEHICLE_DETAILS, this->index); + SetWindowDirty(WC_VEHICLE_DEPOT, this->tile); + + return pass; +} + + /** * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking. * @param engine The engine that caused the problem diff --git a/src/vehicle_base.h b/src/vehicle_base.h --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -324,6 +324,13 @@ public: virtual void OnNewDay() {}; /** + * Crash the (whole) vehicle chain. + * @param flooded whether the cause of the crash is flooding or not. + * @return the number of lost souls. + */ + virtual uint Crash(bool flooded = false); + + /** * Update vehicle sprite- and position caches * @param moved Was the vehicle moved? * @param turned Did the vehicle direction change? diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -789,8 +789,8 @@ static void FloodVehicle(Vehicle *v) if (v->type == VEH_AIRCRAFT) { /* Crashing aircraft are always at z_pos == 1, never on z_pos == 0, - * because that's always the shadow. Except for the heliport, because - * that station has a big z_offset for the aircraft. */ + * because that's always the shadow. Except for the heliport, because + * that station has a big z_offset for the aircraft. */ if (!IsTileType(v->tile, MP_STATION) || !IsAirport(v->tile) || GetTileMaxZ(v->tile) != 0) return; const Station *st = Station::GetByTile(v->tile); const AirportFTAClass *airport = st->Airport(); @@ -800,47 +800,7 @@ static void FloodVehicle(Vehicle *v) v = v->First(); } - uint pass = 0; - /* crash all wagons, and count passengers */ - for (Vehicle *u = v; u != NULL; u = u->Next()) { - if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count(); - u->vehstatus |= VS_CRASHED; - MarkSingleVehicleDirty(u); - } - - switch (v->type) { - default: NOT_REACHED(); - case VEH_TRAIN: { - Train *t = Train::From(v); - if (t->IsFrontEngine()) { - pass += 4; // driver - /* FreeTrainTrackReservation() calls GetVehicleTrackdir() that doesn't like crashed vehicles. - * In this case, v->direction matches v->u.rail.track, so we can do this (it wasn't crashed before) */ - t->vehstatus &= ~VS_CRASHED; - if (!HasBit(t->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(t); - t->vehstatus |= VS_CRASHED; - } - t->crash_anim_pos = 4000; // max 4440, disappear pretty fast - InvalidateWindowClassesData(WC_TRAINS_LIST, 0); - break; - } - - case VEH_ROAD: { - RoadVehicle *rv = RoadVehicle::From(v); - if (rv->IsRoadVehFront()) pass += 1; // driver - rv->crashed_ctr = 2000; // max 2220, disappear pretty fast - InvalidateWindowClassesData(WC_ROADVEH_LIST, 0); - } break; - - case VEH_AIRCRAFT: - pass += 2; // driver - Aircraft::From(v)->crashed_counter = 9000; // max 10000, disappear pretty fast - InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0); - break; - } - - SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); - SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); + uint pass = v->Crash(true); AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED)); SetDParam(0, pass);