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 script_stationlist.cpp Implementation of ScriptStationList and friends. */
9 
10 #include "../../stdafx.h"
11 #include "script_stationlist.hpp"
12 #include "script_vehicle.hpp"
13 #include "script_cargo.hpp"
14 #include "../../station_base.h"
15 #include "../../vehicle_base.h"
16 
17 #include "../../safeguards.h"
18 
ScriptStationList(ScriptStation::StationType station_type)19 ScriptStationList::ScriptStationList(ScriptStation::StationType station_type)
20 {
21 	for (Station *st : Station::Iterate()) {
22 		if ((st->owner == ScriptObject::GetCompany() || ScriptObject::GetCompany() == OWNER_DEITY) && (st->facilities & station_type) != 0) this->AddItem(st->index);
23 	}
24 }
25 
ScriptStationList_Vehicle(VehicleID vehicle_id)26 ScriptStationList_Vehicle::ScriptStationList_Vehicle(VehicleID vehicle_id)
27 {
28 	if (!ScriptVehicle::IsValidVehicle(vehicle_id)) return;
29 
30 	Vehicle *v = ::Vehicle::Get(vehicle_id);
31 
32 	for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
33 		if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination());
34 	}
35 }
36 
ScriptStationList_Cargo(ScriptStationList_Cargo::CargoMode mode,ScriptStationList_Cargo::CargoSelector selector,StationID station_id,CargoID cargo,StationID other_station)37 ScriptStationList_Cargo::ScriptStationList_Cargo(ScriptStationList_Cargo::CargoMode mode,
38 		ScriptStationList_Cargo::CargoSelector selector, StationID station_id, CargoID cargo,
39 		StationID other_station)
40 {
41 	switch (mode) {
42 		case CM_WAITING:
43 			ScriptStationList_CargoWaiting(selector, station_id, cargo, other_station).SwapList(this);
44 			break;
45 		case CM_PLANNED:
46 			ScriptStationList_CargoPlanned(selector, station_id, cargo, other_station).SwapList(this);
47 			break;
48 		default:
49 			NOT_REACHED();
50 	}
51 }
52 
ScriptStationList_CargoWaiting(ScriptStationList_Cargo::CargoSelector selector,StationID station_id,CargoID cargo,StationID other_station)53 ScriptStationList_CargoWaiting::ScriptStationList_CargoWaiting(
54 		ScriptStationList_Cargo::CargoSelector selector, StationID station_id, CargoID cargo,
55 		StationID other_station)
56 {
57 	switch (selector) {
58 		case CS_BY_FROM:
59 			ScriptStationList_CargoWaitingByFrom(station_id, cargo).SwapList(this);
60 			break;
61 		case CS_VIA_BY_FROM:
62 			ScriptStationList_CargoWaitingViaByFrom(station_id, cargo, other_station).SwapList(this);
63 			break;
64 		case CS_BY_VIA:
65 			ScriptStationList_CargoWaitingByVia(station_id, cargo).SwapList(this);
66 			break;
67 		case CS_FROM_BY_VIA:
68 			ScriptStationList_CargoWaitingFromByVia(station_id, cargo, other_station).SwapList(this);
69 			break;
70 		default:
71 			NOT_REACHED();
72 	}
73 }
74 
ScriptStationList_CargoPlanned(ScriptStationList_Cargo::CargoSelector selector,StationID station_id,CargoID cargo,StationID other_station)75 ScriptStationList_CargoPlanned::ScriptStationList_CargoPlanned(
76 		ScriptStationList_Cargo::CargoSelector selector, StationID station_id, CargoID cargo,
77 		StationID other_station)
78 {
79 	switch (selector) {
80 		case CS_BY_FROM:
81 			ScriptStationList_CargoPlannedByFrom(station_id, cargo).SwapList(this);
82 			break;
83 		case CS_VIA_BY_FROM:
84 			ScriptStationList_CargoPlannedViaByFrom(station_id, cargo, other_station).SwapList(this);
85 			break;
86 		case CS_BY_VIA:
87 			ScriptStationList_CargoPlannedByVia(station_id, cargo).SwapList(this);
88 			break;
89 		case CS_FROM_BY_VIA:
90 			ScriptStationList_CargoPlannedFromByVia(station_id, cargo, other_station).SwapList(this);
91 			break;
92 		default:
93 			NOT_REACHED();
94 	}
95 }
96 
97 class CargoCollector {
98 public:
99 	CargoCollector(ScriptStationList_Cargo *parent, StationID station_id, CargoID cargo,
100 			StationID other);
101 	~CargoCollector() ;
102 
103 	template<ScriptStationList_Cargo::CargoSelector Tselector>
104 	void Update(StationID from, StationID via, uint amount);
GE() const105 	const GoodsEntry *GE() const { return ge; }
106 
107 private:
108 	void SetValue();
109 
110 	ScriptStationList_Cargo *list;
111 	const GoodsEntry *ge;
112 	StationID other_station;
113 
114 	StationID last_key;
115 	uint amount;
116 };
117 
CargoCollector(ScriptStationList_Cargo * parent,StationID station_id,CargoID cargo,StationID other)118 CargoCollector::CargoCollector(ScriptStationList_Cargo *parent,
119 		StationID station_id, CargoID cargo, StationID other) :
120 	list(parent), ge(nullptr), other_station(other), last_key(INVALID_STATION), amount(0)
121 {
122 	if (!ScriptStation::IsValidStation(station_id)) return;
123 	if (!ScriptCargo::IsValidCargo(cargo)) return;
124 	this->ge = &(Station::Get(station_id)->goods[cargo]);
125 }
126 
~CargoCollector()127 CargoCollector::~CargoCollector()
128 {
129 	this->SetValue();
130 }
131 
SetValue()132 void CargoCollector::SetValue()
133 {
134 	if (this->amount > 0) {
135 		if (this->list->HasItem(this->last_key)) {
136 			this->list->SetValue(this->last_key,
137 					this->list->GetValue(this->last_key) + this->amount);
138 		} else {
139 			this->list->AddItem(this->last_key, this->amount);
140 		}
141 	}
142 }
143 
144 template<ScriptStationList_Cargo::CargoSelector Tselector>
Update(StationID from,StationID via,uint amount)145 void CargoCollector::Update(StationID from, StationID via, uint amount)
146 {
147 	StationID key = INVALID_STATION;
148 	switch (Tselector) {
149 		case ScriptStationList_Cargo::CS_VIA_BY_FROM:
150 			if (via != this->other_station) return;
151 			FALLTHROUGH;
152 		case ScriptStationList_Cargo::CS_BY_FROM:
153 			key = from;
154 			break;
155 		case ScriptStationList_Cargo::CS_FROM_BY_VIA:
156 			if (from != this->other_station) return;
157 			FALLTHROUGH;
158 		case ScriptStationList_Cargo::CS_BY_VIA:
159 			key = via;
160 			break;
161 	}
162 	if (key == this->last_key) {
163 		this->amount += amount;
164 	} else {
165 		this->SetValue();
166 		this->amount = amount;
167 		this->last_key = key;
168 	}
169 }
170 
171 
172 template<ScriptStationList_Cargo::CargoSelector Tselector>
Add(StationID station_id,CargoID cargo,StationID other_station)173 void ScriptStationList_CargoWaiting::Add(StationID station_id, CargoID cargo, StationID other_station)
174 {
175 	CargoCollector collector(this, station_id, cargo, other_station);
176 	if (collector.GE() == nullptr) return;
177 
178 	StationCargoList::ConstIterator iter = collector.GE()->cargo.Packets()->begin();
179 	StationCargoList::ConstIterator end = collector.GE()->cargo.Packets()->end();
180 	for (; iter != end; ++iter) {
181 		collector.Update<Tselector>((*iter)->SourceStation(), iter.GetKey(), (*iter)->Count());
182 	}
183 }
184 
185 
186 template<ScriptStationList_Cargo::CargoSelector Tselector>
Add(StationID station_id,CargoID cargo,StationID other_station)187 void ScriptStationList_CargoPlanned::Add(StationID station_id, CargoID cargo, StationID other_station)
188 {
189 	CargoCollector collector(this, station_id, cargo, other_station);
190 	if (collector.GE() == nullptr) return;
191 
192 	FlowStatMap::const_iterator iter = collector.GE()->flows.begin();
193 	FlowStatMap::const_iterator end = collector.GE()->flows.end();
194 	for (; iter != end; ++iter) {
195 		const FlowStat::SharesMap *shares = iter->second.GetShares();
196 		uint prev = 0;
197 		for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin();
198 				flow_iter != shares->end(); ++flow_iter) {
199 			collector.Update<Tselector>(iter->first, flow_iter->second, flow_iter->first - prev);
200 			prev = flow_iter->first;
201 		}
202 	}
203 }
204 
ScriptStationList_CargoWaitingByFrom(StationID station_id,CargoID cargo)205 ScriptStationList_CargoWaitingByFrom::ScriptStationList_CargoWaitingByFrom(StationID station_id,
206 		CargoID cargo)
207 {
208 	this->Add<CS_BY_FROM>(station_id, cargo);
209 }
210 
ScriptStationList_CargoWaitingViaByFrom(StationID station_id,CargoID cargo,StationID via)211 ScriptStationList_CargoWaitingViaByFrom::ScriptStationList_CargoWaitingViaByFrom(
212 		StationID station_id, CargoID cargo, StationID via)
213 {
214 	CargoCollector collector(this, station_id, cargo, via);
215 	if (collector.GE() == nullptr) return;
216 
217 	std::pair<StationCargoList::ConstIterator, StationCargoList::ConstIterator> range =
218 			collector.GE()->cargo.Packets()->equal_range(via);
219 	for (StationCargoList::ConstIterator iter = range.first; iter != range.second; ++iter) {
220 		collector.Update<CS_VIA_BY_FROM>((*iter)->SourceStation(), iter.GetKey(), (*iter)->Count());
221 	}
222 }
223 
224 
ScriptStationList_CargoWaitingByVia(StationID station_id,CargoID cargo)225 ScriptStationList_CargoWaitingByVia::ScriptStationList_CargoWaitingByVia(StationID station_id,
226 		CargoID cargo)
227 {
228 	this->Add<CS_BY_VIA>(station_id, cargo);
229 }
230 
ScriptStationList_CargoWaitingFromByVia(StationID station_id,CargoID cargo,StationID from)231 ScriptStationList_CargoWaitingFromByVia::ScriptStationList_CargoWaitingFromByVia(
232 		StationID station_id, CargoID cargo, StationID from)
233 {
234 	this->Add<CS_FROM_BY_VIA>(station_id, cargo, from);
235 }
236 
ScriptStationList_CargoPlannedByFrom(StationID station_id,CargoID cargo)237 ScriptStationList_CargoPlannedByFrom::ScriptStationList_CargoPlannedByFrom(StationID station_id,
238 		CargoID cargo)
239 {
240 	this->Add<CS_BY_FROM>(station_id, cargo);
241 }
242 
ScriptStationList_CargoPlannedViaByFrom(StationID station_id,CargoID cargo,StationID via)243 ScriptStationList_CargoPlannedViaByFrom::ScriptStationList_CargoPlannedViaByFrom(
244 		StationID station_id, CargoID cargo, StationID via)
245 {
246 	this->Add<CS_VIA_BY_FROM>(station_id, cargo, via);
247 }
248 
249 
ScriptStationList_CargoPlannedByVia(StationID station_id,CargoID cargo)250 ScriptStationList_CargoPlannedByVia::ScriptStationList_CargoPlannedByVia(StationID station_id,
251 		CargoID cargo)
252 {
253 	this->Add<CS_BY_VIA>(station_id, cargo);
254 }
255 
256 
ScriptStationList_CargoPlannedFromByVia(StationID station_id,CargoID cargo,StationID from)257 ScriptStationList_CargoPlannedFromByVia::ScriptStationList_CargoPlannedFromByVia(
258 		StationID station_id, CargoID cargo, StationID from)
259 {
260 	CargoCollector collector(this, station_id, cargo, from);
261 	if (collector.GE() == nullptr) return;
262 
263 	FlowStatMap::const_iterator iter = collector.GE()->flows.find(from);
264 	if (iter == collector.GE()->flows.end()) return;
265 	const FlowStat::SharesMap *shares = iter->second.GetShares();
266 	uint prev = 0;
267 	for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin();
268 			flow_iter != shares->end(); ++flow_iter) {
269 		collector.Update<CS_FROM_BY_VIA>(iter->first, flow_iter->second, flow_iter->first - prev);
270 		prev = flow_iter->first;
271 	}
272 }
273