1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <fstream>
4 
5 #include "Game/Camera/CameraController.h"
6 #include "Game/Camera.h"
7 #include "Game/CameraHandler.h"
8 #include "Game/GlobalUnsynced.h"
9 #include "Game/SelectedUnitsHandler.h"
10 #include "MouseHandler.h"
11 #include "SelectionKeyHandler.h"
12 #include "Map/Ground.h"
13 #include "Sim/Misc/CategoryHandler.h"
14 #include "Sim/Misc/TeamHandler.h"
15 #include "Sim/Units/CommandAI/CommandAI.h"
16 #include "Sim/Units/UnitDef.h"
17 #include "Sim/Units/Unit.h"
18 #include "Sim/Units/UnitHandler.h"
19 #include "Sim/Units/UnitTypes/Building.h"
20 #include "System/Log/ILog.h"
21 #include "System/myMath.h"
22 #include "System/FileSystem/DataDirsAccess.h"
23 #include <boost/cstdint.hpp>
24 
25 CSelectionKeyHandler* selectionKeys;
26 
CSelectionKeyHandler()27 CSelectionKeyHandler::CSelectionKeyHandler()
28 	: selectNumber(0)
29 {
30 }
31 
~CSelectionKeyHandler()32 CSelectionKeyHandler::~CSelectionKeyHandler()
33 {
34 }
35 
ReadToken(std::string & str)36 std::string CSelectionKeyHandler::ReadToken(std::string& str)
37 {
38 	std::string ret;
39 
40 	size_t index = 0;
41 	while ((index < str.length()) && (str[index] != '_') && (str[index] != '+')) {
42 		index++;
43 	}
44 
45 	ret = str.substr(0, index);
46 	str = str.substr(index, std::string::npos);
47 
48 	return ret;
49 }
50 
51 
ReadDelimiter(std::string & str)52 std::string CSelectionKeyHandler::ReadDelimiter(std::string& str)
53 {
54 	std::string ret = str.substr(0, 1);
55 	if (str.size() >= 1) {
56 		str = str.substr(1, std::string::npos);
57 	} else {
58 		str = "";
59 	}
60 	return ret;
61 }
62 
63 
64 namespace
65 {
66 	struct Filter
67 	{
68 	public:
69 		typedef std::map<std::string, Filter*> Map;
70 
71 		/// Contains all existing filter singletons.
all__anon7ddc7e940111::Filter72 		static Map& all() {
73 			static Map instance;
74 			return instance;
75 		}
76 
~Filter__anon7ddc7e940111::Filter77 		virtual ~Filter() {}
78 
79 		/// Called immediately before the filter is used.
Prepare__anon7ddc7e940111::Filter80 		virtual void Prepare() {}
81 
82 		/// Called immediately before the filter is used for every parameter.
SetParam__anon7ddc7e940111::Filter83 		virtual void SetParam(int index, const std::string& value) {
84 			assert(false);
85 		}
86 
87 		/**
88 		 * Actual filtering, should return false if unit should be removed
89 		 * from proposed selection.
90 		 */
91 		virtual bool ShouldIncludeUnit(const CUnit* unit) const = 0;
92 
93 		/// Number of arguments this filter has.
94 		const int numArgs;
95 
96 	protected:
Filter__anon7ddc7e940111::Filter97 		Filter(const std::string& name, int args) : numArgs(args) {
98 			all().insert(Map::value_type(name, this));
99 		}
100 	};
101 
102 	// prototype / factory based approach might be better at some point?
103 	// for now these singleton filters seem ok. (they are not reentrant tho!)
104 
105 #define DECLARE_FILTER_EX(name, args, condition, extra, init) \
106 	struct name ## _Filter : public Filter { \
107 		name ## _Filter() : Filter(#name, args) { init; } \
108 		bool ShouldIncludeUnit(const CUnit* unit) const { return condition; } \
109 		extra \
110 	} name ## _filter_instance; \
111 
112 #define DECLARE_FILTER(name, condition) \
113 	DECLARE_FILTER_EX(name, 0, condition, ,)
114 
115 	DECLARE_FILTER(Builder, unit->unitDef->buildSpeed > 0)
116 	DECLARE_FILTER(Building, dynamic_cast<const CBuilding*>(unit) != NULL)
117 	DECLARE_FILTER(Transport, unit->unitDef->transportCapacity > 0)
118 	DECLARE_FILTER(Aircraft, unit->unitDef->canfly)
119 	DECLARE_FILTER(Weapons, !unit->weapons.empty())
120 	DECLARE_FILTER(Idle, unit->commandAI->commandQue.empty())
121 	DECLARE_FILTER(Waiting, !unit->commandAI->commandQue.empty() &&
122 	               (unit->commandAI->commandQue.front().GetID() == CMD_WAIT))
123 	DECLARE_FILTER(InHotkeyGroup, unit->group != NULL)
124 	DECLARE_FILTER(Radar, unit->radarRadius || unit->sonarRadius || unit->jammerRadius)
125 	DECLARE_FILTER(ManualFireUnit, unit->unitDef->canManualFire)
126 
127 	DECLARE_FILTER_EX(WeaponRange, 1, unit->maxRange > minRange,
128 		float minRange;
129 		void SetParam(int index, const std::string& value) {
130 			minRange = atof(value.c_str());
131 		},
132 		minRange=0.0f;
133 	)
134 
135 	DECLARE_FILTER_EX(AbsoluteHealth, 1, unit->health > minHealth,
136 		float minHealth;
137 		void SetParam(int index, const std::string& value) {
138 			minHealth = atof(value.c_str());
139 		},
140 		minHealth=0.0f;
141 	)
142 
143 	DECLARE_FILTER_EX(RelativeHealth, 1, unit->health / unit->maxHealth > minHealth,
144 		float minHealth;
145 		void SetParam(int index, const std::string& value) {
146 			minHealth = atof(value.c_str()) * 0.01f; // convert from percent
147 		},
148 		minHealth=0.0f;
149 	)
150 
151 	DECLARE_FILTER_EX(InPrevSel, 0, prevTypes.find(unit->unitDef->id) != prevTypes.end(),
152 		std::set<int> prevTypes;
153 		void Prepare() {
154 			prevTypes.clear();
155 			const CUnitSet& tu = selectedUnitsHandler.selectedUnits;
156 			for (CUnitSet::const_iterator si = tu.begin(); si != tu.end(); ++si) {
157 				prevTypes.insert((*si)->unitDef->id);
158 			}
159 		},
160 	)
161 
162 	DECLARE_FILTER_EX(NameContain, 1, unit->unitDef->humanName.find(name) != std::string::npos,
163 		std::string name;
164 		void SetParam(int index, const std::string& value) {
165 			name = value;
166 		},
167 	)
168 
169 	DECLARE_FILTER_EX(Category, 1, unit->category == cat,
170 		unsigned int cat;
171 		void SetParam(int index, const std::string& value) {
172 			cat = CCategoryHandler::Instance()->GetCategory(value);
173 		},
174 		cat=0;
175 	)
176 //FIXME: std::strtof is in C99 which M$ doesn't bother to support.
177 #ifdef _MSC_VER
178 	#define STRTOF strtod
179 #else
180 	#define STRTOF strtof
181 #endif
182 
183 	DECLARE_FILTER_EX(RulesParamEquals, 2, unit->modParamsMap.find(param) != unit->modParamsMap.end() &&
184 			((wantedValueStr.empty()) ? unit->modParams[unit->modParamsMap.find(param)->second].valueInt == wantedValue
185 			: unit->modParams[unit->modParamsMap.find(param)->second].valueString == wantedValueStr),
186 		std::string param;
187 		float wantedValue;
188 		std::string wantedValueStr;
189 		void SetParam(int index, const std::string& value) {
190 			switch (index) {
191 				case 0: {
192 					param = value;
193 				} break;
194 				case 1: {
195 					const char* cstr = value.c_str();
196 					char* endNumPos = NULL;
197 					wantedValue = STRTOF(cstr, &endNumPos);
198 					if (endNumPos == cstr) wantedValueStr = value;
199 				} break;
200 			}
201 		},
202 		wantedValue=0.0f;
203 	)
204 
205 #undef DECLARE_FILTER_EX
206 #undef DECLARE_FILTER
207 #undef STRTOF
208 }
209 
210 
211 
DoSelection(std::string selectString)212 void CSelectionKeyHandler::DoSelection(std::string selectString)
213 {
214 	std::list<CUnit*> selection;
215 
216 //	guicontroller->AddText(selectString.c_str());
217 	std::string s=ReadToken(selectString);
218 
219 	if(s=="AllMap"){
220 		if (!gu->spectatingFullSelect) {
221 			// team units
222 			CUnitSet* tu=&teamHandler->Team(gu->myTeam)->units;
223 			for(CUnitSet::iterator ui=tu->begin();ui!=tu->end();++ui){
224 				selection.push_back(*ui);
225 			}
226 		} else {
227 			// all units
228 			std::list<CUnit*>* au=&unitHandler->activeUnits;
229 			for (std::list<CUnit*>::iterator ui=au->begin();ui!=au->end();++ui){
230 				selection.push_back(*ui);
231 			}
232 		}
233 	} else if(s=="Visible"){
234 		if (!gu->spectatingFullSelect) {
235 			// team units in viewport
236 			CUnitSet* tu=&teamHandler->Team(gu->myTeam)->units;
237 			for (CUnitSet::iterator ui=tu->begin();ui!=tu->end();++ui){
238 				if (camera->InView((*ui)->midPos,(*ui)->radius)){
239 					selection.push_back(*ui);
240 				}
241 			}
242 		} else {
243 		  // all units in viewport
244 			std::list<CUnit*>* au=&unitHandler->activeUnits;
245 			for (std::list<CUnit*>::iterator ui=au->begin();ui!=au->end();++ui){
246 				if (camera->InView((*ui)->midPos,(*ui)->radius)){
247 					selection.push_back(*ui);
248 				}
249 			}
250 		}
251 	} else if(s=="FromMouse" || s=="FromMouseC"){
252 		// FromMouse uses distance from a point on the ground,
253 		// so essentially a selection sphere.
254 		// FromMouseC uses a cylinder shaped volume for selection,
255 		// so the heights of the units do not matter.
256 		const bool cylindrical = (s == "FromMouseC");
257 		ReadDelimiter(selectString);
258 		float maxDist=atof(ReadToken(selectString).c_str());
259 
260 		float dist = CGround::LineGroundCol(camera->GetPos(), camera->GetPos() + mouse->dir * 8000, false);
261 		float3 mp=camera->GetPos()+mouse->dir*dist;
262 		if (cylindrical) {
263 			mp.y = 0;
264 		}
265 
266 		if (!gu->spectatingFullSelect) {
267 		  // team units in mouse range
268 			CUnitSet* tu=&teamHandler->Team(gu->myTeam)->units;
269 			for(CUnitSet::iterator ui=tu->begin();ui!=tu->end();++ui){
270 				float3 up = (*ui)->pos;
271 				if (cylindrical) {
272 					up.y = 0;
273 				}
274 				if(mp.SqDistance(up) < Square(maxDist)){
275 					selection.push_back(*ui);
276 				}
277 			}
278 		} else {
279 		  // all units in mouse range
280 			std::list<CUnit*>* au=&unitHandler->activeUnits;
281 			for(std::list<CUnit*>::iterator ui=au->begin();ui!=au->end();++ui){
282 				float3 up = (*ui)->pos;
283 				if (cylindrical) {
284 					up.y = 0;
285 				}
286 				if(mp.SqDistance(up)<Square(maxDist)){
287 					selection.push_back(*ui);
288 				}
289 			}
290 		}
291 	} else if(s=="PrevSelection"){
292 		CUnitSet* su=&selectedUnitsHandler.selectedUnits;
293 		for(CUnitSet::iterator ui=su->begin();ui!=su->end();++ui){
294 			selection.push_back(*ui);
295 		}
296 	} else {
297 		LOG_L(L_WARNING, "Unknown source token %s", s.c_str());
298 		return;
299 	}
300 
301 	ReadDelimiter(selectString);
302 
303 	while(true){
304 		std::string s=ReadDelimiter(selectString);
305 		if(s=="+")
306 			break;
307 
308 		s=ReadToken(selectString);
309 
310 		bool _not=false;
311 
312 		if(s=="Not"){
313 			_not=true;
314 			ReadDelimiter(selectString);
315 			s=ReadToken(selectString);
316 		}
317 
318 		Filter::Map& filters = Filter::all();
319 		Filter::Map::iterator f = filters.find(s);
320 
321 		if (f != filters.end()) {
322 			f->second->Prepare();
323 			for (int i = 0; i < f->second->numArgs; ++i) {
324 				ReadDelimiter(selectString);
325 				f->second->SetParam(i, ReadToken(selectString));
326 			}
327 			std::list<CUnit*>::iterator ui = selection.begin();
328 			while (ui != selection.end()) {
329 				if (f->second->ShouldIncludeUnit(*ui) ^ _not) {
330 					++ui;
331 				}
332 				else {
333 					std::list<CUnit*>::iterator prev = ui++;
334 					selection.erase(prev);
335 				}
336 			}
337 		}
338 		else {
339 			LOG_L(L_WARNING, "Unknown token in filter %s", s.c_str());
340 			return;
341 		}
342 	}
343 
344 	ReadDelimiter(selectString);
345 	s=ReadToken(selectString);
346 
347 	if(s=="ClearSelection"){
348 		selectedUnitsHandler.ClearSelected();
349 
350 		ReadDelimiter(selectString);
351 		s=ReadToken(selectString);
352 	}
353 
354 	if(s=="SelectAll"){
355 		for (std::list<CUnit*>::iterator ui=selection.begin();ui!=selection.end();++ui)
356 			selectedUnitsHandler.AddUnit(*ui);
357 	} else if(s=="SelectOne"){
358 		if(selection.empty())
359 			return;
360 		if(++selectNumber>=selection.size())
361 			selectNumber=0;
362 
363 		CUnit* sel = NULL;
364 		int a=0;
365 		for (std::list<CUnit*>::iterator ui=selection.begin();ui!=selection.end() && a<=selectNumber;++ui,++a)
366 			sel=*ui;
367 
368 		if (sel == NULL)
369 			return;
370 
371 		selectedUnitsHandler.AddUnit(sel);
372 		camHandler->CameraTransition(0.8f);
373 		if(camHandler->GetCurrentControllerNum() != 0){
374 			camHandler->GetCurrentController().SetPos(sel->pos);
375 		} else {	//fps camera
376 
377 			if(camera->rot.x>-1)
378 				camera->rot.x=-1;
379 
380 			float3 wantedCamDir;
381 			wantedCamDir.x=(float)(math::sin(camera->rot.y)*math::cos(camera->rot.x));
382 			wantedCamDir.y=(float)(math::sin(camera->rot.x));
383 			wantedCamDir.z=(float)(math::cos(camera->rot.y)*math::cos(camera->rot.x));
384 			wantedCamDir.ANormalize();
385 
386 			camHandler->GetCurrentController().SetPos(sel->pos - wantedCamDir*800);
387 		}
388 	} else if(s=="SelectNum"){
389 		ReadDelimiter(selectString);
390 		int num=atoi(ReadToken(selectString).c_str());
391 
392 		if(selection.empty())
393 			return;
394 
395 		if(selectNumber>=selection.size())
396 			selectNumber=0;
397 
398 		std::list<CUnit*>::iterator ui=selection.begin();
399 		for (int a=0;a<selectNumber;++a)
400 			++ui;
401 		for (int a=0;a<num;++ui,++a){
402 			if(ui==selection.end())
403 				ui=selection.begin();
404 			selectedUnitsHandler.AddUnit(*ui);
405 		}
406 
407 		selectNumber+=num;
408 	} else if(s=="SelectPart"){
409 		ReadDelimiter(selectString);
410 		float part=atof(ReadToken(selectString).c_str())*0.01f;//convert from percent
411 		int num=(int)(selection.size()*part);
412 
413 		if(selection.empty())
414 			return;
415 
416 		if(selectNumber>=selection.size())
417 			selectNumber=0;
418 
419 		std::list<CUnit*>::iterator ui = selection.begin();
420 		for (int a = 0; a < selectNumber; ++a)
421 			++ui;
422 		for(int a=0;a<num;++ui,++a){
423 			if(ui==selection.end())
424 				ui=selection.begin();
425 			selectedUnitsHandler.AddUnit(*ui);
426 		}
427 
428 		selectNumber+=num;
429 	} else {
430 		LOG_L(L_WARNING, "Unknown token in conclusion %s", s.c_str());
431 	}
432 }
433