1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file cargomonitor.cpp Implementation of the cargo transport monitoring. */
9 
10 #include "stdafx.h"
11 #include "cargomonitor.h"
12 #include "station_base.h"
13 
14 #include "safeguards.h"
15 
16 CargoMonitorMap _cargo_pickups;    ///< Map of monitored pick-ups   to the amount since last query/activation.
17 CargoMonitorMap _cargo_deliveries; ///< Map of monitored deliveries to the amount since last query/activation.
18 
19 /**
20  * Helper method for #ClearCargoPickupMonitoring and #ClearCargoDeliveryMonitoring.
21  * Clears all monitors that belong to the specified company or all if #INVALID_OWNER
22  * is specified as company.
23  * @param cargo_monitor_map reference to the cargo monitor map to operate on.
24  * @param company company to clear cargo monitors for or #INVALID_OWNER if all cargo monitors should be cleared.
25  */
ClearCargoMonitoring(CargoMonitorMap & cargo_monitor_map,CompanyID company=INVALID_OWNER)26 static void ClearCargoMonitoring(CargoMonitorMap &cargo_monitor_map, CompanyID company = INVALID_OWNER)
27 {
28 	if (company == INVALID_OWNER) {
29 		cargo_monitor_map.clear();
30 		return;
31 	}
32 
33 	CargoMonitorMap::iterator next;
34 	for (CargoMonitorMap::iterator it = cargo_monitor_map.begin(); it != cargo_monitor_map.end(); it = next) {
35 		next = it;
36 		next++;
37 		if (DecodeMonitorCompany(it->first) == company) {
38 			cargo_monitor_map.erase(it);
39 		}
40 	}
41 }
42 
43 /**
44  * Clear all pick-up cargo monitors.
45  * @param company clear all pick-up monitors for this company or if #INVALID_OWNER
46  * is passed, all pick-up monitors are cleared regardless of company.
47  */
ClearCargoPickupMonitoring(CompanyID company)48 void ClearCargoPickupMonitoring(CompanyID company)
49 {
50 	ClearCargoMonitoring(_cargo_pickups, company);
51 }
52 
53 /**
54  * Clear all delivery cargo monitors.
55  * @param company clear all delivery monitors for this company or if #INVALID_OWNER
56  * is passed, all delivery monitors are cleared regardless of company.
57  */
ClearCargoDeliveryMonitoring(CompanyID company)58 void ClearCargoDeliveryMonitoring(CompanyID company)
59 {
60 	ClearCargoMonitoring(_cargo_deliveries, company);
61 }
62 
63 /**
64  * Get and reset the amount associated with a cargo monitor.
65  * @param[in,out] monitor_map Monitoring map to search (and reset for the queried entry).
66  * @param monitor Cargo monitor to query/reset.
67  * @param keep_monitoring After returning from this call, continue monitoring.
68  * @return Amount collected since last query/activation for the monitored combination.
69  */
GetAmount(CargoMonitorMap & monitor_map,CargoMonitorID monitor,bool keep_monitoring)70 static int32 GetAmount(CargoMonitorMap &monitor_map, CargoMonitorID monitor, bool keep_monitoring)
71 {
72 	CargoMonitorMap::iterator iter = monitor_map.find(monitor);
73 	if (iter == monitor_map.end()) {
74 		if (keep_monitoring) {
75 			std::pair<CargoMonitorID, uint32> p(monitor, 0);
76 			monitor_map.insert(p);
77 		}
78 		return 0;
79 	} else {
80 		int32 result = iter->second;
81 		iter->second = 0;
82 		if (!keep_monitoring) monitor_map.erase(iter);
83 		return result;
84 	}
85 }
86 
87 /**
88  * Get the amount of cargo delivered for the given cargo monitor since activation or last query.
89  * @param monitor Cargo monitor to query.
90  * @param keep_monitoring After returning from this call, continue monitoring.
91  * @return Amount of delivered cargo for the monitored combination.
92  */
GetDeliveryAmount(CargoMonitorID monitor,bool keep_monitoring)93 int32 GetDeliveryAmount(CargoMonitorID monitor, bool keep_monitoring)
94 {
95 	return GetAmount(_cargo_deliveries, monitor, keep_monitoring);
96 }
97 
98 /**
99  * Get the amount of cargo picked up for the given cargo monitor since activation or last query.
100  * @param monitor Monitoring number to query.
101  * @param keep_monitoring After returning from this call, continue monitoring.
102  * @return Amount of picked up cargo for the monitored combination.
103  * @note Cargo pick up is counted on final delivery, to prevent users getting credit for picking up cargo without delivering it.
104  */
GetPickupAmount(CargoMonitorID monitor,bool keep_monitoring)105 int32 GetPickupAmount(CargoMonitorID monitor, bool keep_monitoring)
106 {
107 	return GetAmount(_cargo_pickups, monitor, keep_monitoring);
108 }
109 
110 /**
111  * Cargo was delivered to its final destination, update the pickup and delivery maps.
112  * @param cargo_type type of cargo.
113  * @param company company delivering the cargo.
114  * @param amount Amount of cargo delivered.
115  * @param src_type type of \a src.
116  * @param src index of source.
117  * @param st station where the cargo is delivered to.
118  * @param dest industry index where the cargo is delivered to.
119  */
AddCargoDelivery(CargoID cargo_type,CompanyID company,uint32 amount,SourceType src_type,SourceID src,const Station * st,IndustryID dest)120 void AddCargoDelivery(CargoID cargo_type, CompanyID company, uint32 amount, SourceType src_type, SourceID src, const Station *st, IndustryID dest)
121 {
122 	if (amount == 0) return;
123 
124 	if (src != INVALID_SOURCE) {
125 		/* Handle pickup update. */
126 		switch (src_type) {
127 			case ST_INDUSTRY: {
128 				CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, src);
129 				CargoMonitorMap::iterator iter = _cargo_pickups.find(num);
130 				if (iter != _cargo_pickups.end()) iter->second += amount;
131 				break;
132 			}
133 			case ST_TOWN: {
134 				CargoMonitorID num = EncodeCargoTownMonitor(company, cargo_type, src);
135 				CargoMonitorMap::iterator iter = _cargo_pickups.find(num);
136 				if (iter != _cargo_pickups.end()) iter->second += amount;
137 				break;
138 			}
139 			default: break;
140 		}
141 	}
142 
143 	/* Handle delivery.
144 	 * Note that delivery in the right area is sufficient to prevent trouble with neighbouring industries or houses. */
145 
146 	/* Town delivery. */
147 	CargoMonitorID num = EncodeCargoTownMonitor(company, cargo_type, st->town->index);
148 	CargoMonitorMap::iterator iter = _cargo_deliveries.find(num);
149 	if (iter != _cargo_deliveries.end()) iter->second += amount;
150 
151 	/* Industry delivery. */
152 	for (Industry *ind : st->industries_near) {
153 		if (ind->index != dest) continue;
154 		CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, ind->index);
155 		CargoMonitorMap::iterator iter = _cargo_deliveries.find(num);
156 		if (iter != _cargo_deliveries.end()) iter->second += amount;
157 	}
158 }
159 
160