diff --git a/src/group.h b/src/group.h index 557fdfbdea..cfc1dd578d 100644 --- a/src/group.h +++ b/src/group.h @@ -26,18 +26,29 @@ struct GroupStatistics { uint16 num_vehicle; ///< Number of vehicles. uint16 *num_engines; ///< Caches the number of engines of each type the company owns. + uint16 num_profit_vehicle; ///< Number of vehicles considered for profit statistics; + Money profit_last_year; ///< Sum of profits for all vehicles. + GroupStatistics(); ~GroupStatistics(); void Clear(); + void ClearProfits() + { + this->num_profit_vehicle = 0; + this->profit_last_year = 0; + } + static GroupStatistics &Get(CompanyID company, GroupID id_g, VehicleType type); static GroupStatistics &Get(const Vehicle *v); static GroupStatistics &GetAllGroup(const Vehicle *v); static void CountVehicle(const Vehicle *v, int delta); static void CountEngine(const Vehicle *v, int delta); + static void VehicleReachedProfitAge(const Vehicle *v); + static void UpdateProfits(); static void UpdateAfterLoad(); }; diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index a919ca53f6..0656d98ede 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -48,6 +48,8 @@ GroupStatistics::~GroupStatistics() void GroupStatistics::Clear() { this->num_vehicle = 0; + this->num_profit_vehicle = 0; + this->profit_last_year = 0; /* This is also called when NewGRF change. So the number of engines might have changed. Reallocate. */ free(this->num_engines); @@ -139,6 +141,13 @@ void GroupStatistics::Clear() stats_all.num_vehicle += delta; stats.num_vehicle += delta; + + if (v->age > VEHICLE_PROFIT_MIN_AGE) { + stats_all.num_profit_vehicle += delta; + stats_all.profit_last_year += v->GetDisplayProfitLastYear() * delta; + stats.num_profit_vehicle += delta; + stats.profit_last_year += v->GetDisplayProfitLastYear() * delta; + } } /** @@ -153,6 +162,45 @@ void GroupStatistics::Clear() GroupStatistics::Get(v).num_engines[v->engine_type] += delta; } +/** + * Add a vehicle to the profit sum of its group. + */ +/* static */ void GroupStatistics::VehicleReachedProfitAge(const Vehicle *v) +{ + GroupStatistics &stats_all = GroupStatistics::GetAllGroup(v); + GroupStatistics &stats = GroupStatistics::Get(v); + + stats_all.num_profit_vehicle++; + stats_all.profit_last_year += v->GetDisplayProfitLastYear(); + stats.num_profit_vehicle++; + stats.profit_last_year += v->GetDisplayProfitLastYear(); +} + +/** + * Recompute the profits for all groups. + */ +/* static */ void GroupStatistics::UpdateProfits() +{ + /* Set up the engine count for all companies */ + Company *c; + FOR_ALL_COMPANIES(c) { + for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) { + c->group_all[type].ClearProfits(); + c->group_default[type].ClearProfits(); + } + } + + /* Recalculate */ + Group *g; + FOR_ALL_GROUPS(g) { + g->statistics.ClearProfits(); + } + + const Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->IsPrimaryVehicle() && v->age > VEHICLE_PROFIT_MIN_AGE) GroupStatistics::VehicleReachedProfitAge(v); + } +} /** * Update the num engines of a groupID. Decrease the old one and increase the new one diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 048d346dce..5a0a46b7f6 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -117,6 +117,7 @@ private: /* Columns in the group list */ enum ListColumns { VGC_NAME, ///< Group name. + VGC_PROFIT, ///< Profit icon. VGC_NUMBER, ///< Number of vehicles in the group. VGC_END @@ -185,6 +186,15 @@ private: this->column_size[VGC_NAME].width = max(170u, this->column_size[VGC_NAME].width); this->tiny_step_height = this->column_size[VGC_NAME].height; + this->column_size[VGC_PROFIT].width = 0; + this->column_size[VGC_PROFIT].height = 0; + static const SpriteID profit_sprites[] = {SPR_PROFIT_NA, SPR_PROFIT_NEGATIVE, SPR_PROFIT_SOME, SPR_PROFIT_LOT}; + for (uint i = 0; i < lengthof(profit_sprites); i++) { + Dimension d = GetSpriteSize(profit_sprites[i]); + this->column_size[VGC_PROFIT] = maxdim(this->column_size[VGC_PROFIT], d); + } + this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_PROFIT].height); + SetDParam(0, GroupStatistics::Get(this->vli.company, ALL_GROUP, this->vli.vtype).num_vehicle > 900 ? 9999 : 999); this->column_size[VGC_NUMBER] = GetStringBoundingBox(STR_TINY_COMMA); this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_NUMBER].height); @@ -193,6 +203,7 @@ private: return WD_FRAMERECT_LEFT + 8 + this->column_size[VGC_NAME].width + 8 + + this->column_size[VGC_PROFIT].width + 2 + this->column_size[VGC_NUMBER].width + 2 + WD_FRAMERECT_RIGHT; } @@ -224,8 +235,22 @@ private: int x = rtl ? right - WD_FRAMERECT_RIGHT - 8 - this->column_size[VGC_NAME].width + 1 : left + WD_FRAMERECT_LEFT + 8; DrawString(x, x + this->column_size[VGC_NAME].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour); + /* draw the profit icon */ + x = rtl ? x - 8 - this->column_size[VGC_PROFIT].width : x + 8 + this->column_size[VGC_NAME].width; + SpriteID spr; + if (stats.num_profit_vehicle == 0) { + spr = SPR_PROFIT_NA; + } else if (stats.profit_last_year < 0) { + spr = SPR_PROFIT_NEGATIVE; + } else if (stats.profit_last_year < 10000 * stats.num_profit_vehicle) { // TODO magic number + spr = SPR_PROFIT_SOME; + } else { + spr = SPR_PROFIT_LOT; + } + DrawSprite(spr, PAL_NONE, x, y + (this->tiny_step_height - this->column_size[VGC_PROFIT].height) / 2); + /* draw the number of vehicles of the group */ - x = rtl ? x - 8 - this->column_size[VGC_NUMBER].width : x + 8 + this->column_size[VGC_NAME].width; + x = rtl ? x - 2 - this->column_size[VGC_NUMBER].width : x + 2 + this->column_size[VGC_PROFIT].width; SetDParam(0, stats.num_vehicle); DrawString(x, x + this->column_size[VGC_NUMBER].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NUMBER].height) / 2, STR_TINY_COMMA, colour, SA_RIGHT | SA_FORCE); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 86099507df..a1e1e9d2ea 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1192,6 +1192,12 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u Train *original_src_head = src_head; Train *original_dst_head = (dst_head == src_head ? NULL : dst_head); + if (flags & DC_EXEC) { + /* Remove old heads from the statistics */ + if (original_src_head != NULL && original_src_head->IsFrontEngine()) GroupStatistics::CountVehicle(original_src_head, -1); + if (original_dst_head != NULL && original_dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(original_dst_head, -1); + } + /* (Re)arrange the trains in the wanted arrangement. */ ArrangeTrains(&dst_head, dst, &src_head, src, move_chain); @@ -1242,18 +1248,6 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u DeleteWindowById(WC_VEHICLE_DETAILS, src->index); DeleteWindowById(WC_VEHICLE_TIMETABLE, src->index); - /* We are going to be moved to a different train, and - * we were the front engine of the original train. */ - if (dst_head != NULL && dst_head != src && (src_head == NULL || !src_head->IsFrontEngine())) { - GroupStatistics::CountVehicle(src, -1); - } - - /* The front engine is going to be moved later in the - * current train, and it will not be a train anymore. */ - if (dst_head == NULL && !src_head->IsFrontEngine()) { - GroupStatistics::CountVehicle(src, -1); - } - /* Delete orders, group stuff and the unit number as we're not the * front of any vehicle anymore. */ DeleteVehicleOrders(src); @@ -1261,18 +1255,16 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u src->unitnumber = 0; } - /* We were a front engine and we are becoming one for a different train. - * Increase the group counter accordingly. */ - if (original_src_head == src && dst_head == src) { - GroupStatistics::CountVehicle(src, 1); - } - /* We weren't a front engine but are becoming one. So * we should be put in the default group. */ if (original_src_head != src && dst_head == src) { SetTrainGroupID(src, DEFAULT_GROUP); } + /* Add new heads to statistics */ + if (src_head != NULL && src_head->IsFrontEngine()) GroupStatistics::CountVehicle(src_head, 1); + if (dst_head != NULL && dst_head->IsFrontEngine()) GroupStatistics::CountVehicle(dst_head, 1); + /* Handle 'new engine' part of cases #1b, #2b, #3b, #4b and #5 in NormaliseTrainHead. */ NormaliseTrainHead(src_head); NormaliseTrainHead(dst_head); @@ -1354,7 +1346,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3 /* Copy other important data from the front engine */ new_head->CopyVehicleConfigAndStatistics(first); - GroupStatistics::CountVehicle(new_head, 1); + GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit /* If we deleted a window then open a new one for the 'new' train */ if (IsLocalCompany() && w != NULL) ShowVehicleViewWindow(new_head); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 890e5e2219..dce61e2390 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1137,7 +1137,10 @@ bool Vehicle::HandleBreakdown() */ void AgeVehicle(Vehicle *v) { - if (v->age < MAX_DAY) v->age++; + if (v->age < MAX_DAY) { + v->age++; + if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v); + } if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return; @@ -2402,6 +2405,11 @@ void VehiclesYearlyLoop() SetWindowDirty(WC_VEHICLE_DETAILS, v->index); } } + GroupStatistics::UpdateProfits(); + SetWindowClassesDirty(WC_TRAINS_LIST); + SetWindowClassesDirty(WC_SHIPS_LIST); + SetWindowClassesDirty(WC_ROADVEH_LIST); + SetWindowClassesDirty(WC_AIRCRAFT_LIST); } diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 475c8a313b..efc07ed075 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -26,6 +26,9 @@ #define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD) #define IS_CUSTOM_SECONDHEAD_SPRITE(x) (x == 0xFE) +static const int VEHICLE_PROFIT_MIN_AGE = DAYS_IN_YEAR * 2; ///< Only vehicles older than this have a meaningful profit. +static const Money VEHICLE_PROFIT_THRESHOLD = 10000; ///< Threshold for a vehicle to be considered making good profit. + typedef Vehicle *VehicleFromPosProc(Vehicle *v, void *data); void VehicleServiceInDepot(Vehicle *v); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 512a5568bf..c3cc8dade9 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -193,11 +193,11 @@ static void DrawVehicleProfitButton(const Vehicle *v, int x, int y) SpriteID spr; /* draw profit-based coloured icons */ - if (v->age <= DAYS_IN_YEAR * 2) { + if (v->age <= VEHICLE_PROFIT_MIN_AGE) { spr = SPR_PROFIT_NA; } else if (v->GetDisplayProfitLastYear() < 0) { spr = SPR_PROFIT_NEGATIVE; - } else if (v->GetDisplayProfitLastYear() < 10000) { + } else if (v->GetDisplayProfitLastYear() < VEHICLE_PROFIT_THRESHOLD) { spr = SPR_PROFIT_SOME; } else { spr = SPR_PROFIT_LOT;