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