1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include "GuiHandler.h"
4
5
6 #include "CommandColors.h"
7 #include "KeyBindings.h"
8 #include "KeyCodes.h"
9 #include "MiniMap.h"
10 #include "MouseHandler.h"
11 #include "Game/Camera.h"
12 #include "Game/Game.h"
13 #include "Game/GameHelper.h"
14 #include "Game/GlobalUnsynced.h"
15 #include "Game/SelectedUnitsHandler.h"
16 #include "Game/TraceRay.h"
17 #include "Lua/LuaConfig.h"
18 #include "Lua/LuaTextures.h"
19 #include "Lua/LuaGaia.h"
20 #include "Lua/LuaRules.h"
21 #include "Lua/LuaUI.h"
22 #include "Map/BaseGroundDrawer.h"
23 #include "Map/Ground.h"
24 #include "Map/MapInfo.h"
25 #include "Map/MetalMap.h"
26 #include "Map/ReadMap.h"
27 #include "Rendering/Fonts/glFont.h"
28 #include "Rendering/IconHandler.h"
29 #include "Rendering/UnitDrawer.h"
30 #include "Rendering/GL/glExtra.h"
31 #include "Rendering/Textures/Bitmap.h"
32 #include "Rendering/Textures/NamedTextures.h"
33 #include "Sim/Features/Feature.h"
34 #include "Sim/Misc/LosHandler.h"
35 #include "Sim/Units/CommandAI/CommandAI.h"
36 #include "Sim/Units/CommandAI/BuilderCAI.h"
37 #include "Sim/Units/UnitDefHandler.h"
38 #include "Sim/Units/Unit.h"
39 #include "Sim/Units/UnitHandler.h"
40 #include "Sim/Units/UnitLoader.h"
41 #include "Sim/Weapons/WeaponDefHandler.h"
42 #include "Sim/Weapons/Weapon.h"
43 #include "System/Config/ConfigHandler.h"
44 #include "System/EventHandler.h"
45 #include "System/GlobalConfig.h"
46 #include "System/Log/ILog.h"
47 #include "System/myMath.h"
48 #include "System/Util.h"
49 #include "System/Input/KeyInput.h"
50 #include "System/Sound/ISound.h"
51 #include "System/Sound/ISoundChannels.h"
52 #include "System/FileSystem/FileHandler.h"
53 #include "System/FileSystem/SimpleParser.h"
54
55 #include <map>
56 #include <set>
57 #include <list>
58
59 #include <SDL_keycode.h>
60 #include <SDL_mouse.h>
61
62 CONFIG(bool, MiniMapMarker).defaultValue(true);
63 CONFIG(bool, InvertQueueKey).defaultValue(false);
64
65 //////////////////////////////////////////////////////////////////////
66 // Construction/Destruction
67 //////////////////////////////////////////////////////////////////////
68
69
70 CGuiHandler* guihandler = NULL;
71
72
CGuiHandler()73 CGuiHandler::CGuiHandler():
74 inCommand(-1),
75 buildFacing(FACING_SOUTH),
76 buildSpacing(0),
77 needShift(false),
78 showingMetal(false),
79 activeMousePress(false),
80 forceLayoutUpdate(false),
81 maxPage(0),
82 activePage(0),
83 defaultCmdMemory(-1),
84 explicitCommand(-1),
85 curIconCommand(-1),
86 actionOffset(0),
87 drawSelectionInfo(true),
88 gatherMode(false)
89 {
90 icons = new IconInfo[16];
91 iconsSize = 16;
92 iconsCount = 0;
93
94 LoadDefaults();
95 LoadConfig("ctrlpanel.txt");
96
97 miniMapMarker = configHandler->GetBool("MiniMapMarker");
98 invertQueueKey = configHandler->GetBool("InvertQueueKey");
99
100 autoShowMetal = mapInfo->gui.autoShowMetal;
101
102 useStencil = false;
103 if (GLEW_NV_depth_clamp) {
104 GLint stencilBits;
105 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
106 useStencil = (stencilBits >= 1);
107 }
108
109 failedSound = sound->GetSoundId("FailedCommand");
110 }
111
112
~CGuiHandler()113 CGuiHandler::~CGuiHandler()
114 {
115 CLuaUI::FreeHandler();
116
117 delete[] icons;
118
119 for (auto& p: textureMap) {
120 glDeleteTextures(1, &p.second);
121 }
122 }
123
124
GetQueueKeystate() const125 bool CGuiHandler::GetQueueKeystate() const
126 {
127 return (!invertQueueKey && KeyInput::GetKeyModState(KMOD_SHIFT)) ||
128 (invertQueueKey && !KeyInput::GetKeyModState(KMOD_SHIFT));
129 }
130
131
LoadDefaults()132 void CGuiHandler::LoadDefaults()
133 {
134 xIcons = 2;
135 yIcons = 8;
136
137 xPos = 0.000f;
138 yPos = 0.175f;
139 xIconSize = 0.060f;
140 yIconSize = 0.060f;
141 textBorder = 0.003f;
142 iconBorder = 0.003f;
143 frameBorder = 0.003f;
144
145 xSelectionPos = 0.018f;
146 ySelectionPos = 0.127f;
147
148 frameAlpha = -1.0f;
149 textureAlpha = 0.8f;
150
151 dropShadows = true;
152 useOptionLEDs = true;
153
154 selectGaps = true;
155 selectThrough = false;
156
157 menuName = "";
158
159 outlineFonts = false;
160
161 attackRect = false;
162 newAttackMode = true;
163 invColorSelect = true;
164 frontByEnds = false;
165 }
166
167
SafeAtoF(float & var,const std::string & value)168 static bool SafeAtoF(float& var, const std::string& value)
169 {
170 char* endPtr;
171 const char* startPtr = value.c_str();
172 const float tmp = (float)strtod(startPtr, &endPtr);
173 if (endPtr == startPtr) {
174 return false;
175 }
176 var = tmp;
177 return true;
178 }
179
180
LoadConfig(const std::string & cfg)181 bool CGuiHandler::LoadConfig(const std::string& cfg)
182 {
183 CSimpleParser parser(cfg);
184
185 std::string deadStr = "";
186 std::string prevStr = "";
187 std::string nextStr = "";
188 std::string fillOrderStr = "";
189
190 while (true) {
191 const std::string line = parser.GetCleanLine();
192 if (line.empty()) {
193 break;
194 }
195
196 const std::vector<std::string> &words = parser.Tokenize(line, 1);
197
198 const std::string command = StringToLower(words[0]);
199
200 if ((command == "dropshadows") && (words.size() > 1)) {
201 dropShadows = !!atoi(words[1].c_str());
202 }
203 else if ((command == "useoptionleds") && (words.size() > 1)) {
204 useOptionLEDs = !!atoi(words[1].c_str());
205 }
206 else if ((command == "selectgaps") && (words.size() > 1)) {
207 selectGaps = !!atoi(words[1].c_str());
208 }
209 else if ((command == "selectthrough") && (words.size() > 1)) {
210 selectThrough = !!atoi(words[1].c_str());
211 }
212 else if ((command == "deadiconslot") && (words.size() > 1)) {
213 deadStr = StringToLower(words[1]);
214 }
215 else if ((command == "prevpageslot") && (words.size() > 1)) {
216 prevStr = StringToLower(words[1]);
217 }
218 else if ((command == "nextpageslot") && (words.size() > 1)) {
219 nextStr = StringToLower(words[1]);
220 }
221 else if ((command == "fillorder") && (words.size() > 1)) {
222 fillOrderStr = StringToLower(words[1]);
223 }
224 else if ((command == "xicons") && (words.size() > 1)) {
225 xIcons = atoi(words[1].c_str());
226 }
227 else if ((command == "yicons") && (words.size() > 1)) {
228 yIcons = atoi(words[1].c_str());
229 }
230 else if ((command == "xiconsize") && (words.size() > 1)) {
231 SafeAtoF(xIconSize, words[1]);
232 }
233 else if ((command == "yiconsize") && (words.size() > 1)) {
234 SafeAtoF(yIconSize, words[1]);
235 }
236 else if ((command == "xpos") && (words.size() > 1)) {
237 SafeAtoF(xPos, words[1]);
238 }
239 else if ((command == "ypos") && (words.size() > 1)) {
240 SafeAtoF(yPos, words[1]);
241 }
242 else if ((command == "textborder") && (words.size() > 1)) {
243 SafeAtoF(textBorder, words[1]);
244 }
245 else if ((command == "iconborder") && (words.size() > 1)) {
246 SafeAtoF(iconBorder, words[1]);
247 }
248 else if ((command == "frameborder") && (words.size() > 1)) {
249 SafeAtoF(frameBorder, words[1]);
250 }
251 else if ((command == "xselectionpos") && (words.size() > 1)) {
252 SafeAtoF(xSelectionPos, words[1]);
253 }
254 else if ((command == "yselectionpos") && (words.size() > 1)) {
255 SafeAtoF(ySelectionPos, words[1]);
256 }
257 else if ((command == "framealpha") && (words.size() > 1)) {
258 SafeAtoF(frameAlpha, words[1]);
259 }
260 else if ((command == "texturealpha") && (words.size() > 1)) {
261 SafeAtoF(textureAlpha, words[1]);
262 }
263 else if ((command == "outlinefont") && (words.size() > 1)) {
264 outlineFonts = !!atoi(words[1].c_str());
265 }
266 else if ((command == "attackrect") && (words.size() > 1)) {
267 attackRect = !!atoi(words[1].c_str());
268 }
269 else if ((command == "newattackmode") && (words.size() > 1)) {
270 newAttackMode = !!atoi(words[1].c_str());
271 }
272 else if ((command == "invcolorselect") && (words.size() > 1)) {
273 invColorSelect = !!atoi(words[1].c_str());
274 }
275 else if ((command == "frontbyends") && (words.size() > 1)) {
276 frontByEnds = !!atoi(words[1].c_str());
277 }
278 }
279
280 // sane clamps
281 xIcons = std::max(2, xIcons);
282 yIcons = std::max(2, yIcons);
283 xIconSize = std::max(0.010f, xIconSize);
284 yIconSize = std::max(0.010f, yIconSize);
285 iconBorder = std::max(0.0f, iconBorder);
286 frameBorder = std::max(0.0f, frameBorder);
287
288 xIconStep = xIconSize + (iconBorder * 2.0f);
289 yIconStep = yIconSize + (iconBorder * 2.0f);
290
291 iconsPerPage = (xIcons * yIcons);
292
293 buttonBox.x1 = xPos;
294 buttonBox.x2 = xPos + (frameBorder * 2.0f) + (xIcons * xIconStep);
295 buttonBox.y1 = yPos;
296 buttonBox.y2 = yPos + (frameBorder * 2.0f) + (yIcons * yIconStep);
297
298 if (!deadStr.empty()) {
299 deadIconSlot = ParseIconSlot(deadStr);
300 } else {
301 deadIconSlot = -1;
302 }
303
304 if (!prevStr.empty() && (prevStr != "auto")) {
305 if (prevStr == "none") {
306 prevPageSlot = -1;
307 } else {
308 prevPageSlot = ParseIconSlot(prevStr);
309 }
310 } else {
311 prevPageSlot = iconsPerPage - 2;
312 }
313
314 if (!nextStr.empty() && (nextStr != "auto")) {
315 if (nextStr == "none") {
316 nextPageSlot = -1;
317 } else {
318 nextPageSlot = ParseIconSlot(nextStr);
319 }
320 } else {
321 nextPageSlot = iconsPerPage - 1;
322 }
323
324 if (deadIconSlot >= 0) {
325 const float fullBorder = frameBorder + iconBorder;
326 const float fx = (float)(deadIconSlot % xIcons);
327 const float fy = (float)(deadIconSlot / xIcons);
328 xBpos = buttonBox.x1 + (fullBorder + (0.5 * xIconSize) + (fx * xIconStep));
329 yBpos = buttonBox.y2 - (fullBorder + (0.5 * yIconSize) + (fy * yIconStep));
330 }
331 else if ((prevPageSlot >= 0) && (nextPageSlot >= 0)) {
332 // place in the middle of adjacent paging icons
333 const int delta = abs(prevPageSlot - nextPageSlot);
334 if ((delta == 1) || (delta == xIcons)) {
335 const float fullBorder = frameBorder + iconBorder;
336 const float fxp = (float)(prevPageSlot % xIcons);
337 const float fyp = (float)(prevPageSlot / xIcons);
338 const float fxn = (float)(nextPageSlot % xIcons);
339 const float fyn = (float)(nextPageSlot / xIcons);
340 const float fx = 0.5f * (fxp + fxn);
341 const float fy = 0.5f * (fyp + fyn);
342 xBpos = buttonBox.x1 + (fullBorder + (0.5 * xIconSize) + (fx * xIconStep));
343 yBpos = buttonBox.y2 - (fullBorder + (0.5 * yIconSize) + (fy * yIconStep));
344 }
345 else {
346 xBpos = yBpos = -1.0f; // off screen
347 }
348 }
349 else {
350 xBpos = yBpos = -1.0f; // off screen
351 }
352
353 ParseFillOrder(fillOrderStr);
354
355 return true;
356 }
357
358
ParseFillOrder(const std::string & text)359 void CGuiHandler::ParseFillOrder(const std::string& text)
360 {
361 // setup the default order
362 fillOrder.clear();
363 for (int i = 0; i < iconsPerPage; i++) {
364 fillOrder.push_back(i);
365 }
366
367 // split the std::string into slot names
368 const std::vector<std::string> &slotNames = CSimpleParser::Tokenize(text, 0);
369 if ((int)slotNames.size() != iconsPerPage) {
370 return;
371 }
372
373 std::set<int> slotSet;
374 std::vector<int> slotVec;
375 for (int s = 0; s < iconsPerPage; s++) {
376 const int slotNumber = ParseIconSlot(slotNames[s]);
377 if ((slotNumber < 0) || (slotNumber >= iconsPerPage) ||
378 (slotSet.find(slotNumber) != slotSet.end())) {
379 return;
380 }
381 slotSet.insert(slotNumber);
382 slotVec.push_back(slotNumber);
383 }
384
385 fillOrder = slotVec;
386 }
387
388
ParseIconSlot(const std::string & text) const389 int CGuiHandler::ParseIconSlot(const std::string& text) const
390 {
391 const char rowLetter = tolower(text[0]);
392 if ((rowLetter < 'a') || (rowLetter > 'z')) {
393 return -1;
394 }
395 const int row = rowLetter - 'a';
396 if (row >= yIcons) {
397 return -1;
398 }
399
400 char* endPtr;
401 const char* startPtr = text.c_str() + 1;
402 int column = strtol(startPtr, &endPtr, 10);
403 if ((endPtr == startPtr) || (column < 0) || (column >= xIcons)) {
404 return -1;
405 }
406
407 return (row * xIcons) + column;
408 }
409
410
ReloadConfigFromFile(const std::string & fileName)411 bool CGuiHandler::ReloadConfigFromFile(const std::string& fileName)
412 {
413 CFileHandler ifs(fileName);
414 std::string cfg;
415 ifs.LoadStringData(cfg);
416 return ReloadConfigFromString(cfg);
417 }
418
419
ReloadConfigFromString(const std::string & cfg)420 bool CGuiHandler::ReloadConfigFromString(const std::string& cfg)
421 {
422 LoadConfig(cfg);
423 activePage = 0;
424 selectedUnitsHandler.SetCommandPage(activePage);
425 LayoutIcons(false);
426 return true;
427 }
428
429
ResizeIconArray(unsigned int size)430 void CGuiHandler::ResizeIconArray(unsigned int size)
431 {
432 unsigned int minIconsSize = iconsSize;
433 while (minIconsSize < size) {
434 minIconsSize *= 2;
435 }
436 if (iconsSize < minIconsSize) {
437 iconsSize = minIconsSize;
438 delete[] icons;
439 icons = NULL; // to prevent a dead-pointer in case of an out-of-memory exception on the next line
440 icons = new IconInfo[iconsSize];
441 }
442 }
443
444
AppendPrevAndNext(std::vector<CommandDescription> & cmds)445 void CGuiHandler::AppendPrevAndNext(std::vector<CommandDescription>& cmds)
446 {
447 CommandDescription cd;
448
449 cd.id=CMD_INTERNAL;
450 cd.action="prevmenu";
451 cd.type=CMDTYPE_PREV;
452 cd.name="";
453 cd.tooltip = "Previous menu";
454 cmds.push_back(cd);
455
456 cd.id=CMD_INTERNAL;
457 cd.action="nextmenu";
458 cd.type=CMDTYPE_NEXT;
459 cd.name="";
460 cd.tooltip = "Next menu";
461 cmds.push_back(cd);
462 }
463
464
FindInCommandPage()465 int CGuiHandler::FindInCommandPage()
466 {
467 if ((inCommand < 0) || ((size_t)inCommand >= commands.size())) {
468 return -1;
469 }
470 for (int ii = 0; ii < iconsCount; ii++) {
471 const IconInfo& icon = icons[ii];
472 if (icon.commandsID == inCommand) {
473 return (ii / iconsPerPage);
474 }
475 }
476 return -1;
477 }
478
479
RevertToCmdDesc(const CommandDescription & cmdDesc,bool defaultCommand,bool samePage)480 void CGuiHandler::RevertToCmdDesc(const CommandDescription& cmdDesc,
481 bool defaultCommand, bool samePage)
482 {
483 for (size_t a = 0; a < commands.size(); ++a) {
484 if (commands[a].id == cmdDesc.id) {
485 if (defaultCommand) {
486 defaultCmdMemory = a;
487 return;
488 }
489 inCommand = a;
490 if (commands[a].type == CMDTYPE_ICON_BUILDING) {
491 const UnitDef* ud = unitDefHandler->GetUnitDefByID(-commands[a].id);
492 SetShowingMetal(ud->extractsMetal > 0);
493 } else {
494 SetShowingMetal(false);
495 }
496 if (samePage) {
497 for (int ii = 0; ii < iconsCount; ii++) {
498 if (inCommand == icons[ii].commandsID) {
499 activePage = std::min(maxPage, (ii / iconsPerPage));;
500 selectedUnitsHandler.SetCommandPage(activePage);
501 }
502 }
503 }
504 return;
505 }
506 }
507 }
508
509
LayoutIcons(bool useSelectionPage)510 void CGuiHandler::LayoutIcons(bool useSelectionPage)
511 {
512 bool defCmd, validInCommand, samePage;
513 CommandDescription cmdDesc;
514 {
515 defCmd =
516 (mouse->buttons[SDL_BUTTON_RIGHT].pressed &&
517 (defaultCmdMemory >= 0) && (inCommand < 0) &&
518 ((activeReceiver == this) || (minimap->ProxyMode())));
519
520 const int activeCmd = defCmd ? defaultCmdMemory : inCommand;
521
522 // copy the current command state
523 validInCommand = (activeCmd >= 0) && ((size_t)activeCmd < commands.size());
524 if (validInCommand) {
525 cmdDesc = commands[activeCmd];
526 }
527 samePage = validInCommand && (activePage == FindInCommandPage());
528 useSelectionPage = useSelectionPage && !samePage;
529
530 // reset some of our state
531 inCommand = -1;
532 defaultCmdMemory = -1;
533 commands.clear();
534 forceLayoutUpdate = false;
535 }
536
537 if (luaUI && luaUI->WantsEvent("LayoutButtons")) {
538 if (LayoutCustomIcons(useSelectionPage)) {
539 if (validInCommand) {
540 RevertToCmdDesc(cmdDesc, defCmd, samePage);
541 }
542 return; // we're done here
543 }
544 else {
545 CLuaUI::FreeHandler();
546 }
547 }
548
549 // get the commands to process
550 CSelectedUnitsHandler::AvailableCommandsStruct ac;
551 ac = selectedUnitsHandler.GetAvailableCommands();
552 ConvertCommands(ac.commands);
553
554 std::vector<CommandDescription> hidden;
555 std::vector<CommandDescription>::const_iterator cdi;
556
557 // separate the visible/hidden icons
558 for (cdi = ac.commands.begin(); cdi != ac.commands.end(); ++cdi){
559 if (cdi->hidden) {
560 hidden.push_back(*cdi);
561 } else {
562 commands.push_back(*cdi);
563 }
564 }
565
566 // assign extra icons for internal commands
567 int extraIcons = 0;
568 if (deadIconSlot >= 0) { extraIcons++; }
569 if (prevPageSlot >= 0) { extraIcons++; }
570 if (nextPageSlot >= 0) { extraIcons++; }
571
572 const int cmdCount = (int)commands.size();
573 const int cmdIconsPerPage = (iconsPerPage - extraIcons);
574 const int pageCount = ((cmdCount + (cmdIconsPerPage - 1)) / cmdIconsPerPage);
575
576 const bool multiPage = (pageCount > 1);
577
578 const int prevPageCmd = cmdCount + 0;
579 const int nextPageCmd = cmdCount + 1;
580
581 maxPage = std::max(0, pageCount - 1);
582 iconsCount = pageCount * iconsPerPage;
583
584 // resize the icon array if required
585 ResizeIconArray(iconsCount);
586
587 int ci = 0; // command index
588
589 for (int ii = 0; ii < iconsCount; ii++) {
590
591 // map the icon order
592 const int tmpSlot = (ii % iconsPerPage);
593 const int mii = ii - tmpSlot + fillOrder[tmpSlot]; // mapped icon index
594
595 IconInfo& icon = icons[mii];
596 const int slot = (mii % iconsPerPage);
597
598 if (slot == deadIconSlot) {
599 icon.commandsID = -1;
600 }
601 else if (slot == nextPageSlot) {
602 icon.commandsID = multiPage ? nextPageCmd : -1;
603 }
604 else if (slot == prevPageSlot) {
605 icon.commandsID = multiPage ? prevPageCmd : -1;
606 }
607 else if (ci >= cmdCount) {
608 icon.commandsID = -1;
609 }
610 else {
611 icon.commandsID = ci;
612 ci++;
613 }
614
615 // setup the geometry
616 if (icon.commandsID >= 0) {
617 const float fx = (float)(slot % xIcons);
618 const float fy = (float)(slot / xIcons);
619
620 const float fullBorder = frameBorder + iconBorder;
621 icon.visual.x1 = buttonBox.x1 + (fullBorder + (fx * xIconStep));
622 icon.visual.x2 = icon.visual.x1 + xIconSize;
623 icon.visual.y1 = buttonBox.y2 - (fullBorder + (fy * yIconStep));
624 icon.visual.y2 = icon.visual.y1 - yIconSize;
625
626 const float noGap = selectGaps ? 0.0f : (iconBorder + 0.0005f);
627 icon.selection.x1 = icon.visual.x1 - noGap;
628 icon.selection.x2 = icon.visual.x2 + noGap;
629 icon.selection.y1 = icon.visual.y1 + noGap;
630 icon.selection.y2 = icon.visual.y2 - noGap;
631 }
632 else {
633 // make sure this icon does not get selected
634 icon.selection.x1 = icon.selection.x2 = -1.0f;
635 icon.selection.y1 = icon.selection.y2 = -1.0f;
636 }
637 }
638
639 // append the Prev and Next commands (if required)
640 if (multiPage) {
641 AppendPrevAndNext(commands);
642 }
643
644 // append the hidden commands
645 for (cdi = hidden.begin(); cdi != hidden.end(); ++cdi) {
646 commands.push_back(*cdi);
647 }
648
649 // try to setup the old command state
650 // (inCommand, activePage, showingMetal)
651 if (validInCommand) {
652 RevertToCmdDesc(cmdDesc, defCmd, samePage);
653 } else if (useSelectionPage) {
654 activePage = std::min(maxPage, ac.commandPage);
655 }
656 activePage = std::min(maxPage, activePage);
657 }
658
659
LayoutCustomIcons(bool useSelectionPage)660 bool CGuiHandler::LayoutCustomIcons(bool useSelectionPage)
661 {
662 if (luaUI == NULL) {
663 return false;
664 }
665
666 // get the commands to process
667 CSelectedUnitsHandler::AvailableCommandsStruct ac;
668 ac = selectedUnitsHandler.GetAvailableCommands();
669 std::vector<CommandDescription> cmds = ac.commands;
670 if (!cmds.empty()) {
671 ConvertCommands(cmds);
672 AppendPrevAndNext(cmds);
673 }
674
675 // call for a custom layout
676 int tmpXicons = xIcons;
677 int tmpYicons = yIcons;
678 std::vector<int> removeCmds;
679 std::vector<CommandDescription> customCmds;
680 std::vector<int> onlyTextureCmds;
681 std::vector<CLuaUI::ReStringPair> reTextureCmds;
682 std::vector<CLuaUI::ReStringPair> reNamedCmds;
683 std::vector<CLuaUI::ReStringPair> reTooltipCmds;
684 std::vector<CLuaUI::ReParamsPair> reParamsCmds;
685 std::map<int, int> iconMap;
686
687 if (!luaUI->LayoutButtons(tmpXicons, tmpYicons, cmds,
688 removeCmds, customCmds,
689 onlyTextureCmds, reTextureCmds,
690 reNamedCmds, reTooltipCmds, reParamsCmds,
691 iconMap, menuName)) {
692 return false;
693 }
694
695 if ((tmpXicons < 2) || (tmpYicons < 2)) {
696 LOG_L(L_ERROR, "LayoutCustomIcons() bad xIcons or yIcons (%i, %i)",
697 tmpXicons, tmpYicons);
698 return false;
699 }
700
701 unsigned int i;
702 const int tmpIconsPerPage = (tmpXicons * tmpYicons);
703
704 // build a set to better find unwanted commands
705 set<int> removeIDs;
706 for (i = 0; i < removeCmds.size(); i++) {
707 const int index = removeCmds[i];
708 if ((index >= 0) || ((size_t)index < cmds.size())) {
709 removeIDs.insert(index);
710 } else {
711 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad removeCmd (%i)",
712 index);
713 }
714 }
715 // remove unwanted commands (and mark all as hidden)
716 std::vector<CommandDescription> tmpCmds;
717 for (i = 0; i < cmds.size(); i++) {
718 if (removeIDs.find(i) == removeIDs.end()) {
719 cmds[i].hidden = true;
720 tmpCmds.push_back(cmds[i]);
721 }
722 }
723 cmds = tmpCmds;
724
725 // add the custom commands
726 for (i = 0; i < customCmds.size(); i++) {
727 customCmds[i].hidden = true;
728 cmds.push_back(customCmds[i]);
729 }
730 const int cmdCount = (int)cmds.size();
731
732 // set commands to onlyTexture
733 for (i = 0; i < onlyTextureCmds.size(); i++) {
734 const int index = onlyTextureCmds[i];
735 if ((index >= 0) && (index < cmdCount)) {
736 cmds[index].onlyTexture = true;
737 } else {
738 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad onlyTexture (%i)",
739 index);
740 }
741 }
742
743 // retexture commands
744 for (i = 0; i < reTextureCmds.size(); i++) {
745 const int index = reTextureCmds[i].cmdIndex;
746 if ((index >= 0) && (index < cmdCount)) {
747 cmds[index].iconname = reTextureCmds[i].texture;
748 } else {
749 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad reTexture (%i)",
750 index);
751 }
752 }
753
754 // reNamed commands
755 for (i = 0; i < reNamedCmds.size(); i++) {
756 const int index = reNamedCmds[i].cmdIndex;
757 if ((index >= 0) && (index < cmdCount)) {
758 cmds[index].name = reNamedCmds[i].texture;
759 } else {
760 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad reNamed (%i)",
761 index);
762 }
763 }
764
765 // reTooltip commands
766 for (i = 0; i < reTooltipCmds.size(); i++) {
767 const int index = reTooltipCmds[i].cmdIndex;
768 if ((index >= 0) && (index < cmdCount)) {
769 cmds[index].tooltip = reTooltipCmds[i].texture;
770 } else {
771 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad reNamed (%i)",
772 index);
773 }
774 }
775
776 // reParams commands
777 for (i = 0; i < reParamsCmds.size(); i++) {
778 const int index = reParamsCmds[i].cmdIndex;
779 if ((index >= 0) && (index < cmdCount)) {
780 const map<int, std::string>& params = reParamsCmds[i].params;
781 map<int, std::string>::const_iterator pit;
782 for (pit = params.begin(); pit != params.end(); ++pit) {
783 const int p = pit->first;
784 if ((p >= 0) && (p < (int)cmds[index].params.size())) {
785 cmds[index].params[p] = pit->second;
786 }
787 }
788 } else {
789 LOG_L(L_ERROR, "LayoutCustomIcons() skipping bad reParams (%i)",
790 index);
791 }
792 }
793
794 // build the iconList from the map
795 std::vector<int> iconList;
796 int nextPos = 0;
797 map<int, int>::iterator mit;
798 for (mit = iconMap.begin(); mit != iconMap.end(); ++mit) {
799 const int iconPos = mit->first;
800 if (iconPos < nextPos) {
801 continue;
802 }
803 else if (iconPos > nextPos) {
804 // fill in the blanks
805 for (int p = nextPos; p < iconPos; p++) {
806 iconList.push_back(-1);
807 }
808 }
809 iconList.push_back(mit->second); // cmdIndex
810 nextPos = iconPos + 1;
811 }
812
813 const int iconListCount = (int)iconList.size();
814 const int pageCount = ((iconListCount + (tmpIconsPerPage - 1)) / tmpIconsPerPage);
815 const int tmpIconsCount = (pageCount * tmpIconsPerPage);
816
817 // resize the icon array if required
818 ResizeIconArray(tmpIconsCount);
819
820 // build the iconList
821 for (int ii = 0; ii < tmpIconsCount; ii++) {
822 IconInfo& icon = icons[ii];
823
824 const int index = (ii < (int)iconList.size()) ? iconList[ii] : -1;
825
826 if ((index >= 0) && (index < cmdCount)) {
827
828 icon.commandsID = index;
829 cmds[index].hidden = false;
830
831 const int slot = (ii % tmpIconsPerPage);
832 const float fx = (float)(slot % tmpXicons);
833 const float fy = (float)(slot / tmpXicons);
834
835 const float fullBorder = frameBorder + iconBorder;
836 icon.visual.x1 = buttonBox.x1 + (fullBorder + (fx * xIconStep));
837 icon.visual.x2 = icon.visual.x1 + xIconSize;
838 icon.visual.y1 = buttonBox.y2 - (fullBorder + (fy * yIconStep));
839 icon.visual.y2 = icon.visual.y1 - yIconSize;
840
841 const float noGap = selectGaps ? 0.0f : (iconBorder + 0.0005f);
842 icon.selection.x1 = icon.visual.x1 - noGap;
843 icon.selection.x2 = icon.visual.x2 + noGap;
844 icon.selection.y1 = icon.visual.y1 + noGap;
845 icon.selection.y2 = icon.visual.y2 - noGap;
846 }
847 else {
848 // make sure this icon does not get selected
849 icon.commandsID = -1;
850 icon.selection.x1 = icon.selection.x2 = -1.0f;
851 icon.selection.y1 = icon.selection.y2 = -1.0f;
852 }
853 }
854
855 commands = cmds;
856
857 xIcons = tmpXicons;
858 yIcons = tmpYicons;
859 iconsCount = tmpIconsCount;
860 iconsPerPage = tmpIconsPerPage;
861
862 maxPage = std::max(0, pageCount - 1);
863 if (useSelectionPage) {
864 activePage = std::min(maxPage, ac.commandPage);
865 } else {
866 activePage = std::min(maxPage, activePage);
867 }
868
869 buttonBox.x1 = xPos;
870 buttonBox.x2 = xPos + (frameBorder * 2.0f) + (xIcons * xIconStep);
871 buttonBox.y1 = yPos;
872 buttonBox.y2 = yPos + (frameBorder * 2.0f) + (yIcons * yIconStep);
873
874 return true;
875 }
876
877
GiveCommand(Command & cmd,bool fromUser)878 void CGuiHandler::GiveCommand(Command& cmd, bool fromUser)
879 {
880 commandsToGive.push_back(std::pair<const Command, bool>(cmd, fromUser));
881 }
882
883
GiveCommandsNow()884 void CGuiHandler::GiveCommandsNow() {
885 std::vector< std::pair<Command, bool> > commandsToGiveTemp;
886 {
887 commandsToGiveTemp.swap(commandsToGive);
888 }
889
890 for(std::vector< std::pair<Command, bool> >::iterator i = commandsToGiveTemp.begin(); i != commandsToGiveTemp.end(); ++i) {
891 const Command& cmd = (*i).first;
892 if (eventHandler.CommandNotify(cmd)) {
893 continue;
894 }
895
896 selectedUnitsHandler.GiveCommand(cmd, (*i).second);
897
898 if (gatherMode) {
899 if ((cmd.GetID() == CMD_MOVE) || (cmd.GetID() == CMD_FIGHT)) {
900 Command gatherCmd(CMD_GATHERWAIT);
901 GiveCommand(gatherCmd, false);
902 }
903 }
904 }
905 }
906
907
ConvertCommands(std::vector<CommandDescription> & cmds)908 void CGuiHandler::ConvertCommands(std::vector<CommandDescription>& cmds)
909 {
910 if (newAttackMode) {
911 const int count = (int)cmds.size();
912 for (int i = 0; i < count; i++) {
913 CommandDescription& cd = cmds[i];
914 if ((cd.id == CMD_ATTACK) && (cd.type == CMDTYPE_ICON_UNIT_OR_MAP)) {
915 if (attackRect) {
916 cd.type = CMDTYPE_ICON_UNIT_OR_RECTANGLE;
917 } else {
918 cd.type = CMDTYPE_ICON_UNIT_OR_AREA;
919 }
920 }
921 }
922 }
923 }
924
925
SetShowingMetal(bool show)926 void CGuiHandler::SetShowingMetal(bool show)
927 {
928 CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
929
930 if (!show) {
931 if (showingMetal) {
932 gd->DisableExtraTexture();
933 showingMetal = false;
934 }
935 } else {
936 if (autoShowMetal) {
937 if (gd->GetDrawMode() != CBaseGroundDrawer::drawMetal) {
938 gd->SetMetalTexture();
939 showingMetal = true;
940 }
941 }
942 }
943 }
944
945
Update()946 void CGuiHandler::Update()
947 {
948 SetCursorIcon();
949
950 {
951 if (!invertQueueKey && (needShift && !KeyInput::GetKeyModState(KMOD_SHIFT))) {
952 SetShowingMetal(false);
953 inCommand=-1;
954 needShift=false;
955 }
956 }
957
958 GiveCommandsNow();
959
960 const bool commandsChanged = selectedUnitsHandler.CommandsChanged();
961
962 if (commandsChanged) {
963 SetShowingMetal(false);
964 LayoutIcons(true);
965 }
966 else if (forceLayoutUpdate) {
967 LayoutIcons(false);
968 }
969 }
970
971
972 /******************************************************************************/
973 /******************************************************************************/
974
SetCursorIcon() const975 void CGuiHandler::SetCursorIcon() const
976 {
977 string newCursor = "cursornormal";
978 float cursorScale = 1.0f;
979
980 CInputReceiver* ir = NULL;
981 if (!game->hideInterface)
982 ir = GetReceiverAt(mouse->lastx, mouse->lasty);
983
984 if ((ir != NULL) && (ir != minimap)) {
985 mouse->ChangeCursor(newCursor, cursorScale);
986 return;
987 }
988
989 if (ir == minimap)
990 cursorScale = minimap->CursorScale();
991
992 const bool useMinimap = (minimap->ProxyMode() || ((activeReceiver != this) && (ir == minimap)));
993
994 if ((inCommand >= 0) && ((size_t)inCommand<commands.size())) {
995 const CommandDescription& cmdDesc = commands[inCommand];
996
997 if (!cmdDesc.mouseicon.empty()) {
998 newCursor=cmdDesc.mouseicon;
999 } else {
1000 newCursor=cmdDesc.name;
1001 }
1002
1003 if (useMinimap && (cmdDesc.id < 0)) {
1004 BuildInfo bi;
1005 bi.pos = minimap->GetMapPosition(mouse->lastx, mouse->lasty);
1006 bi.buildFacing = buildFacing;
1007 bi.def = unitDefHandler->GetUnitDefByID(-cmdDesc.id);
1008 bi.pos = CGameHelper::Pos2BuildPos(bi, false);
1009 // if an unit (enemy), is not in LOS, then TestUnitBuildSquare()
1010 // does not consider it when checking for position blocking
1011 CFeature* feature = NULL;
1012 if (!CGameHelper::TestUnitBuildSquare(bi, feature, gu->myAllyTeam, false)) {
1013 newCursor = "BuildBad";
1014 } else {
1015 newCursor = "BuildGood";
1016 }
1017 }
1018
1019 if (!TryTarget(cmdDesc)) {
1020 newCursor = "AttackBad";
1021 }
1022 }
1023 else if (!useMinimap || minimap->FullProxy()) {
1024 int defcmd;
1025 if (mouse->buttons[SDL_BUTTON_RIGHT].pressed &&
1026 ((activeReceiver == this) || (minimap->ProxyMode()))) {
1027 defcmd = defaultCmdMemory;
1028 } else {
1029 defcmd = GetDefaultCommand(mouse->lastx, mouse->lasty);
1030 }
1031 if ((defcmd >= 0) && ((size_t)defcmd < commands.size())) {
1032 const CommandDescription& cmdDesc = commands[defcmd];
1033 if (!cmdDesc.mouseicon.empty()) {
1034 newCursor=cmdDesc.mouseicon;
1035 } else {
1036 newCursor=cmdDesc.name;
1037 }
1038 }
1039 }
1040
1041 if (gatherMode &&
1042 ((newCursor == "Move") ||
1043 (newCursor == "Fight"))) {
1044 newCursor = "GatherWait";
1045 }
1046
1047 mouse->ChangeCursor(newCursor, cursorScale);
1048 }
1049
1050
TryTarget(const CommandDescription & cmdDesc) const1051 bool CGuiHandler::TryTarget(const CommandDescription& cmdDesc) const
1052 {
1053 if (cmdDesc.id != CMD_ATTACK)
1054 return true;
1055
1056 if (selectedUnitsHandler.selectedUnits.empty())
1057 return true;
1058
1059 // get mouse-hovered map pos
1060 CUnit* targetUnit = NULL;
1061 CFeature* targetFeature = NULL;
1062
1063 const float viewRange = globalRendering->viewRange * 1.4f;
1064 const float dist = TraceRay::GuiTraceRay(camera->GetPos(), mouse->dir, viewRange, NULL, targetUnit, targetFeature, true);
1065 const float3 groundPos = camera->GetPos() + mouse->dir * dist;
1066
1067 float3 modGroundPos;
1068
1069 if (dist <= 0.0f)
1070 return false;
1071
1072 for (const CUnit* u: selectedUnitsHandler.selectedUnits) {
1073 // mobile kamikaze can always move into range
1074 //FIXME do a range check in case of immobile kamikaze (-> mines)
1075 if (u->unitDef->canKamikaze && !u->immobile)
1076 return true;
1077
1078 for (const CWeapon* w: u->weapons) {
1079 w->AdjustTargetPosToWater(modGroundPos = groundPos, targetUnit == NULL);
1080
1081 if (u->immobile) {
1082 // immobile unit
1083 // check range and weapon target properties
1084 if (w->TryTarget(modGroundPos, false, targetUnit)) {
1085 return true;
1086 }
1087 } else {
1088 // mobile units can always move into range
1089 // only check if we got a weapon that can shot the target (i.e. anti-air/anti-sub)
1090 if (w->TestTarget(modGroundPos, false, targetUnit)) {
1091 return true;
1092 }
1093 }
1094 }
1095 }
1096
1097 return false;
1098 }
1099
1100
MousePress(int x,int y,int button)1101 bool CGuiHandler::MousePress(int x, int y, int button)
1102 {
1103 {
1104 if (button != SDL_BUTTON_LEFT && button != SDL_BUTTON_RIGHT && button != -SDL_BUTTON_RIGHT && button != -SDL_BUTTON_LEFT)
1105 return false;
1106
1107 if (button < 0) {
1108 // proxied click from the minimap
1109 button = -button;
1110 activeMousePress=true;
1111 }
1112 else if (AboveGui(x,y)) {
1113 activeMousePress = true;
1114 if ((curIconCommand < 0) && !game->hideInterface) {
1115 const int iconPos = IconAtPos(x, y);
1116 if (iconPos >= 0) {
1117 curIconCommand = icons[iconPos].commandsID;
1118 }
1119 }
1120 if (button == SDL_BUTTON_RIGHT)
1121 inCommand = defaultCmdMemory = -1;
1122 return true;
1123 }
1124 else if (minimap && minimap->IsAbove(x, y)) {
1125 return false; // let the minimap do its job
1126 }
1127
1128 if (inCommand >= 0) {
1129 if (invertQueueKey && (button == SDL_BUTTON_RIGHT) &&
1130 !mouse->buttons[SDL_BUTTON_LEFT].pressed) { // for rocker gestures
1131 SetShowingMetal(false);
1132 inCommand = -1;
1133 needShift = false;
1134 return false;
1135 }
1136 activeMousePress = true;
1137 return true;
1138 }
1139 }
1140 if (button == SDL_BUTTON_RIGHT) {
1141 activeMousePress = true;
1142 defaultCmdMemory = GetDefaultCommand(x, y);
1143 return true;
1144 }
1145
1146 return false;
1147 }
1148
1149
MouseRelease(int x,int y,int button,const float3 & cameraPos,const float3 & mouseDir)1150 void CGuiHandler::MouseRelease(int x, int y, int button, const float3& cameraPos, const float3& mouseDir)
1151 {
1152 if (button != SDL_BUTTON_LEFT && button != SDL_BUTTON_RIGHT && button != -SDL_BUTTON_RIGHT && button != -SDL_BUTTON_LEFT)
1153 return;
1154
1155 int lastIconCmd = curIconCommand;
1156 curIconCommand = -1;
1157
1158 int iconCmd = -1;
1159 explicitCommand = inCommand;
1160
1161 if (activeMousePress) {
1162 activeMousePress = false;
1163 } else {
1164 return;
1165 }
1166
1167 if (!invertQueueKey && needShift && !KeyInput::GetKeyModState(KMOD_SHIFT)) {
1168 SetShowingMetal(false);
1169 inCommand = -1;
1170 needShift = false;
1171 }
1172
1173 if (button < 0) {
1174 button = -button; // proxied click from the minimap
1175 } else {
1176 // setup iconCmd
1177 if (!game->hideInterface) {
1178 const int iconPos = IconAtPos(x, y);
1179 if (iconPos >= 0) {
1180 iconCmd = icons[iconPos].commandsID;
1181 if(iconCmd != lastIconCmd)
1182 iconCmd = -1; // mouse was pressed on one button and released on another one --> ignore the command
1183 }
1184 }
1185 }
1186
1187 if ((button == SDL_BUTTON_RIGHT) && (iconCmd == -1)) {
1188 // right click -> set the default cmd
1189 inCommand = defaultCmdMemory;
1190 defaultCmdMemory = -1;
1191 }
1192
1193 if ((iconCmd >= 0) && ((size_t)iconCmd < commands.size())) {
1194 const bool rightMouseButton = (button == SDL_BUTTON_RIGHT);
1195 SetActiveCommand(iconCmd, rightMouseButton);
1196 return;
1197 }
1198
1199 // not over a button, try to execute a command
1200 Command c = GetCommand(x, y, button, false, cameraPos, mouseDir);
1201
1202 if (c.GetID() == CMD_FAILED) { // indicates we should not finish the current command
1203 Channels::UserInterface->PlaySample(failedSound, 5);
1204 return;
1205 }
1206
1207 // if cmd_stop is returned it indicates that no good command could be found
1208 if (c.GetID() != CMD_STOP) {
1209 GiveCommand(c);
1210
1211 lastKeySet.Reset();
1212 }
1213
1214 FinishCommand(button);
1215 }
1216
1217
SetActiveCommand(int cmdIndex,bool rightMouseButton)1218 bool CGuiHandler::SetActiveCommand(int cmdIndex, bool rightMouseButton)
1219 {
1220 if (cmdIndex >= (int)commands.size()) {
1221 return false;
1222 }
1223 else if (cmdIndex < 0) {
1224 // cancel the current command
1225 inCommand = -1;
1226 defaultCmdMemory = -1;
1227 needShift = false;
1228 activeMousePress = false;
1229 SetShowingMetal(false);
1230 return true;
1231 }
1232
1233 CommandDescription& cd = commands[cmdIndex];
1234 if (cd.disabled) {
1235 return false;
1236 }
1237
1238 lastKeySet.Reset();
1239
1240 switch (cd.type) {
1241 case CMDTYPE_ICON: {
1242 Command c(cd.id);
1243 if (cd.id != CMD_STOP) {
1244 c.options = CreateOptions(rightMouseButton);
1245 if (invertQueueKey && ((cd.id < 0) || (cd.id == CMD_STOCKPILE))) {
1246 c.options = c.options ^ SHIFT_KEY;
1247 }
1248 }
1249 GiveCommand(c);
1250 break;
1251 }
1252 case CMDTYPE_ICON_MODE: {
1253 int newMode = atoi(cd.params[0].c_str()) + 1;
1254 if (newMode > (static_cast<int>(cd.params.size())-2)) {
1255 newMode = 0;
1256 }
1257
1258 // not really required
1259 char t[10];
1260 SNPRINTF(t, 10, "%d", newMode);
1261 cd.params[0] = t;
1262
1263 Command c(cd.id, CreateOptions(rightMouseButton), newMode);
1264 GiveCommand(c);
1265 forceLayoutUpdate = true;
1266 break;
1267 }
1268 case CMDTYPE_NUMBER:
1269 case CMDTYPE_ICON_MAP:
1270 case CMDTYPE_ICON_AREA:
1271 case CMDTYPE_ICON_UNIT:
1272 case CMDTYPE_ICON_UNIT_OR_MAP:
1273 case CMDTYPE_ICON_FRONT:
1274 case CMDTYPE_ICON_UNIT_OR_AREA:
1275 case CMDTYPE_ICON_UNIT_OR_RECTANGLE:
1276 case CMDTYPE_ICON_UNIT_FEATURE_OR_AREA: {
1277 inCommand = cmdIndex;
1278 SetShowingMetal(false);
1279 activeMousePress = false;
1280 break;
1281 }
1282 case CMDTYPE_ICON_BUILDING: {
1283 const UnitDef* ud = unitDefHandler->GetUnitDefByID(-cd.id);
1284 inCommand = cmdIndex;
1285 SetShowingMetal(ud->extractsMetal > 0);
1286 activeMousePress = false;
1287 break;
1288 }
1289 case CMDTYPE_NEXT: {
1290 ++activePage;
1291 if (activePage > maxPage) {
1292 activePage = 0;
1293 }
1294 selectedUnitsHandler.SetCommandPage(activePage);
1295 break;
1296 }
1297 case CMDTYPE_PREV: {
1298 --activePage;
1299 if (activePage < 0) {
1300 activePage=maxPage;
1301 }
1302 selectedUnitsHandler.SetCommandPage(activePage);
1303 break;
1304 }
1305 case CMDTYPE_CUSTOM: {
1306 RunCustomCommands(cd.params, rightMouseButton);
1307 break;
1308 }
1309 default:
1310 break;
1311 }
1312
1313 return true;
1314 }
1315
1316
SetActiveCommand(int cmdIndex,int button,bool leftMouseButton,bool rightMouseButton,bool alt,bool ctrl,bool meta,bool shift)1317 bool CGuiHandler::SetActiveCommand(int cmdIndex, int button,
1318 bool leftMouseButton, bool rightMouseButton,
1319 bool alt, bool ctrl, bool meta, bool shift)
1320 {
1321 // use the button value instead of rightMouseButton
1322 const bool effectiveRMB = (button == SDL_BUTTON_LEFT) ? false : true;
1323
1324 // setup the mouse and key states
1325 const bool prevLMB = mouse->buttons[SDL_BUTTON_LEFT].pressed;
1326 const bool prevRMB = mouse->buttons[SDL_BUTTON_RIGHT].pressed;
1327 const boost::uint8_t prevAlt = KeyInput::GetKeyModState(KMOD_ALT);
1328 const boost::uint8_t prevCtrl = KeyInput::GetKeyModState(KMOD_CTRL);
1329 const boost::uint8_t prevMeta = KeyInput::GetKeyModState(KMOD_GUI);
1330 const boost::uint8_t prevShift = KeyInput::GetKeyModState(KMOD_SHIFT);
1331
1332 mouse->buttons[SDL_BUTTON_LEFT].pressed = leftMouseButton;
1333 mouse->buttons[SDL_BUTTON_RIGHT].pressed = rightMouseButton;
1334
1335 KeyInput::SetKeyModState(KMOD_ALT, alt);
1336 KeyInput::SetKeyModState(KMOD_CTRL, ctrl);
1337 KeyInput::SetKeyModState(KMOD_GUI, meta);
1338 KeyInput::SetKeyModState(KMOD_SHIFT, shift);
1339
1340 const bool retval = SetActiveCommand(cmdIndex, effectiveRMB);
1341
1342 // revert the mouse and key states
1343 KeyInput::SetKeyModState(KMOD_SHIFT, prevShift);
1344 KeyInput::SetKeyModState(KMOD_GUI, prevMeta);
1345 KeyInput::SetKeyModState(KMOD_CTRL, prevCtrl);
1346 KeyInput::SetKeyModState(KMOD_ALT, prevAlt);
1347
1348 mouse->buttons[SDL_BUTTON_RIGHT].pressed = prevRMB;
1349 mouse->buttons[SDL_BUTTON_LEFT].pressed = prevLMB;
1350
1351 return retval;
1352 }
1353
1354
IconAtPos(int x,int y)1355 int CGuiHandler::IconAtPos(int x, int y) // GetToolTip --> IconAtPos
1356 {
1357 const float fx = MouseX(x);
1358 const float fy = MouseY(y);
1359
1360 if ((fx < buttonBox.x1) || (fx > buttonBox.x2) ||
1361 (fy < buttonBox.y1) || (fy > buttonBox.y2)) {
1362 return -1;
1363 }
1364
1365 int xSlot = int((fx - (buttonBox.x1 + frameBorder)) / xIconStep);
1366 int ySlot = int(((buttonBox.y2 - frameBorder) - fy) / yIconStep);
1367 xSlot = std::min(std::max(xSlot, 0), xIcons - 1);
1368 ySlot = std::min(std::max(ySlot, 0), yIcons - 1);
1369 const int ii = (activePage * iconsPerPage) + (ySlot * xIcons) + xSlot;
1370 if ((ii >= 0) && (ii < iconsCount)) {
1371 if ((fx > icons[ii].selection.x1) && (fx < icons[ii].selection.x2) &&
1372 (fy > icons[ii].selection.y2) && (fy < icons[ii].selection.y1)) {
1373 return ii;
1374 }
1375 }
1376
1377 return -1;
1378 }
1379
1380
1381 /******************************************************************************/
1382
1383 enum ModState {
1384 DontCare, Required, Forbidden
1385 };
1386
1387 struct ModGroup {
ModGroupModGroup1388 ModGroup()
1389 : alt(DontCare),
1390 ctrl(DontCare),
1391 meta(DontCare),
1392 shift(DontCare),
1393 right(DontCare) {}
1394 ModState alt, ctrl, meta, shift, right;
1395 };
1396
1397
ParseCustomCmdMods(std::string & cmd,ModGroup & in,ModGroup & out)1398 static bool ParseCustomCmdMods(std::string& cmd, ModGroup& in, ModGroup& out)
1399 {
1400 const char* c = cmd.c_str();
1401 if (*c != '@') {
1402 return false;
1403 }
1404 c++;
1405 bool neg = false;
1406 while ((*c != 0) && (*c != '@')) {
1407 char ch = *c;
1408 if (ch == '-') {
1409 neg = true;
1410 }
1411 else if (ch == '+') {
1412 neg = false;
1413 }
1414 else if (ch == 'a') { in.alt = neg ? Forbidden : Required; neg = false; }
1415 else if (ch == 'c') { in.ctrl = neg ? Forbidden : Required; neg = false; }
1416 else if (ch == 'm') { in.meta = neg ? Forbidden : Required; neg = false; }
1417 else if (ch == 's') { in.shift = neg ? Forbidden : Required; neg = false; }
1418 else if (ch == 'r') { in.right = neg ? Forbidden : Required; neg = false; }
1419 else if (ch == 'A') { out.alt = neg ? Forbidden : Required; neg = false; }
1420 else if (ch == 'C') { out.ctrl = neg ? Forbidden : Required; neg = false; }
1421 else if (ch == 'M') { out.meta = neg ? Forbidden : Required; neg = false; }
1422 else if (ch == 'S') { out.shift = neg ? Forbidden : Required; neg = false; }
1423 else if (ch == 'R') { out.right = neg ? Forbidden : Required; neg = false; }
1424
1425 c++;
1426 }
1427
1428 if (*c == 0) {
1429 return false;
1430 }
1431 cmd = cmd.substr((c + 1) - cmd.c_str());
1432
1433 return true;
1434 }
1435
1436
CheckCustomCmdMods(bool rightMouseButton,ModGroup & inMods)1437 static bool CheckCustomCmdMods(bool rightMouseButton, ModGroup& inMods)
1438 {
1439 if (((inMods.alt == Required) && !KeyInput::GetKeyModState(KMOD_ALT)) ||
1440 ((inMods.alt == Forbidden) && KeyInput::GetKeyModState(KMOD_ALT)) ||
1441 ((inMods.ctrl == Required) && !KeyInput::GetKeyModState(KMOD_CTRL)) ||
1442 ((inMods.ctrl == Forbidden) && KeyInput::GetKeyModState(KMOD_CTRL)) ||
1443 ((inMods.meta == Required) && !KeyInput::GetKeyModState(KMOD_GUI)) ||
1444 ((inMods.meta == Forbidden) && KeyInput::GetKeyModState(KMOD_GUI)) ||
1445 ((inMods.shift == Required) && !KeyInput::GetKeyModState(KMOD_SHIFT)) ||
1446 ((inMods.shift == Forbidden) && KeyInput::GetKeyModState(KMOD_SHIFT)) ||
1447 ((inMods.right == Required) && !rightMouseButton) ||
1448 ((inMods.right == Forbidden) && rightMouseButton)) {
1449 return false;
1450 }
1451 return true;
1452 }
1453
1454
RunCustomCommands(const std::vector<std::string> & cmds,bool rightMouseButton)1455 void CGuiHandler::RunCustomCommands(const std::vector<std::string>& cmds, bool rightMouseButton)
1456 {
1457 static int depth = 0;
1458 if (depth > 8) {
1459 return; // recursion protection
1460 }
1461 depth++;
1462
1463 for (int p = 0; p < (int)cmds.size(); p++) {
1464 std::string copy = cmds[p];
1465 ModGroup inMods; // must match for the action to execute
1466 ModGroup outMods; // controls the state of the modifiers (ex: "group1")
1467 if (ParseCustomCmdMods(copy, inMods, outMods)) {
1468 if (CheckCustomCmdMods(rightMouseButton, inMods)) {
1469 const bool tmpAlt = !!KeyInput::GetKeyModState(KMOD_ALT);
1470 const bool tmpCtrl = !!KeyInput::GetKeyModState(KMOD_CTRL);
1471 const bool tmpMeta = !!KeyInput::GetKeyModState(KMOD_GUI);
1472 const bool tmpShift = !!KeyInput::GetKeyModState(KMOD_SHIFT);
1473
1474 if (outMods.alt != DontCare) { KeyInput::SetKeyModState(KMOD_ALT, int(outMods.alt == Required)); }
1475 if (outMods.ctrl != DontCare) { KeyInput::SetKeyModState(KMOD_CTRL, int(outMods.ctrl == Required)); }
1476 if (outMods.meta != DontCare) { KeyInput::SetKeyModState(KMOD_GUI, int(outMods.meta == Required)); }
1477 if (outMods.shift != DontCare) { KeyInput::SetKeyModState(KMOD_SHIFT, int(outMods.shift == Required)); }
1478
1479 Action action(copy);
1480 if (!ProcessLocalActions(action)) {
1481 game->ProcessAction(action);
1482 }
1483
1484 KeyInput::SetKeyModState(KMOD_ALT, tmpAlt);
1485 KeyInput::SetKeyModState(KMOD_CTRL, tmpCtrl);
1486 KeyInput::SetKeyModState(KMOD_GUI, tmpMeta);
1487 KeyInput::SetKeyModState(KMOD_SHIFT, tmpShift);
1488 }
1489 }
1490 }
1491 depth--;
1492 }
1493
1494
AboveGui(int x,int y)1495 bool CGuiHandler::AboveGui(int x, int y)
1496 {
1497 if (iconsCount <= 0) {
1498 return false;
1499 }
1500 if (!selectThrough) {
1501 const float fx = MouseX(x);
1502 const float fy = MouseY(y);
1503 if ((fx > buttonBox.x1) && (fx < buttonBox.x2) &&
1504 (fy > buttonBox.y1) && (fy < buttonBox.y2)) {
1505 return true;
1506 }
1507 }
1508 return (IconAtPos(x,y) >= 0);
1509 }
1510
1511
CreateOptions(bool rightMouseButton)1512 unsigned char CGuiHandler::CreateOptions(bool rightMouseButton)
1513 {
1514 unsigned char options = 0;
1515
1516 if (rightMouseButton) {
1517 options |= RIGHT_MOUSE_KEY;
1518 }
1519 if (GetQueueKeystate()) {
1520 // allow mouse button 'rocker' movements to force
1521 // immediate mode (when queuing is the default mode)
1522 if (!invertQueueKey ||
1523 (!mouse->buttons[SDL_BUTTON_LEFT].pressed &&
1524 !mouse->buttons[SDL_BUTTON_RIGHT].pressed)) {
1525 options |= SHIFT_KEY;
1526 }
1527 }
1528 if (KeyInput::GetKeyModState(KMOD_CTRL)) { options |= CONTROL_KEY; }
1529 if (KeyInput::GetKeyModState(KMOD_ALT) ) { options |= ALT_KEY; }
1530 if (KeyInput::GetKeyModState(KMOD_GUI)) { options |= META_KEY; }
1531
1532 return options;
1533 }
CreateOptions(int button)1534 unsigned char CGuiHandler::CreateOptions(int button)
1535 {
1536 return CreateOptions(button != SDL_BUTTON_LEFT);
1537 }
1538
1539
GetNumberInput(const CommandDescription & cd) const1540 float CGuiHandler::GetNumberInput(const CommandDescription& cd) const
1541 {
1542 float minV = 0.0f;
1543 float maxV = 100.0f;
1544 if (!cd.params.empty()) { minV = atof(cd.params[0].c_str()); }
1545 if (cd.params.size() >= 2) { maxV = atof(cd.params[1].c_str()); }
1546 const int minX = (globalRendering->viewSizeX * 1) / 4;
1547 const int maxX = (globalRendering->viewSizeX * 3) / 4;
1548 const int effX = std::max(std::min(mouse->lastx, maxX), minX);
1549 const float factor = float(effX - minX) / float(maxX - minX);
1550
1551 return (minV + (factor * (maxV - minV)));
1552 }
1553
1554 // CALLINFO:
1555 // DrawMapStuff --> GetDefaultCommand
1556 // CMouseHandler::DrawCursor --> DrawCentroidCursor --> GetDefaultCommand
1557 // LuaUnsyncedRead::GetDefaultCommand --> GetDefaultCommand
GetDefaultCommand(int x,int y,const float3 & cameraPos,const float3 & mouseDir) const1558 int CGuiHandler::GetDefaultCommand(int x, int y, const float3& cameraPos, const float3& mouseDir) const
1559 {
1560 CInputReceiver* ir = NULL;
1561 if (!game->hideInterface) {
1562 ir = GetReceiverAt(x, y);
1563 }
1564
1565 if ((ir != NULL) && (ir != minimap)) {
1566 return -1;
1567 }
1568
1569 int cmdID = -1;
1570
1571 {
1572 CUnit* unit = NULL;
1573 CFeature* feature = NULL;
1574 if ((ir == minimap) && (minimap->FullProxy())) {
1575 unit = minimap->GetSelectUnit(minimap->GetMapPosition(x, y));
1576 }
1577 else {
1578 const float viewRange = globalRendering->viewRange * 1.4f;
1579 const float dist = TraceRay::GuiTraceRay(cameraPos, mouseDir, viewRange, NULL, unit, feature, true);
1580 const float3 hit = cameraPos + mouseDir * dist;
1581
1582 // make sure the ray hit in the map
1583 if (!unit && !feature && !hit.IsInBounds())
1584 return -1;
1585 }
1586
1587 cmdID = selectedUnitsHandler.GetDefaultCmd(unit, feature);
1588 }
1589
1590 // make sure the command is currently available
1591 for (int c = 0; c < (int)commands.size(); c++) {
1592 if (cmdID == commands[c].id) {
1593 return c;
1594 }
1595 }
1596 return -1;
1597 }
1598
1599
ProcessLocalActions(const Action & action)1600 bool CGuiHandler::ProcessLocalActions(const Action& action)
1601 {
1602 // do not process these actions if the control panel is not visible
1603 if (iconsCount <= 0) {
1604 return false;
1605 }
1606
1607 // only process the build options while building
1608 // (conserve the keybinding space where we can)
1609 if ((inCommand >= 0) && ((size_t)inCommand < commands.size()) &&
1610 ((commands[inCommand].type == CMDTYPE_ICON_BUILDING) ||
1611 (commands[inCommand].id == CMD_UNLOAD_UNITS))) {
1612 if (ProcessBuildActions(action)) {
1613 return true;
1614 }
1615 }
1616
1617 if (action.command == "buildiconsfirst") {
1618 activePage = 0;
1619 selectedUnitsHandler.SetCommandPage(activePage);
1620 selectedUnitsHandler.ToggleBuildIconsFirst();
1621 LayoutIcons(false);
1622 return true;
1623 }
1624 else if (action.command == "firstmenu") {
1625 activePage = 0;
1626 selectedUnitsHandler.SetCommandPage(activePage);
1627 return true;
1628 }
1629 else if (action.command == "showcommands") {
1630 // bonus command for debugging
1631 LOG("Available Commands:");
1632 for(size_t i = 0; i < commands.size(); ++i){
1633 LOG(" command: " _STPF_ ", id = %i, action = %s",
1634 i, commands[i].id, commands[i].action.c_str());
1635 }
1636 // show the icon/command linkage
1637 LOG("Icon Linkage:");
1638 for(int ii = 0; ii < iconsCount; ++ii){
1639 LOG(" icon: %i, commandsID = %i", ii, icons[ii].commandsID);
1640 }
1641 LOG("maxPage = %i", maxPage);
1642 LOG("activePage = %i", activePage);
1643 LOG("iconsSize = %u", iconsSize);
1644 LOG("iconsCount = %i", iconsCount);
1645 LOG("commands.size() = " _STPF_, commands.size());
1646 return true;
1647 }
1648 else if (action.command == "invqueuekey") {
1649 if (action.extra.empty()) {
1650 invertQueueKey = !invertQueueKey;
1651 } else {
1652 invertQueueKey = !!atoi(action.extra.c_str());
1653 }
1654 needShift = false;
1655 configHandler->Set("InvertQueueKey", invertQueueKey ? 1 : 0);
1656 return true;
1657 }
1658
1659 return false;
1660 }
1661
1662
ProcessBuildActions(const Action & action)1663 bool CGuiHandler::ProcessBuildActions(const Action& action)
1664 {
1665 const std::string& arg = StringToLower(action.extra);
1666 bool ret = false;
1667
1668 if (action.command == "buildspacing") {
1669 if (arg == "inc") {
1670 buildSpacing++;
1671 ret = true;
1672 }
1673 else if (arg == "dec") {
1674 if (buildSpacing > 0) {
1675 buildSpacing--;
1676 }
1677 ret = true;
1678 }
1679 }
1680 else if (action.command == "buildfacing") {
1681 static const char* buildFaceDirs[] = {"South", "East", "North", "West"};
1682
1683 if (arg == "inc") {
1684 buildFacing = (buildFacing + 1) % NUM_FACINGS;
1685 ret = true;
1686 }
1687 else if (arg == "dec") {
1688 buildFacing = (buildFacing + NUM_FACINGS - 1) % NUM_FACINGS;
1689 ret = true;
1690 }
1691 else if (arg == "south") {
1692 buildFacing = FACING_SOUTH;
1693 ret = true;
1694 }
1695 else if (arg == "east") {
1696 buildFacing = FACING_EAST;
1697 ret = true;
1698 }
1699 else if (arg == "north") {
1700 buildFacing = FACING_NORTH;
1701 ret = true;
1702 }
1703 else if (arg == "west") {
1704 buildFacing = FACING_WEST;
1705 ret = true;
1706 }
1707
1708 LOG("Buildings set to face %s", buildFaceDirs[buildFacing]);
1709 }
1710
1711 return ret;
1712 }
1713
1714
GetIconPosCommand(int slot) const1715 int CGuiHandler::GetIconPosCommand(int slot) const // only called by SetActiveCommand
1716 {
1717 if (slot < 0) {
1718 return -1;
1719 }
1720 const int iconPos = slot + (activePage * iconsPerPage);
1721 if (iconPos < iconsCount) {
1722 return icons[iconPos].commandsID;
1723 }
1724 return -1;
1725 }
1726
1727
KeyPressed(int key,bool isRepeat)1728 bool CGuiHandler::KeyPressed(int key, bool isRepeat)
1729 {
1730 if (key == SDLK_ESCAPE && activeMousePress) {
1731 activeMousePress = false;
1732 inCommand = -1;
1733 SetShowingMetal(false);
1734 return true;
1735 }
1736 if (key == SDLK_ESCAPE && inCommand >= 0) {
1737 inCommand=-1;
1738 SetShowingMetal(false);
1739 return true;
1740 }
1741
1742 const CKeySet ks(key, false);
1743
1744 // setup actionOffset
1745 //WTF a bit more documentation???
1746 int tmpActionOffset = actionOffset;
1747 if ((inCommand < 0) || (lastKeySet.Key() < 0)){
1748 actionOffset = 0;
1749 tmpActionOffset = 0;
1750 lastKeySet.Reset();
1751 }
1752 else if (!ks.IsPureModifier()) {
1753 // not a modifier
1754 if ((ks == lastKeySet) && (ks.Key() >= 0)) {
1755 actionOffset++;
1756 tmpActionOffset = actionOffset;
1757 } else {
1758 tmpActionOffset = 0;
1759 }
1760 }
1761
1762 const CKeyBindings::ActionList& al = keyBindings->GetActionList(ks);
1763 for (int ali = 0; ali < (int)al.size(); ++ali) {
1764 const int actionIndex = (ali + tmpActionOffset) % (int)al.size(); //????
1765 const Action& action = al[actionIndex];
1766 if (SetActiveCommand(action, ks, actionIndex)) {
1767 return true;
1768 }
1769 }
1770
1771 return false;
1772 }
1773
1774
SetActiveCommand(const Action & action,const CKeySet & ks,int actionIndex)1775 bool CGuiHandler::SetActiveCommand(const Action& action,
1776 const CKeySet& ks, int actionIndex)
1777 {
1778 if (ProcessLocalActions(action)) {
1779 return true;
1780 }
1781
1782 // See if we have a positional icon command
1783 int iconCmd = -1;
1784 if (!action.extra.empty() && (action.command == "iconpos")) {
1785 const int iconSlot = ParseIconSlot(action.extra);
1786 iconCmd = GetIconPosCommand(iconSlot);
1787 }
1788
1789 for (size_t a = 0; a < commands.size(); ++a) {
1790
1791 CommandDescription& cmdDesc = commands[a];
1792
1793 if ((static_cast<int>(a) != iconCmd) && (cmdDesc.action != action.command)) {
1794 continue; // not a match
1795 }
1796
1797 if (cmdDesc.disabled) {
1798 continue; // can not use this command
1799 }
1800
1801 const int cmdType = cmdDesc.type;
1802
1803 // set the activePage
1804 if (!cmdDesc.hidden &&
1805 (((cmdType == CMDTYPE_ICON) &&
1806 ((cmdDesc.id < 0) ||
1807 (cmdDesc.id == CMD_STOCKPILE))) ||
1808 (cmdType == CMDTYPE_ICON_MODE) ||
1809 (cmdType == CMDTYPE_ICON_BUILDING))) {
1810 for (int ii = 0; ii < iconsCount; ii++) {
1811 if (icons[ii].commandsID == static_cast<int>(a)) {
1812 activePage = std::min(maxPage, (ii / iconsPerPage));
1813 selectedUnitsHandler.SetCommandPage(activePage);
1814 }
1815 }
1816 }
1817
1818 switch (cmdType) {
1819 case CMDTYPE_ICON:{
1820 Command c(cmdDesc.id);
1821 if ((cmdDesc.id < 0) || (cmdDesc.id == CMD_STOCKPILE)) {
1822 if (action.extra == "+5") {
1823 c.options = SHIFT_KEY;
1824 } else if (action.extra == "+20") {
1825 c.options = CONTROL_KEY;
1826 } else if (action.extra == "+100") {
1827 c.options = SHIFT_KEY | CONTROL_KEY;
1828 } else if (action.extra == "-1") {
1829 c.options = RIGHT_MOUSE_KEY;
1830 } else if (action.extra == "-5") {
1831 c.options = RIGHT_MOUSE_KEY | SHIFT_KEY;
1832 } else if (action.extra == "-20") {
1833 c.options = RIGHT_MOUSE_KEY | CONTROL_KEY;
1834 } else if (action.extra == "-100") {
1835 c.options = RIGHT_MOUSE_KEY | SHIFT_KEY | CONTROL_KEY;
1836 }
1837 }
1838 else if (action.extra.find("queued") != std::string::npos) {
1839 c.options |= SHIFT_KEY;
1840 }
1841 GiveCommand(c);
1842 break;
1843 }
1844 case CMDTYPE_ICON_MODE: {
1845 int newMode;
1846
1847 if (!action.extra.empty() && (iconCmd < 0)) {
1848 newMode = atoi(action.extra.c_str());
1849 } else {
1850 newMode = atoi(cmdDesc.params[0].c_str()) + 1;
1851 }
1852
1853 if ((newMode < 0) || ((size_t)newMode > (cmdDesc.params.size() - 2))) {
1854 newMode = 0;
1855 }
1856
1857 // not really required
1858 char t[10];
1859 SNPRINTF(t, 10, "%d", newMode);
1860 cmdDesc.params[0] = t;
1861
1862 Command c(cmdDesc.id, 0, newMode);
1863 GiveCommand(c);
1864 forceLayoutUpdate = true;
1865 break;
1866 }
1867 case CMDTYPE_NUMBER:{
1868 if (!action.extra.empty()) {
1869 const CommandDescription& cd = cmdDesc;
1870 float value = atof(action.extra.c_str());
1871 float minV = 0.0f;
1872 float maxV = 100.0f;
1873 if (!cd.params.empty()) { minV = atof(cd.params[0].c_str()); }
1874 if (cd.params.size() >= 2) { maxV = atof(cd.params[1].c_str()); }
1875 value = std::max(std::min(value, maxV), minV);
1876 Command c(cd.id, 0, value);
1877 if (action.extra.find("queued") != std::string::npos) {
1878 c.options = SHIFT_KEY;
1879 }
1880 GiveCommand(c);
1881 break;
1882 }
1883 else {
1884 // fall through
1885 }
1886 }
1887 case CMDTYPE_ICON_MAP:
1888 case CMDTYPE_ICON_AREA:
1889 case CMDTYPE_ICON_UNIT:
1890 case CMDTYPE_ICON_UNIT_OR_MAP:
1891 case CMDTYPE_ICON_FRONT:
1892 case CMDTYPE_ICON_UNIT_OR_AREA:
1893 case CMDTYPE_ICON_UNIT_OR_RECTANGLE:
1894 case CMDTYPE_ICON_UNIT_FEATURE_OR_AREA: {
1895 SetShowingMetal(false);
1896 actionOffset = actionIndex;
1897 lastKeySet = ks;
1898 inCommand = a;
1899 break;
1900 }
1901 case CMDTYPE_ICON_BUILDING: {
1902 const UnitDef* ud=unitDefHandler->GetUnitDefByID(-cmdDesc.id);
1903 SetShowingMetal(ud->extractsMetal > 0);
1904 actionOffset = actionIndex;
1905 lastKeySet = ks;
1906 inCommand = a;
1907 break;
1908 }
1909 case CMDTYPE_NEXT: {
1910 ++activePage;
1911 if(activePage > maxPage)
1912 activePage=0;
1913 selectedUnitsHandler.SetCommandPage(activePage);
1914 break;
1915 }
1916 case CMDTYPE_PREV:{
1917 --activePage;
1918 if(activePage<0)
1919 activePage = maxPage;
1920 selectedUnitsHandler.SetCommandPage(activePage);
1921 break;
1922 }
1923 case CMDTYPE_CUSTOM: {
1924 RunCustomCommands(cmdDesc.params, false);
1925 break;
1926 }
1927 default:{
1928 lastKeySet.Reset();
1929 SetShowingMetal(false);
1930 inCommand = a;
1931 }
1932 }
1933 return true; // we used the command
1934 }
1935
1936 return false; // couldn't find a match
1937 }
1938
1939
KeyReleased(int key)1940 bool CGuiHandler::KeyReleased(int key)
1941 {
1942 return false;
1943 }
1944
FinishCommand(int button)1945 void CGuiHandler::FinishCommand(int button)
1946 {
1947 if ((button == SDL_BUTTON_LEFT) && (KeyInput::GetKeyModState(KMOD_SHIFT) || invertQueueKey)) {
1948 needShift = true;
1949 } else {
1950 SetShowingMetal(false);
1951 inCommand = -1;
1952 }
1953 }
1954
1955
IsAbove(int x,int y)1956 bool CGuiHandler::IsAbove(int x, int y)
1957 {
1958 return AboveGui(x, y);
1959 }
1960
1961
GetTooltip(int x,int y)1962 std::string CGuiHandler::GetTooltip(int x, int y)
1963 {
1964 std::string s;
1965
1966 const int iconPos = IconAtPos(x, y);
1967 const int iconCmd = (iconPos >= 0) ? icons[iconPos].commandsID : -1;
1968 if ((iconCmd >= 0) && (iconCmd < (int)commands.size())) {
1969 if (commands[iconCmd].tooltip != "") {
1970 s = commands[iconCmd].tooltip;
1971 }else{
1972 s = commands[iconCmd].name;
1973 }
1974
1975 const CKeyBindings::HotkeyList& hl = keyBindings->GetHotkeys(commands[iconCmd].action);
1976 if(!hl.empty()){
1977 s += "\nHotkeys:";
1978 for (const std::string& hk: hl) {
1979 s += " " + hk;
1980 }
1981 }
1982 }
1983 return s;
1984 }
1985
1986 // CALLINFO:
1987 // luaunsyncedread::getcurrenttooltip --> mousehandler::getcurrenttooltip
1988 // tooltipconsole::draw --> mousehandler::getcurrenttooltip
1989 // mousehandler::getcurrenttooltip --> GetBuildTooltip
1990 // mousehandler::getcurrenttooltip --> CMiniMap::gettooltip --> GetBuildTooltip
GetBuildTooltip() const1991 std::string CGuiHandler::GetBuildTooltip() const
1992 {
1993 if ((inCommand >= 0) && ((size_t)inCommand < commands.size()) &&
1994 (commands[inCommand].type == CMDTYPE_ICON_BUILDING)) {
1995 return commands[inCommand].tooltip;
1996 }
1997 return std::string("");
1998 }
1999
2000
GetOrderPreview()2001 Command CGuiHandler::GetOrderPreview()
2002 {
2003 return GetCommand(mouse->lastx, mouse->lasty, -1, true);
2004 }
2005
2006
CheckCommand(Command c)2007 inline Command CheckCommand(Command c) {
2008 if (selectedUnitsHandler.selectedUnits.empty() || (c.options & SHIFT_KEY))
2009 return c; // always allow queued commands, since conditions may change so the command becomes valid
2010 for (CUnitSet::iterator ui = selectedUnitsHandler.selectedUnits.begin(); ui != selectedUnitsHandler.selectedUnits.end(); ++ui) {
2011 if((*ui)->commandAI->AllowedCommand(c, false))
2012 return c;
2013 }
2014 Command failedRet(CMD_FAILED);
2015 return failedRet;
2016 }
2017
ZeroRadiusAllowed(const Command & c)2018 bool ZeroRadiusAllowed(const Command &c) {
2019 switch(c.GetID()) {
2020 case CMD_CAPTURE:
2021 case CMD_GUARD:
2022 case CMD_LOAD_UNITS:
2023 case CMD_RECLAIM:
2024 case CMD_REPAIR:
2025 case CMD_RESTORE:
2026 case CMD_RESURRECT:
2027 return false;
2028 }
2029 return true;
2030 }
2031
GetCommand(int mouseX,int mouseY,int buttonHint,bool preview,const float3 & cameraPos,const float3 & mouseDir)2032 Command CGuiHandler::GetCommand(int mouseX, int mouseY, int buttonHint, bool preview, const float3& cameraPos, const float3& mouseDir)
2033 {
2034 Command defaultRet(CMD_FAILED);
2035
2036 int button;
2037 if (buttonHint >= SDL_BUTTON_LEFT) {
2038 button = buttonHint;
2039 } else if (inCommand != -1) {
2040 button = SDL_BUTTON_LEFT;
2041 } else if (mouse->buttons[SDL_BUTTON_RIGHT].pressed) {
2042 button = SDL_BUTTON_RIGHT;
2043 } else {
2044 return Command(CMD_STOP);
2045 }
2046
2047 int tempInCommand = inCommand;
2048
2049 if (button == SDL_BUTTON_RIGHT && preview) {
2050 // right click -> default cmd
2051 // (in preview we might not have default cmd memory set)
2052 if (mouse->buttons[SDL_BUTTON_RIGHT].pressed) {
2053 tempInCommand = defaultCmdMemory;
2054 } else {
2055 tempInCommand = GetDefaultCommand(mouseX, mouseY, cameraPos, mouseDir);
2056 }
2057 }
2058
2059 if ((tempInCommand >= 0) && ((size_t)tempInCommand < commands.size())) {
2060 switch(commands[tempInCommand].type) {
2061
2062 case CMDTYPE_NUMBER: {
2063 const float value = GetNumberInput(commands[tempInCommand]);
2064 Command c(commands[tempInCommand].id, CreateOptions(button), value);
2065 return CheckCommand(c);
2066 }
2067
2068 case CMDTYPE_ICON: {
2069 Command c(commands[tempInCommand].id, CreateOptions(button));
2070 if (button == SDL_BUTTON_LEFT && !preview) {
2071 LOG_L(L_WARNING, "CMDTYPE_ICON left button press in incommand test? This should not happen.");
2072 }
2073 return CheckCommand(c);
2074 }
2075
2076 case CMDTYPE_ICON_MAP: {
2077 const float dist = CGround::LineGroundCol(cameraPos, cameraPos + (mouseDir * globalRendering->viewRange * 1.4f), false);
2078 if (dist < 0) {
2079 return defaultRet;
2080 }
2081 const float3 pos = cameraPos + (mouseDir * dist);
2082 Command c(commands[tempInCommand].id, CreateOptions(button), pos);
2083 return CheckCommand(c);
2084 }
2085
2086 case CMDTYPE_ICON_BUILDING: {
2087 const float dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
2088 if (dist < 0) {
2089 return defaultRet;
2090 }
2091 const UnitDef* unitdef = unitDefHandler->GetUnitDefByID(-commands[inCommand].id);
2092
2093 if(!unitdef){
2094 return Command(CMD_STOP);
2095 }
2096
2097 const float3 pos = cameraPos + mouseDir * dist;
2098 std::vector<BuildInfo> buildPos;
2099 BuildInfo bi(unitdef, pos, buildFacing);
2100 if (GetQueueKeystate() && (button == SDL_BUTTON_LEFT)) {
2101 const float dist = CGround::LineGroundCol(
2102 mouse->buttons[SDL_BUTTON_LEFT].camPos,
2103 mouse->buttons[SDL_BUTTON_LEFT].camPos +
2104 mouse->buttons[SDL_BUTTON_LEFT].dir * globalRendering->viewRange * 1.4f,
2105 false
2106 );
2107 const float3 pos2 = mouse->buttons[SDL_BUTTON_LEFT].camPos + mouse->buttons[SDL_BUTTON_LEFT].dir * dist;
2108 buildPos = GetBuildPos(BuildInfo(unitdef, pos2, buildFacing), bi, cameraPos, mouseDir);
2109 } else
2110 buildPos = GetBuildPos(bi, bi, cameraPos, mouseDir);
2111
2112 if (buildPos.empty()) {
2113 return Command(CMD_STOP);
2114 }
2115
2116 if (buildPos.size() == 1) {
2117 CFeature* feature = NULL;
2118 // TODO Maybe also check out-of-range for immobile builder?
2119 if (!CGameHelper::TestUnitBuildSquare(buildPos[0], feature, gu->myAllyTeam, false)) {
2120 return defaultRet;
2121 }
2122 }
2123
2124 int a = 0; // limit the number of max commands possible to send to avoid overflowing the network buffer
2125 for (std::vector<BuildInfo>::iterator bpi = buildPos.begin(); bpi != --buildPos.end() && a < 200; ++bpi) {
2126 ++a;
2127 Command c = bpi->CreateCommand(CreateOptions(button));
2128 if (!preview) {
2129 GiveCommand(c);
2130 }
2131 }
2132 Command c = buildPos.back().CreateCommand(CreateOptions(button));
2133 return CheckCommand(c);
2134 }
2135
2136 case CMDTYPE_ICON_UNIT: {
2137 CUnit* unit = NULL;
2138 CFeature* feature = NULL;
2139 Command c(commands[tempInCommand].id, CreateOptions(button));
2140
2141 TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, true);
2142 if (!unit) {
2143 return defaultRet;
2144 }
2145 c.PushParam(unit->id);
2146 return CheckCommand(c);
2147 }
2148
2149 case CMDTYPE_ICON_UNIT_OR_MAP: {
2150 Command c(commands[tempInCommand].id, CreateOptions(button));
2151
2152 CUnit* unit = NULL;
2153 CFeature* feature = NULL;
2154 const float dist2 = TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, true);
2155 if (dist2 > (globalRendering->viewRange * 1.4f - 300)) {
2156 return defaultRet;
2157 }
2158
2159 if (unit) {
2160 // clicked on unit
2161 c.PushParam(unit->id);
2162 } else {
2163 // clicked in map
2164 c.PushPos(cameraPos + (mouseDir * dist2));
2165 }
2166 return CheckCommand(c);
2167 }
2168
2169 case CMDTYPE_ICON_FRONT: {
2170 float dist = CGround::LineGroundCol(
2171 mouse->buttons[button].camPos,
2172 mouse->buttons[button].camPos +
2173 mouse->buttons[button].dir * globalRendering->viewRange * 1.4f,
2174 false
2175 );
2176 if (dist < 0) {
2177 return defaultRet;
2178 }
2179
2180 float3 pos = mouse->buttons[button].camPos + (mouse->buttons[button].dir * dist);
2181
2182 Command c(commands[tempInCommand].id, CreateOptions(button), pos);
2183
2184 if (mouse->buttons[button].movement > 30) { // only create the front if the mouse has moved enough
2185 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
2186 if (dist < 0) {
2187 return defaultRet;
2188 }
2189 float3 pos2 = cameraPos + (mouseDir * dist);
2190
2191 ProcessFrontPositions(pos, pos2);
2192
2193 c.SetPos(0, pos);
2194
2195 if (!commands[tempInCommand].params.empty() &&
2196 pos.SqDistance2D(pos2) > Square(atof(commands[tempInCommand].params[0].c_str()))) {
2197 float3 dif = pos2 - pos;
2198 dif.ANormalize();
2199 pos2 = pos + dif * atoi(commands[tempInCommand].params[0].c_str());
2200 }
2201
2202 c.PushPos(pos2);
2203 }
2204 return CheckCommand(c);
2205 }
2206
2207 case CMDTYPE_ICON_UNIT_OR_AREA:
2208 case CMDTYPE_ICON_UNIT_FEATURE_OR_AREA:
2209 case CMDTYPE_ICON_AREA: {
2210 float maxRadius = 100000;
2211 if (commands[tempInCommand].params.size() == 1) {
2212 maxRadius=atof(commands[tempInCommand].params[0].c_str());
2213 }
2214
2215 Command c(commands[tempInCommand].id, CreateOptions(button));
2216
2217 if (mouse->buttons[button].movement < 4) {
2218 CUnit* unit = NULL;
2219 CFeature* feature = NULL;
2220 const float dist2 = TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, true);
2221
2222 if (dist2 > (globalRendering->viewRange * 1.4f - 300) && (commands[tempInCommand].type != CMDTYPE_ICON_UNIT_FEATURE_OR_AREA)) {
2223 return defaultRet;
2224 }
2225
2226 if (feature && commands[tempInCommand].type == CMDTYPE_ICON_UNIT_FEATURE_OR_AREA) { // clicked on feature
2227 c.PushParam(unitHandler->MaxUnits() + feature->id);
2228 } else if (unit && commands[tempInCommand].type != CMDTYPE_ICON_AREA) { // clicked on unit
2229 if (c.GetID() == CMD_RESURRECT)
2230 return defaultRet; // cannot resurrect units!
2231 c.PushParam(unit->id);
2232 } else { // clicked in map
2233 if (explicitCommand < 0 || !ZeroRadiusAllowed(c)) // only attack ground if explicitly set the command
2234 return defaultRet;
2235 c.PushPos(cameraPos + (mouseDir * dist2));
2236 c.PushParam(0); // zero radius
2237 if (c.GetID() == CMD_UNLOAD_UNITS) {
2238 c.PushParam((float)buildFacing);
2239 }
2240 }
2241 } else { // created area
2242 float dist = CGround::LineGroundCol(
2243 mouse->buttons[button].camPos,
2244 mouse->buttons[button].camPos +
2245 mouse->buttons[button].dir * globalRendering->viewRange * 1.4f,
2246 false
2247 );
2248
2249 if (dist < 0) {
2250 return defaultRet;
2251 }
2252 const float3 pos = mouse->buttons[button].camPos + mouse->buttons[button].dir * dist;
2253 c.PushPos(pos);
2254 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
2255 if (dist < 0) {
2256 return defaultRet;
2257 }
2258 const float3 pos2 = cameraPos + mouseDir * dist;
2259 c.PushParam(std::min(maxRadius, pos.distance2D(pos2)));
2260 if (c.GetID() == CMD_UNLOAD_UNITS) {
2261 c.PushParam((float)buildFacing);
2262 }
2263 }
2264 return CheckCommand(c);
2265 }
2266
2267 case CMDTYPE_ICON_UNIT_OR_RECTANGLE: {
2268 Command c(commands[tempInCommand].id, CreateOptions(button));
2269
2270 if (mouse->buttons[button].movement < 16) {
2271 CUnit* unit;
2272 CFeature* feature;
2273 const float dist2 = TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, true);
2274
2275 if (dist2 > (globalRendering->viewRange * 1.4f - 300)) {
2276 return defaultRet;
2277 }
2278
2279 if (unit) {
2280 // clicked on unit
2281 c.PushParam(unit->id);
2282 } else {
2283 // clicked in map
2284 if (explicitCommand < 0) { // only attack ground if explicitly set the command
2285 return defaultRet;
2286 }
2287 c.PushPos(cameraPos + (mouseDir * dist2));
2288 }
2289 } else {
2290 // created rectangle
2291 float dist = CGround::LineGroundCol(
2292 mouse->buttons[button].camPos,
2293 mouse->buttons[button].camPos +
2294 mouse->buttons[button].dir * globalRendering->viewRange * 1.4f,
2295 false
2296 );
2297 if (dist < 0) {
2298 return defaultRet;
2299 }
2300 const float3 startPos = mouse->buttons[button].camPos + mouse->buttons[button].dir * dist;
2301 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
2302 if (dist < 0) {
2303 return defaultRet;
2304 }
2305 const float3 endPos = cameraPos + (mouseDir * dist);
2306 c.PushPos(startPos);
2307 c.PushPos(endPos);
2308 }
2309 return CheckCommand(c);
2310 }
2311
2312 default:
2313 return Command(CMD_STOP);
2314 }
2315 } else {
2316 if (!preview) {
2317 inCommand = -1;
2318 }
2319 }
2320 return Command(CMD_STOP);
2321 }
2322
2323
2324
WouldCancelAnyQueued(const BuildInfo & b)2325 static bool WouldCancelAnyQueued(const BuildInfo& b)
2326 {
2327 Command c = b.CreateCommand();
2328 CUnitSet::iterator ui = selectedUnitsHandler.selectedUnits.begin();
2329 for (; ui != selectedUnitsHandler.selectedUnits.end(); ++ui) {
2330 if ((*ui)->commandAI->WillCancelQueued(c)) {
2331 return true;
2332 }
2333 }
2334 return false;
2335 }
2336
FillRowOfBuildPos(const BuildInfo & startInfo,float x,float z,float xstep,float zstep,int n,int facing,bool nocancel,std::vector<BuildInfo> & ret)2337 static void FillRowOfBuildPos(const BuildInfo& startInfo, float x, float z, float xstep, float zstep, int n, int facing, bool nocancel, std::vector<BuildInfo>& ret)
2338 {
2339 for (int i = 0; i < n; ++i) {
2340 BuildInfo bi(startInfo.def, float3(x, 0.0f, z), (startInfo.buildFacing + facing) % NUM_FACINGS);
2341 bi.pos=CGameHelper::Pos2BuildPos(bi, false);
2342 if (!nocancel || !WouldCancelAnyQueued(bi)) {
2343 ret.push_back(bi);
2344 }
2345 x += xstep;
2346 z += zstep;
2347 }
2348 }
2349
2350 // Assuming both builds have the same unitdef
GetBuildPos(const BuildInfo & startInfo,const BuildInfo & endInfo,const float3 & cameraPos,const float3 & mouseDir)2351 std::vector<BuildInfo> CGuiHandler::GetBuildPos(const BuildInfo& startInfo, const BuildInfo& endInfo, const float3& cameraPos, const float3& mouseDir)
2352 {
2353 std::vector<BuildInfo> ret;
2354
2355 float3 start = CGameHelper::Pos2BuildPos(startInfo, false);
2356 float3 end = CGameHelper::Pos2BuildPos(endInfo, false);
2357
2358 BuildInfo other; // the unit around which buildings can be circled
2359
2360 if (GetQueueKeystate() && KeyInput::GetKeyModState(KMOD_CTRL)) {
2361 CUnit* unit;
2362 CFeature* feature;
2363 TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, true);
2364
2365 if (unit) {
2366 other.def = unit->unitDef;
2367 other.pos = unit->pos;
2368 other.buildFacing = unit->buildFacing;
2369 } else {
2370 Command c = CGameHelper::GetBuildCommand(cameraPos, mouseDir);
2371 if (c.GetID() < 0 && c.params.size() == 4) {
2372 other.pos = c.GetPos(0);
2373 other.def = unitDefHandler->GetUnitDefByID(-c.GetID());
2374 other.buildFacing = int(c.params[3]);
2375 }
2376 }
2377 }
2378
2379 if (other.def && GetQueueKeystate() && KeyInput::GetKeyModState(KMOD_CTRL)) {
2380 // circle build around building
2381 int oxsize = other.GetXSize() * SQUARE_SIZE;
2382 int ozsize = other.GetZSize() * SQUARE_SIZE;
2383 int xsize = startInfo.GetXSize() * SQUARE_SIZE;
2384 int zsize = startInfo.GetZSize() * SQUARE_SIZE;
2385
2386 start = end = CGameHelper::Pos2BuildPos(other, false);
2387 start.x -= oxsize / 2;
2388 start.z -= ozsize / 2;
2389 end.x += oxsize / 2;
2390 end.z += ozsize / 2;
2391
2392 int nvert = 1 + (oxsize / xsize);
2393 int nhori = 1 + (ozsize / xsize);
2394
2395 FillRowOfBuildPos(startInfo, end.x + zsize / 2, start.z + xsize / 2, 0, xsize, nhori, 3, true, ret);
2396 FillRowOfBuildPos(startInfo, end.x - xsize / 2, end.z + zsize / 2, -xsize, 0, nvert, 2, true, ret);
2397 FillRowOfBuildPos(startInfo, start.x - zsize / 2, end.z - xsize / 2, 0, -xsize, nhori, 1, true, ret);
2398 FillRowOfBuildPos(startInfo, start.x + xsize / 2, start.z - zsize / 2, xsize, 0, nvert, 0, true, ret);
2399
2400 } else { // rectangle or line
2401 const float3 delta = end - start;
2402
2403 const float xsize = SQUARE_SIZE * (startInfo.GetXSize() + buildSpacing * 2);
2404 const int xnum = (int)((math::fabs(delta.x) + xsize * 1.4f)/xsize);
2405 float xstep = (int)((0 < delta.x) ? xsize : -xsize);
2406
2407 const float zsize = SQUARE_SIZE * (startInfo.GetZSize() + buildSpacing * 2);
2408 const int znum = (int)((math::fabs(delta.z) + zsize * 1.4f)/zsize);
2409 float zstep = (int)((0 < delta.z) ? zsize : -zsize);
2410
2411 if (KeyInput::GetKeyModState(KMOD_ALT)) { // build a rectangle
2412 if (KeyInput::GetKeyModState(KMOD_CTRL)) { // hollow rectangle
2413 if ((1 < xnum) && (1 < znum)) {
2414 // go "down" on the "left" side
2415 FillRowOfBuildPos(startInfo, start.x , start.z + zstep , 0, zstep, znum - 1, 0, false, ret);
2416 // go "right" on the "bottom" side
2417 FillRowOfBuildPos(startInfo, start.x + xstep , start.z + (znum - 1) * zstep, xstep, 0, xnum - 1, 0, false, ret);
2418 // go "up" on the "right" side
2419 FillRowOfBuildPos(startInfo, start.x + (xnum - 1) * xstep, start.z + (znum - 2) * zstep, 0, -zstep, znum - 1, 0, false, ret);
2420 // go "left" on the "top" side
2421 FillRowOfBuildPos(startInfo, start.x + (xnum - 2) * xstep, start.z , -xstep, 0, xnum - 1, 0, false, ret);
2422 } else if (1 == xnum) {
2423 FillRowOfBuildPos(startInfo, start.x, start.z, 0, zstep, znum, 0, false, ret);
2424 } else if (1 == znum) {
2425 FillRowOfBuildPos(startInfo, start.x, start.z, xstep, 0, xnum, 0, false, ret);
2426 }
2427 } else { // filled rectangle
2428 int zn = 0;
2429 for (float z = start.z; zn < znum; ++zn) {
2430 if (zn & 1) {
2431 // every odd line "right" to "left"
2432 FillRowOfBuildPos(startInfo, start.x + (xnum - 1) * xstep, z, -xstep, 0, xnum, 0, false, ret);
2433 } else {
2434 // every even line "left" to "right"
2435 FillRowOfBuildPos(startInfo, start.x , z, xstep, 0, xnum, 0, false, ret);
2436 }
2437 z += zstep;
2438 }
2439 }
2440 } else { // build a line
2441 const bool xDominatesZ = math::fabs(delta.x) > math::fabs(delta.z);
2442 if (xDominatesZ) {
2443 zstep = KeyInput::GetKeyModState(KMOD_CTRL) ? 0 : xstep * delta.z / (delta.x ? delta.x : 1);
2444 } else {
2445 xstep = KeyInput::GetKeyModState(KMOD_CTRL) ? 0 : zstep * delta.x / (delta.z ? delta.z : 1);
2446 }
2447 FillRowOfBuildPos(startInfo, start.x, start.z, xstep, zstep, xDominatesZ ? xnum : znum, 0, false, ret);
2448 }
2449 }
2450 return ret;
2451 }
2452
2453
ProcessFrontPositions(float3 & pos0,const float3 & pos1)2454 void CGuiHandler::ProcessFrontPositions(float3& pos0, const float3& pos1)
2455 {
2456 if (!frontByEnds) {
2457 return; // leave it centered
2458 }
2459 pos0 = pos1 + ((pos0 - pos1) * 0.5f);
2460 pos0.y = CGround::GetHeightReal(pos0.x, pos0.z, false);
2461 }
2462
2463
2464 /******************************************************************************/
2465 /******************************************************************************/
2466
Draw()2467 void CGuiHandler::Draw()
2468 {
2469 if ((iconsCount <= 0) && (luaUI == NULL)) {
2470 return;
2471 }
2472
2473 glPushAttrib(GL_ENABLE_BIT);
2474
2475 glDisable(GL_FOG);
2476 glDisable(GL_DEPTH_TEST);
2477 glDisable(GL_LIGHTING);
2478 glDisable(GL_TEXTURE_2D);
2479 glEnable(GL_BLEND);
2480 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2481 glEnable(GL_ALPHA_TEST);
2482 glAlphaFunc(GL_GEQUAL, 0.01f);
2483
2484 if (iconsCount > 0) {
2485 DrawButtons();
2486 }
2487
2488 glPopAttrib();
2489 }
2490
2491
FindCornerText(const std::string & corner,const vector<std::string> & params)2492 static std::string FindCornerText(const std::string& corner, const vector<std::string>& params)
2493 {
2494 for (int p = 0; p < (int)params.size(); p++) {
2495 if (params[p].find(corner) == 0) {
2496 return params[p].substr(corner.length());
2497 }
2498 }
2499 return std::string("");
2500 }
2501
2502
DrawCustomButton(const IconInfo & icon,bool highlight)2503 void CGuiHandler::DrawCustomButton(const IconInfo& icon, bool highlight)
2504 {
2505 const CommandDescription& cmdDesc = commands[icon.commandsID];
2506
2507 const bool usedTexture = DrawTexture(icon, cmdDesc.iconname);
2508
2509 // highlight overlay before text is applied
2510 if (highlight) {
2511 DrawHilightQuad(icon);
2512 }
2513
2514 if (!usedTexture || !cmdDesc.onlyTexture) {
2515 DrawName(icon, cmdDesc.name, false);
2516 }
2517
2518 DrawNWtext(icon, FindCornerText("$nw$", cmdDesc.params));
2519 DrawSWtext(icon, FindCornerText("$sw$", cmdDesc.params));
2520 DrawNEtext(icon, FindCornerText("$ne$", cmdDesc.params));
2521 DrawSEtext(icon, FindCornerText("$se$", cmdDesc.params));
2522
2523 if (!usedTexture) {
2524 glColor4f(1.0f, 1.0f, 1.0f, 0.1f);
2525 DrawIconFrame(icon);
2526 }
2527 }
2528
2529
DrawUnitBuildIcon(const IconInfo & icon,int unitDefID)2530 bool CGuiHandler::DrawUnitBuildIcon(const IconInfo& icon, int unitDefID)
2531 {
2532 const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
2533 if (ud) {
2534 const Box& b = icon.visual;
2535 glEnable(GL_TEXTURE_2D);
2536 glColor4f(1.0f, 1.0f, 1.0f, textureAlpha);
2537 glBindTexture(GL_TEXTURE_2D, unitDefHandler->GetUnitDefImage(ud));
2538 glBegin(GL_QUADS);
2539 glTexCoord2f(0.0f, 0.0f); glVertex2f(b.x1, b.y1);
2540 glTexCoord2f(1.0f, 0.0f); glVertex2f(b.x2, b.y1);
2541 glTexCoord2f(1.0f, 1.0f); glVertex2f(b.x2, b.y2);
2542 glTexCoord2f(0.0f, 1.0f); glVertex2f(b.x1, b.y2);
2543 glEnd();
2544 return true;
2545 }
2546
2547 return false;
2548 }
2549
2550
ParseTextures(const std::string & texString,std::string & tex1,std::string & tex2,float & xscale,float & yscale)2551 static inline bool ParseTextures(const std::string& texString,
2552 std::string& tex1, std::string& tex2, float& xscale, float& yscale)
2553 {
2554 // format: "&<xscale>x<yscale>&<tex>1&<tex2>" -- <>'s are not included
2555
2556 char* endPtr;
2557
2558 const char* c = texString.c_str() + 1;
2559 xscale = strtod(c, &endPtr);
2560 if ((endPtr == c) || (endPtr[0] != 'x')) { return false; }
2561 c = endPtr + 1;
2562 yscale = strtod(c, &endPtr);
2563 if ((endPtr == c) || (endPtr[0] != '&')) { return false; }
2564 c = endPtr + 1;
2565 const char* tex1Start = c;
2566 while ((c[0] != 0) && (c[0] != '&')) { c++; }
2567 if (c[0] != '&') { return false; }
2568 const int tex1Len = c - tex1Start;
2569 c++;
2570 tex1 = c; // draw 'tex2' first
2571 tex2 = std::string(tex1Start, tex1Len);
2572
2573 return true;
2574 }
2575
2576
BindUnitTexByString(const std::string & str)2577 static inline bool BindUnitTexByString(const std::string& str)
2578 {
2579 char* endPtr;
2580 const char* startPtr = str.c_str() + 1; // skip the '#'
2581 const int unitDefID = (int)strtol(startPtr, &endPtr, 10);
2582 if (endPtr == startPtr) {
2583 return false; // bad unitID spec
2584 }
2585
2586 const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
2587 if (ud == NULL) {
2588 return false;
2589 }
2590
2591 glBindTexture(GL_TEXTURE_2D, unitDefHandler->GetUnitDefImage(ud));
2592
2593 return true;
2594 }
2595
2596
BindIconTexByString(const std::string & str)2597 static inline bool BindIconTexByString(const std::string& str)
2598 {
2599 char* endPtr;
2600 const char* startPtr = str.c_str() + 1; // skip the '^'
2601 const int unitDefID = (int)strtol(startPtr, &endPtr, 10);
2602 if (endPtr == startPtr) {
2603 return false; // bad unitID spec
2604 }
2605
2606 const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
2607 if (ud == NULL) {
2608 return false;
2609 }
2610
2611 ud->iconType->BindTexture();
2612
2613 return true;
2614 }
2615
2616
BindLuaTexByString(const std::string & str)2617 static inline bool BindLuaTexByString(const std::string& str)
2618 {
2619 CLuaHandle* luaHandle = NULL;
2620 const char scriptType = str[1];
2621 switch (scriptType) {
2622 case 'u': { luaHandle = luaUI; break; }
2623 case 'g': { luaHandle = &luaGaia->unsyncedLuaHandle; break; }
2624 case 'm': { luaHandle = &luaRules->unsyncedLuaHandle; break; }
2625 default: { break; }
2626 }
2627 if (luaHandle == NULL) {
2628 return false;
2629 }
2630 if (str[2] != LuaTextures::prefix) { // '!'
2631 return false;
2632 }
2633
2634 const string luaTexStr = str.substr(2);
2635 const LuaTextures::Texture* texInfo =
2636 luaHandle->GetTextures().GetInfo(luaTexStr);
2637 if (texInfo == NULL) {
2638 return false;
2639 }
2640
2641 if (texInfo->target != GL_TEXTURE_2D) {
2642 return false;
2643 }
2644 glBindTexture(GL_TEXTURE_2D, texInfo->id);
2645
2646 return true;
2647 }
2648
2649
BindTextureString(const std::string & str)2650 static bool BindTextureString(const std::string& str)
2651 {
2652 if (str[0] == '#') {
2653 return BindUnitTexByString(str);
2654 } else if (str[0] == '^') {
2655 return BindIconTexByString(str);
2656 } else if (str[0] == LuaTextures::prefix) { // '!'
2657 return BindLuaTexByString(str);
2658 } else {
2659 return CNamedTextures::Bind(str);
2660 }
2661 }
2662
2663
DrawTexture(const IconInfo & icon,const std::string & texName)2664 bool CGuiHandler::DrawTexture(const IconInfo& icon, const std::string& texName)
2665 {
2666 if (texName.empty()) {
2667 return false;
2668 }
2669
2670 std::string tex1;
2671 std::string tex2;
2672 float xscale = 1.0f;
2673 float yscale = 1.0f;
2674
2675 // double texture?
2676 if (texName[0] == '&') {
2677 if (!ParseTextures(texName, tex1, tex2, xscale, yscale)) {
2678 return false;
2679 }
2680 } else {
2681 tex1 = texName;
2682 }
2683
2684 // bind the texture for the full size quad
2685 if (!BindTextureString(tex1)) {
2686 if (tex2.empty()) {
2687 return false;
2688 } else {
2689 if (!BindTextureString(tex2)) {
2690 return false;
2691 } else {
2692 tex2.clear(); // cancel the scaled draw
2693 }
2694 }
2695 }
2696
2697 glEnable(GL_TEXTURE_2D);
2698 glColor4f(1.0f, 1.0f, 1.0f, textureAlpha);
2699
2700 // draw the full size quad
2701 const Box& b = icon.visual;
2702 glBegin(GL_QUADS);
2703 glTexCoord2f(0.0f, 0.0f); glVertex2f(b.x1, b.y1);
2704 glTexCoord2f(1.0f, 0.0f); glVertex2f(b.x2, b.y1);
2705 glTexCoord2f(1.0f, 1.0f); glVertex2f(b.x2, b.y2);
2706 glTexCoord2f(0.0f, 1.0f); glVertex2f(b.x1, b.y2);
2707 glEnd();
2708
2709 if (tex2.empty()) {
2710 return true; // success, no second texture to draw
2711 }
2712
2713 // bind the texture for the scaled quad
2714 if (!BindTextureString(tex2)) {
2715 return false;
2716 }
2717
2718 assert(xscale<=0.5); //border >= 50% makes no sence
2719 assert(yscale<=0.5);
2720
2721 // calculate the scaled quad
2722 const float x1 = b.x1 + (xIconSize * xscale);
2723 const float x2 = b.x2 - (xIconSize * xscale);
2724 const float y1 = b.y1 - (yIconSize * yscale);
2725 const float y2 = b.y2 + (yIconSize * yscale);
2726
2727 // draw the scaled quad
2728 glBegin(GL_QUADS);
2729 glTexCoord2f(0.0f, 0.0f); glVertex2f(x1, y1);
2730 glTexCoord2f(1.0f, 0.0f); glVertex2f(x2, y1);
2731 glTexCoord2f(1.0f, 1.0f); glVertex2f(x2, y2);
2732 glTexCoord2f(0.0f, 1.0f); glVertex2f(x1, y2);
2733 glEnd();
2734
2735 return true;
2736 }
2737
2738
DrawIconFrame(const IconInfo & icon)2739 void CGuiHandler::DrawIconFrame(const IconInfo& icon)
2740 {
2741 const Box& b = icon.visual;
2742 glDisable(GL_TEXTURE_2D);
2743 glBegin(GL_LINE_LOOP);
2744 const float fudge = 0.001f; // avoids getting creamed if iconBorder == 0.0
2745 glVertex2f(b.x1 + fudge, b.y1 - fudge);
2746 glVertex2f(b.x2 - fudge, b.y1 - fudge);
2747 glVertex2f(b.x2 - fudge, b.y2 + fudge);
2748 glVertex2f(b.x1 + fudge, b.y2 + fudge);
2749 glEnd();
2750 }
2751
2752
DrawName(const IconInfo & icon,const std::string & text,bool offsetForLEDs)2753 void CGuiHandler::DrawName(const IconInfo& icon, const std::string& text,
2754 bool offsetForLEDs)
2755 {
2756 if (text.empty()) {
2757 return;
2758 }
2759 const Box& b = icon.visual;
2760
2761 const float yShrink = offsetForLEDs ? (0.125f * yIconSize) : 0.0f;
2762
2763 const float tWidth = std::max(0.01f, font->GetSize() * font->GetTextWidth(text) * globalRendering->pixelX); // FIXME
2764 const float tHeight = std::max(0.01f, font->GetSize() * font->GetTextHeight(text) * globalRendering->pixelY); // FIXME merge in 1 function?
2765 const float textBorder2 = (2.0f * textBorder);
2766 const float xScale = (xIconSize - textBorder2 ) / tWidth;
2767 const float yScale = (yIconSize - textBorder2 - yShrink) / tHeight;
2768 const float fontScale = std::min(xScale, yScale);
2769
2770 const float xCenter = 0.5f * (b.x1 + b.x2);
2771 const float yCenter = 0.5f * (b.y1 + b.y2 + yShrink);
2772
2773 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
2774 font->glPrint(xCenter, yCenter, fontScale, (dropShadows ? FONT_SHADOW : 0) | FONT_CENTER | FONT_VCENTER | FONT_SCALE | FONT_NORM, text);
2775 }
2776
2777
DrawNWtext(const IconInfo & icon,const std::string & text)2778 void CGuiHandler::DrawNWtext(const IconInfo& icon, const std::string& text)
2779 {
2780 if (text.empty()) {
2781 return;
2782 }
2783 const Box& b = icon.visual;
2784 const float tHeight = font->GetSize() * font->GetTextHeight(text) * globalRendering->pixelY;
2785 const float fontScale = (yIconSize * 0.2f) / tHeight;
2786 const float xPos = b.x1 + textBorder + 0.002f;
2787 const float yPos = b.y1 - textBorder - 0.006f;
2788
2789 font->glPrint(xPos, yPos, fontScale, FONT_TOP | FONT_SCALE | FONT_NORM, text);
2790 }
2791
2792
DrawSWtext(const IconInfo & icon,const std::string & text)2793 void CGuiHandler::DrawSWtext(const IconInfo& icon, const std::string& text)
2794 {
2795 if (text.empty()) {
2796 return;
2797 }
2798 const Box& b = icon.visual;
2799 const float tHeight = font->GetSize() * font->GetTextHeight(text) * globalRendering->pixelY;
2800 const float fontScale = (yIconSize * 0.2f) / tHeight;
2801 const float xPos = b.x1 + textBorder + 0.002f;
2802 const float yPos = b.y2 + textBorder + 0.002f;
2803
2804 font->glPrint(xPos, yPos, fontScale, FONT_SCALE | FONT_NORM, text);
2805 }
2806
2807
DrawNEtext(const IconInfo & icon,const std::string & text)2808 void CGuiHandler::DrawNEtext(const IconInfo& icon, const std::string& text)
2809 {
2810 if (text.empty()) {
2811 return;
2812 }
2813 const Box& b = icon.visual;
2814 const float tHeight = font->GetSize() * font->GetTextHeight(text) * globalRendering->pixelY;
2815 const float fontScale = (yIconSize * 0.2f) / tHeight;
2816 const float xPos = b.x2 - textBorder - 0.002f;
2817 const float yPos = b.y1 - textBorder - 0.006f;
2818
2819 font->glPrint(xPos, yPos, fontScale, FONT_TOP | FONT_RIGHT | FONT_SCALE | FONT_NORM, text);
2820 }
2821
2822
DrawSEtext(const IconInfo & icon,const std::string & text)2823 void CGuiHandler::DrawSEtext(const IconInfo& icon, const std::string& text)
2824 {
2825 if (text.empty()) {
2826 return;
2827 }
2828 const Box& b = icon.visual;
2829 const float tHeight = font->GetSize() * font->GetTextHeight(text) * globalRendering->pixelY;
2830 const float fontScale = (yIconSize * 0.2f) / tHeight;
2831 const float xPos = b.x2 - textBorder - 0.002f;
2832 const float yPos = b.y2 + textBorder + 0.002f;
2833
2834 font->glPrint(xPos, yPos, fontScale, FONT_RIGHT | FONT_SCALE | FONT_NORM, text);
2835 }
2836
2837
DrawHilightQuad(const IconInfo & icon)2838 void CGuiHandler::DrawHilightQuad(const IconInfo& icon)
2839 {
2840 if (icon.commandsID == inCommand) {
2841 glColor4f(0.3f, 0.0f, 0.0f, 1.0f);
2842 } else if (mouse->buttons[SDL_BUTTON_LEFT].pressed) {
2843 glColor4f(0.2f, 0.0f, 0.0f, 1.0f);
2844 } else {
2845 glColor4f(0.0f, 0.0f, 0.2f, 1.0f);
2846 }
2847 const Box& b = icon.visual;
2848 glDisable(GL_TEXTURE_2D);
2849 glBlendFunc(GL_ONE, GL_ONE); // additive blending
2850 glBegin(GL_QUADS);
2851 glVertex2f(b.x1, b.y1);
2852 glVertex2f(b.x2, b.y1);
2853 glVertex2f(b.x2, b.y2);
2854 glVertex2f(b.x1, b.y2);
2855 glEnd();
2856 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2857 }
2858
2859
DrawButtons()2860 void CGuiHandler::DrawButtons() // Only called by Draw
2861 {
2862 glLineWidth(1.0f);
2863 font->Begin();
2864
2865 // frame box
2866 const float alpha = (frameAlpha < 0.0f) ? guiAlpha : frameAlpha;
2867 if (alpha > 0.0f) {
2868 glColor4f(0.2f, 0.2f, 0.2f, alpha);
2869 glBegin(GL_QUADS);
2870 glVertex2f(buttonBox.x1, buttonBox.y1);
2871 glVertex2f(buttonBox.x1, buttonBox.y2);
2872 glVertex2f(buttonBox.x2, buttonBox.y2);
2873 glVertex2f(buttonBox.x2, buttonBox.y1);
2874 glEnd();
2875 }
2876
2877 const int mouseIcon = IconAtPos(mouse->lastx, mouse->lasty);
2878 const int buttonStart = std::min(iconsCount, activePage * iconsPerPage);
2879 const int buttonEnd = std::min(iconsCount, buttonStart + iconsPerPage);
2880
2881 for (int ii = buttonStart; ii < buttonEnd; ii++) {
2882
2883 const IconInfo& icon = icons[ii];
2884 if (icon.commandsID < 0) {
2885 continue; // inactive icon
2886 }
2887 const CommandDescription& cmdDesc = commands[icon.commandsID];
2888 const bool customCommand = (cmdDesc.id == CMD_INTERNAL) &&
2889 (cmdDesc.type == CMDTYPE_CUSTOM);
2890 const bool highlight = ((mouseIcon == ii) ||
2891 (icon.commandsID == inCommand)) &&
2892 (!customCommand || !cmdDesc.params.empty());
2893
2894 if (customCommand) {
2895 DrawCustomButton(icon, highlight);
2896 }
2897 else {
2898 bool usedTexture = false;
2899 bool onlyTexture = cmdDesc.onlyTexture;
2900 const bool useLEDs = useOptionLEDs && (cmdDesc.type == CMDTYPE_ICON_MODE);
2901
2902 // specified texture
2903 if (DrawTexture(icon, cmdDesc.iconname)) {
2904 usedTexture = true;
2905 }
2906
2907 // unit buildpic
2908 if (!usedTexture) {
2909 if (cmdDesc.id < 0) {
2910 const UnitDef* ud = unitDefHandler->GetUnitDefByID(-cmdDesc.id);
2911 if (ud != NULL) {
2912 DrawUnitBuildIcon(icon, -cmdDesc.id);
2913 usedTexture = true;
2914 onlyTexture = true;
2915 }
2916 }
2917 }
2918
2919 // highlight background before text is applied
2920 if (highlight) {
2921 DrawHilightQuad(icon);
2922 }
2923
2924 // build count text (NOTE the weird bracing)
2925 if (cmdDesc.id < 0) {
2926 const int psize = (int)cmdDesc.params.size();
2927 if (psize > 0) { DrawSWtext(icon, cmdDesc.params[0]);
2928 if (psize > 1) { DrawNEtext(icon, cmdDesc.params[1]);
2929 if (psize > 2) { DrawNWtext(icon, cmdDesc.params[2]);
2930 if (psize > 3) { DrawSEtext(icon, cmdDesc.params[3]); } } } }
2931 }
2932
2933 // draw arrows, or a frame for text
2934 if (!usedTexture || !onlyTexture) {
2935 if ((cmdDesc.type == CMDTYPE_PREV) || (cmdDesc.type == CMDTYPE_NEXT)) {
2936 // pick the color for the arrow
2937 if (highlight) {
2938 glColor4f(1.0f, 1.0f, 0.0f, 1.0f); // selected
2939 } else {
2940 glColor4f(0.7f, 0.7f, 0.7f, 1.0f); // normal
2941 }
2942 if (cmdDesc.type == CMDTYPE_PREV) {
2943 DrawPrevArrow(icon);
2944 } else {
2945 DrawNextArrow(icon);
2946 }
2947 }
2948 else if (!usedTexture) {
2949 // no texture, no arrow, ... draw a frame
2950 glColor4f(1.0f, 1.0f, 1.0f, 0.1f);
2951 DrawIconFrame(icon);
2952 }
2953
2954 // draw the text
2955 // command name (or parameter)
2956 std::string toPrint = cmdDesc.name;
2957 if (cmdDesc.type == CMDTYPE_ICON_MODE
2958 && !cmdDesc.params.empty()) {
2959 const int opt = atoi(cmdDesc.params[0].c_str()) + 1;
2960 if (opt < 0 || static_cast<size_t>(opt) < cmdDesc.params.size()) {
2961 toPrint = cmdDesc.params[opt];
2962 }
2963 }
2964 DrawName(icon, toPrint, useLEDs);
2965 }
2966
2967 // draw the mode indicators
2968 if (useLEDs) {
2969 DrawOptionLEDs(icon);
2970 }
2971 }
2972
2973 // darken disabled commands
2974 if (cmdDesc.disabled) {
2975 glDisable(GL_TEXTURE_2D);
2976 glBlendFunc(GL_DST_COLOR, GL_ZERO);
2977 glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
2978 const Box& vb = icon.visual;
2979 glRectf(vb.x1, vb.y1, vb.x2, vb.y2);
2980 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2981 }
2982
2983 // highlight outline
2984 if (highlight) {
2985 if (icon.commandsID == inCommand) {
2986 glColor4f(1.0f, 1.0f, 0.0f, 0.75f);
2987 } else if (mouse->buttons[SDL_BUTTON_LEFT].pressed ||
2988 mouse->buttons[SDL_BUTTON_RIGHT].pressed) {
2989 glColor4f(1.0f, 0.0f, 0.0f, 0.50f);
2990 } else {
2991 glColor4f(1.0f, 1.0f, 1.0f, 0.50f);
2992 }
2993 glLineWidth(1.49f);
2994 DrawIconFrame(icon);
2995 glLineWidth(1.0f);
2996 }
2997 }
2998
2999 // active page indicator
3000 if (luaUI == NULL) {
3001 if (selectedUnitsHandler.BuildIconsFirst()) {
3002 glColor4fv(cmdColors.build);
3003 } else {
3004 glColor4f(0.7f, 0.7f, 0.7f, 1.0f);
3005 }
3006 const float textSize = 1.2f;
3007 font->glFormat(xBpos, yBpos, textSize, FONT_CENTER | FONT_VCENTER | FONT_SCALE | FONT_NORM, "%i", activePage + 1);
3008 }
3009
3010 DrawMenuName();
3011
3012 font->End();
3013
3014 // LuaUI can handle this
3015 if (luaUI == NULL || drawSelectionInfo)
3016 DrawSelectionInfo();
3017
3018 DrawNumberInput();
3019 }
3020
3021
DrawMenuName()3022 void CGuiHandler::DrawMenuName() // Only called by drawbuttons
3023 {
3024 if (!menuName.empty() && (iconsCount > 0)) {
3025 const float fontScale = 1.0f;
3026 const float xp = 0.5f * (buttonBox.x1 + buttonBox.x2);
3027 const float yp = buttonBox.y2 + (yIconSize * 0.125f);
3028
3029 if (!outlineFonts) {
3030 const float textHeight = fontScale * font->GetTextHeight(menuName) * globalRendering->pixelY;
3031 glDisable(GL_TEXTURE_2D);
3032 glColor4f(0.2f, 0.2f, 0.2f, guiAlpha);
3033 glRectf(buttonBox.x1,
3034 buttonBox.y2,
3035 buttonBox.x2,
3036 buttonBox.y2 + textHeight + (yIconSize * 0.25f));
3037 font->glPrint(xp, yp, fontScale, FONT_CENTER | FONT_SCALE | FONT_NORM, menuName);
3038 }
3039 else {
3040 font->SetColors(); // default
3041 font->glPrint(xp, yp, fontScale, FONT_CENTER | FONT_OUTLINE | FONT_SCALE | FONT_NORM, menuName);
3042 }
3043 }
3044
3045 }
3046
3047
DrawSelectionInfo()3048 void CGuiHandler::DrawSelectionInfo()
3049 {
3050 if (!selectedUnitsHandler.selectedUnits.empty()) {
3051 std::ostringstream buf;
3052
3053 if (selectedUnitsHandler.GetSelectedGroup() != -1) {
3054 buf << "Selected units " << selectedUnitsHandler.selectedUnits.size() << " [Group " << selectedUnitsHandler.GetSelectedGroup() << "]";
3055 } else {
3056 buf << "Selected units " << selectedUnitsHandler.selectedUnits.size();
3057 }
3058
3059 const float fontScale = 1.0f;
3060 const float fontSize = fontScale * smallFont->GetSize();
3061
3062 if (!outlineFonts) {
3063 float descender;
3064 const float textWidth = fontSize * smallFont->GetTextWidth(buf.str()) * globalRendering->pixelX;
3065 float textHeight = fontSize * smallFont->GetTextHeight(buf.str(), &descender) * globalRendering->pixelY;
3066 const float textDescender = fontSize * descender * globalRendering->pixelY; //! descender is always negative
3067 textHeight -= textDescender;
3068
3069 glDisable(GL_TEXTURE_2D);
3070 glColor4f(0.2f, 0.2f, 0.2f, guiAlpha);
3071 glRectf(xSelectionPos - frameBorder,
3072 ySelectionPos - frameBorder,
3073 xSelectionPos + frameBorder + textWidth,
3074 ySelectionPos + frameBorder + textHeight);
3075 glColor4f(1.0f, 1.0f, 1.0f, 0.8f);
3076 smallFont->glPrint(xSelectionPos, ySelectionPos - textDescender, fontSize, FONT_BASELINE | FONT_NORM, buf.str());
3077 } else {
3078 smallFont->SetColors(); // default
3079 smallFont->glPrint(xSelectionPos, ySelectionPos, fontSize, FONT_OUTLINE | FONT_NORM, buf.str());
3080 }
3081 }
3082 }
3083
3084
DrawNumberInput()3085 void CGuiHandler::DrawNumberInput() // Only called by drawbuttons
3086 {
3087 // draw the value for CMDTYPE_NUMBER commands
3088 if ((inCommand >= 0) && ((size_t)inCommand < commands.size())) {
3089 const CommandDescription& cd = commands[inCommand];
3090 if (cd.type == CMDTYPE_NUMBER) {
3091 const float value = GetNumberInput(cd);
3092 glDisable(GL_TEXTURE_2D);
3093 glColor4f(1.0f, 1.0f, 1.0f, 0.8f);
3094 const float mouseX = (float)mouse->lastx / (float)globalRendering->viewSizeX;
3095 const float slideX = std::min(std::max(mouseX, 0.25f), 0.75f);
3096 //const float mouseY = 1.0f - (float)(mouse->lasty - 16) / (float)globalRendering->viewSizeY;
3097 glColor4f(1.0f, 1.0f, 0.0f, 0.8f);
3098 glRectf(0.235f, 0.45f, 0.25f, 0.55f);
3099 glRectf(0.75f, 0.45f, 0.765f, 0.55f);
3100 glColor4f(0.0f, 0.0f, 1.0f, 0.8f);
3101 glRectf(0.25f, 0.49f, 0.75f, 0.51f);
3102 glBegin(GL_TRIANGLES);
3103 glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
3104 glVertex2f(slideX + 0.015f, 0.55f);
3105 glVertex2f(slideX - 0.015f, 0.55f);
3106 glVertex2f(slideX, 0.50f);
3107 glVertex2f(slideX - 0.015f, 0.45f);
3108 glVertex2f(slideX + 0.015f, 0.45f);
3109 glVertex2f(slideX, 0.50f);
3110 glEnd();
3111 glColor4f(1.0f, 1.0f, 1.0f, 0.9f);
3112 font->glFormat(slideX, 0.56f, 2.0f, FONT_CENTER | FONT_SCALE | FONT_NORM, "%i", (int)value);
3113 }
3114 }
3115 }
3116
3117
DrawPrevArrow(const IconInfo & icon)3118 void CGuiHandler::DrawPrevArrow(const IconInfo& icon)
3119 {
3120 const Box& b = icon.visual;
3121 const float yCenter = 0.5f * (b.y1 + b.y2);
3122 const float xSize = 0.166f * math::fabs(b.x2 - b.x1);
3123 const float ySize = 0.125f * math::fabs(b.y2 - b.y1);
3124 const float xSiz2 = 2.0f * xSize;
3125 glDisable(GL_TEXTURE_2D);
3126 glBegin(GL_POLYGON);
3127 glVertex2f(b.x2 - xSize, yCenter - ySize);
3128 glVertex2f(b.x1 + xSiz2, yCenter - ySize);
3129 glVertex2f(b.x1 + xSize, yCenter);
3130 glVertex2f(b.x1 + xSiz2, yCenter + ySize);
3131 glVertex2f(b.x2 - xSize, yCenter + ySize);
3132 glEnd();
3133 }
3134
3135
DrawNextArrow(const IconInfo & icon)3136 void CGuiHandler::DrawNextArrow(const IconInfo& icon)
3137 {
3138 const Box& b = icon.visual;
3139 const float yCenter = 0.5f * (b.y1 + b.y2);
3140 const float xSize = 0.166f * math::fabs(b.x2 - b.x1);
3141 const float ySize = 0.125f * math::fabs(b.y2 - b.y1);
3142 const float xSiz2 = 2.0f * xSize;
3143 glDisable(GL_TEXTURE_2D);
3144 glBegin(GL_POLYGON);
3145 glVertex2f(b.x1 + xSize, yCenter - ySize);
3146 glVertex2f(b.x2 - xSiz2, yCenter - ySize);
3147 glVertex2f(b.x2 - xSize, yCenter);
3148 glVertex2f(b.x2 - xSiz2, yCenter + ySize);
3149 glVertex2f(b.x1 + xSize, yCenter + ySize);
3150 glEnd();
3151 }
3152
3153
DrawOptionLEDs(const IconInfo & icon)3154 void CGuiHandler::DrawOptionLEDs(const IconInfo& icon)
3155 {
3156 const CommandDescription& cmdDesc = commands[icon.commandsID];
3157
3158 const int pCount = (int)cmdDesc.params.size() - 1;
3159 if (pCount < 2) {
3160 return;
3161 }
3162 const int option = atoi(cmdDesc.params[0].c_str());
3163
3164 glLoadIdentity();
3165
3166 glDisable(GL_TEXTURE_2D);
3167
3168 const float xs = xIconSize / float(1 + (pCount * 2));
3169 const float ys = yIconSize * 0.125f;
3170 const float x1 = icon.visual.x1;
3171 const float y2 = icon.visual.y2;
3172 const float yp = 1.0f / float(globalRendering->viewSizeY);
3173
3174 for (int x = 0; x < pCount; x++) {
3175 if (x != option) {
3176 glColor4f(0.25f, 0.25f, 0.25f, 0.50f); // dark
3177 } else {
3178 if (pCount == 2) {
3179 if (option == 0) {
3180 glColor4f(1.0f, 0.0f, 0.0f, 0.75f); // red
3181 } else {
3182 glColor4f(0.0f, 1.0f, 0.0f, 0.75f); // green
3183 }
3184 } else if (pCount == 3) {
3185 if (option == 0) {
3186 glColor4f(1.0f, 0.0f, 0.0f, 0.75f); // red
3187 } else if (option == 1) {
3188 glColor4f(1.0f, 1.0f, 0.0f, 0.75f); // yellow
3189 } else {
3190 glColor4f(0.0f, 1.0f, 0.0f, 0.75f); // green
3191 }
3192 } else {
3193 glColor4f(0.75f, 0.75f, 0.75f, 0.75f); // light
3194 }
3195 }
3196
3197 const float startx = x1 + (xs * float(1 + (2 * x)));
3198 const float starty = y2 + (3.0f * yp) + textBorder;
3199
3200 glRectf(startx, starty, startx + xs, starty + ys);
3201
3202 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
3203 glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
3204 glRectf(startx, starty, startx + xs, starty + ys);
3205 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
3206 }
3207 }
3208
3209
3210 /******************************************************************************/
3211 /******************************************************************************/
3212
DrawSensorRange(int radius,const float * color,const float3 & pos)3213 static inline void DrawSensorRange(int radius,
3214 const float* color, const float3& pos)
3215 {
3216 const int sensorScale = radarHandler->radarDiv;
3217 const int realRadius = ((radius / sensorScale) * sensorScale);
3218 if (realRadius > 0) {
3219 glColor4fv(color);
3220 glSurfaceCircle(pos, (float)realRadius, 40);
3221 }
3222 }
3223
3224
GetConeList()3225 static inline GLuint GetConeList()
3226 {
3227 static GLuint list = 0; // FIXME: put in the class
3228 if (list != 0) {
3229 return list;
3230 }
3231 list = glGenLists(1);
3232 glNewList(list, GL_COMPILE); {
3233 glBegin(GL_TRIANGLE_FAN);
3234 const int divs = 64;
3235 glVertex3f(0.0f, 0.0f, 0.0f);
3236 for (int i = 0; i <= divs; i++) {
3237 const float rad = (PI * 2.0) * (float)i / (float)divs;
3238 glVertex3f(1.0f, math::sin(rad), math::cos(rad));
3239 }
3240 glEnd();
3241 }
3242 glEndList();
3243 return list;
3244 }
3245
3246
DrawWeaponCone(const float3 & pos,float len,float hrads,float heading,float pitch)3247 static void DrawWeaponCone(const float3& pos,
3248 float len, float hrads, float heading, float pitch)
3249 {
3250 glPushMatrix();
3251
3252 const float xlen = len * math::cos(hrads);
3253 const float yzlen = len * math::sin(hrads);
3254 glTranslatef(pos.x, pos.y, pos.z);
3255 glRotatef(heading * (180.0 / PI), 0.0f, 1.0f, 0.0f);
3256 glRotatef(pitch * (180.0 / PI), 0.0f, 0.0f, 1.0f);
3257 glScalef(xlen, yzlen, yzlen);
3258
3259 glEnable(GL_CULL_FACE);
3260
3261 glCullFace(GL_FRONT);
3262 glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
3263 glCallList(GetConeList());
3264
3265 glCullFace(GL_BACK);
3266 glColor4f(0.0f, 1.0f, 0.0f, 0.25f);
3267 glCallList(GetConeList());
3268
3269 glDisable(GL_CULL_FACE);
3270
3271 glPopMatrix();
3272 }
3273
3274
DrawWeaponArc(const CUnit * unit)3275 static inline void DrawWeaponArc(const CUnit* unit)
3276 {
3277 for (unsigned int n = 0; n < unit->weapons.size(); n++) {
3278 const CWeapon* w = unit->weapons[n];
3279
3280 // attack order needs to have been issued or wantedDir is undefined
3281 if (w->targetType == Target_None)
3282 continue;
3283 if (w->weaponDef->projectileType == WEAPON_BASE_PROJECTILE)
3284 continue;
3285
3286 const float3 weaponDir = (unit->frontdir * w->onlyForward) + (w->wantedDir * (1 - w->onlyForward));
3287
3288 const float hrads = math::acos(w->maxForwardAngleDif);
3289 const float heading = math::atan2(-weaponDir.z, weaponDir.x);
3290 const float pitch = math::asin(weaponDir.y);
3291
3292 // note: cone visualization is invalid for ballistic weapons
3293 DrawWeaponCone(w->weaponMuzzlePos, w->range, hrads, heading, pitch);
3294 }
3295 }
3296
3297
DrawMapStuff(bool onMinimap)3298 void CGuiHandler::DrawMapStuff(bool onMinimap)
3299 {
3300 if (!onMinimap) {
3301 glEnable(GL_DEPTH_TEST);
3302 glDepthMask(GL_FALSE);
3303 glDisable(GL_TEXTURE_2D);
3304 glEnable(GL_BLEND);
3305 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3306 glDisable(GL_ALPHA_TEST);
3307 }
3308
3309 float3 cameraPos=camera->GetPos();
3310 float3 mouseDir=mouse->dir;
3311
3312 // setup for minimap proxying
3313 const bool minimapCoords =
3314 (minimap->ProxyMode() ||
3315 ((activeReceiver != this) && !game->hideInterface &&
3316 (GetReceiverAt(mouse->lastx, mouse->lasty) == minimap)));
3317 if (minimapCoords) {
3318 cameraPos = minimap->GetMapPosition(mouse->lastx, mouse->lasty);
3319 mouseDir = -UpVector;
3320 if (miniMapMarker && minimap->FullProxy() &&
3321 !onMinimap && !minimap->GetMinimized()) {
3322 DrawMiniMapMarker(cameraPos);
3323 }
3324 }
3325
3326
3327 if (activeMousePress) {
3328 int cmdIndex = -1;
3329 int button = SDL_BUTTON_LEFT;
3330 if ((inCommand >= 0) && ((size_t)inCommand < commands.size())) {
3331 cmdIndex = inCommand;
3332 } else {
3333 if (mouse->buttons[SDL_BUTTON_RIGHT].pressed &&
3334 ((activeReceiver == this) || (minimap->ProxyMode()))) {
3335 cmdIndex = defaultCmdMemory;
3336 button = SDL_BUTTON_RIGHT;
3337 }
3338 }
3339
3340 if (mouse->buttons[button].pressed && (cmdIndex >= 0) && ((size_t)cmdIndex < commands.size())) {
3341 const CommandDescription& cmdDesc = commands[cmdIndex];
3342 switch (cmdDesc.type) {
3343 case CMDTYPE_ICON_FRONT: {
3344 if (mouse->buttons[button].movement > 30) {
3345 float maxSize = 1000000.0f;
3346 float sizeDiv = 0.0f;
3347 if (!cmdDesc.params.empty()) {
3348 maxSize = atof(cmdDesc.params[0].c_str());
3349 if (cmdDesc.params.size() > 1) {
3350 sizeDiv = atof(cmdDesc.params[1].c_str());
3351 }
3352 }
3353 DrawFront(button, maxSize, sizeDiv, onMinimap, cameraPos, mouseDir);
3354 }
3355 break;
3356 }
3357 case CMDTYPE_ICON_UNIT_OR_AREA:
3358 case CMDTYPE_ICON_UNIT_FEATURE_OR_AREA:
3359 case CMDTYPE_ICON_AREA: {
3360 float maxRadius=100000;
3361 if (cmdDesc.params.size() == 1) {
3362 maxRadius = atof(cmdDesc.params[0].c_str());
3363 }
3364 if (mouse->buttons[button].movement > 4) {
3365 float dist = CGround::LineGroundCol(
3366 mouse->buttons[button].camPos,
3367 mouse->buttons[button].camPos +
3368 mouse->buttons[button].dir * globalRendering->viewRange * 1.4f,
3369 false
3370 );
3371 if(dist<0){
3372 break;
3373 }
3374 float3 pos=mouse->buttons[button].camPos+mouse->buttons[button].dir*dist;
3375 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
3376 if (dist < 0) {
3377 break;
3378 }
3379 float3 pos2 = cameraPos + mouseDir * dist;
3380 const float* color;
3381 switch (cmdDesc.id) {
3382 case CMD_ATTACK:
3383 case CMD_AREA_ATTACK: { color = cmdColors.attack; break; }
3384 case CMD_REPAIR: { color = cmdColors.repair; break; }
3385 case CMD_RECLAIM: { color = cmdColors.reclaim; break; }
3386 case CMD_RESTORE: { color = cmdColors.restore; break; }
3387 case CMD_RESURRECT: { color = cmdColors.resurrect; break; }
3388 case CMD_LOAD_UNITS: { color = cmdColors.load; break; }
3389 case CMD_UNLOAD_UNIT:
3390 case CMD_UNLOAD_UNITS: { color = cmdColors.unload; break; }
3391 case CMD_CAPTURE: { color = cmdColors.capture; break; }
3392 default: {
3393 static const float grey[4] = { 0.5f, 0.5f, 0.5f, 0.5f };
3394 color = grey;
3395 }
3396 }
3397 const float radius = std::min(maxRadius, pos.distance2D(pos2));
3398 if (!onMinimap) {
3399 DrawArea(pos, radius, color);
3400 }
3401 else {
3402 glColor4f(color[0], color[1], color[2], 0.5f);
3403 glBegin(GL_TRIANGLE_FAN);
3404 const int divs = 256;
3405 for(int i = 0; i <= divs; ++i) {
3406 const float radians = (2 * PI) * (float)i / (float)divs;
3407 float3 p(pos.x, 0.0f, pos.z);
3408 p.x += fastmath::sin(radians) * radius;
3409 p.z += fastmath::cos(radians) * radius;
3410 glVertexf3(p);
3411 }
3412 glEnd();
3413 }
3414 }
3415 break;
3416 }
3417 case CMDTYPE_ICON_UNIT_OR_RECTANGLE:{
3418 if (mouse->buttons[button].movement >= 16) {
3419 float dist = CGround::LineGroundCol(
3420 mouse->buttons[button].camPos,
3421 mouse->buttons[button].camPos +
3422 mouse->buttons[button].dir * globalRendering->viewRange * 1.4f,
3423 false
3424 );
3425 if (dist < 0) {
3426 break;
3427 }
3428 const float3 pos1 = mouse->buttons[button].camPos+mouse->buttons[button].dir*dist;
3429 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
3430 if (dist < 0) {
3431 break;
3432 }
3433 const float3 pos2 = cameraPos+mouseDir*dist;
3434 if (!onMinimap) {
3435 DrawSelectBox(pos1, pos2, cameraPos);
3436 } else {
3437 glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
3438 glBegin(GL_QUADS);
3439 glVertex3f(pos1.x, 0.0f, pos1.z);
3440 glVertex3f(pos2.x, 0.0f, pos1.z);
3441 glVertex3f(pos2.x, 0.0f, pos2.z);
3442 glVertex3f(pos1.x, 0.0f, pos2.z);
3443 glEnd();
3444 }
3445 }
3446 break;
3447 }
3448 }
3449 }
3450 }
3451
3452 if (!onMinimap) {
3453 glBlendFunc((GLenum)cmdColors.SelectedBlendSrc(),
3454 (GLenum)cmdColors.SelectedBlendDst());
3455 glLineWidth(cmdColors.SelectedLineWidth());
3456 } else {
3457 glLineWidth(1.49f);
3458 }
3459
3460 // draw the ranges for the unit that is being pointed at
3461 const CUnit* pointedAt = NULL;
3462
3463 if (GetQueueKeystate()) {
3464 CUnit* unit = NULL;
3465 CFeature* feature = NULL;
3466
3467 if (minimapCoords) {
3468 unit = minimap->GetSelectUnit(cameraPos);
3469 } else {
3470 // ignore the returned distance, we don't care about it here
3471 TraceRay::GuiTraceRay(cameraPos, mouseDir, globalRendering->viewRange * 1.4f, NULL, unit, feature, false);
3472 }
3473
3474 if (unit && ((unit->losStatus[gu->myAllyTeam] & LOS_INLOS) || gu->spectatingFullView)) {
3475 pointedAt = unit;
3476 const UnitDef* unitdef = unit->unitDef;
3477 const bool enemyUnit = ((unit->allyteam != gu->myAllyTeam) && !gu->spectatingFullView);
3478 if (enemyUnit && unitdef->decoyDef) {
3479 unitdef = unitdef->decoyDef;
3480 }
3481
3482 // draw weapon range
3483 if (unitdef->maxWeaponRange > 0) {
3484 glDisable(GL_DEPTH_TEST);
3485 glColor4fv(cmdColors.rangeAttack);
3486 glBallisticCircle(unit->pos, unitdef->maxWeaponRange,
3487 unit->weapons[0], 40);
3488 glEnable(GL_DEPTH_TEST);
3489 }
3490 // draw decloak distance
3491 if (unit->decloakDistance > 0.0f) {
3492 glColor4fv(cmdColors.rangeDecloak);
3493 if (unit->unitDef->decloakSpherical && globalRendering->drawdebug) {
3494 glPushMatrix();
3495 glTranslatef(unit->midPos.x, unit->midPos.y, unit->midPos.z);
3496 glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
3497 GLUquadricObj* q = gluNewQuadric();
3498 gluQuadricDrawStyle(q, GLU_LINE);
3499 gluSphere(q, unit->decloakDistance, 10, 10);
3500 gluDeleteQuadric(q);
3501 glPopMatrix();
3502 } else { // cylindrical
3503 glSurfaceCircle(unit->pos, unit->decloakDistance, 40);
3504 }
3505 }
3506 // draw self destruct and damage distance
3507 if (unitdef->kamikazeDist > 0) {
3508 glColor4fv(cmdColors.rangeKamikaze);
3509 glSurfaceCircle(unit->pos, unitdef->kamikazeDist, 40);
3510
3511 const WeaponDef* wd = unitdef->selfdExpWeaponDef;
3512
3513 if (wd != NULL) {
3514 glColor4fv(cmdColors.rangeSelfDestruct);
3515 glSurfaceCircle(unit->pos, wd->damageAreaOfEffect, 40);
3516 }
3517 }
3518 // draw build distance for immobile builders
3519 if (unitdef->builder) {
3520 const float radius = unitdef->buildDistance;
3521 if (radius > 0.0f) {
3522 glColor4fv(cmdColors.rangeBuild);
3523 glSurfaceCircle(unit->pos, radius, 40);
3524 }
3525 }
3526 // draw shield range for immobile units
3527 if (unitdef->shieldWeaponDef) {
3528 glColor4fv(cmdColors.rangeShield);
3529 glSurfaceCircle(unit->pos, unitdef->shieldWeaponDef->shieldRadius, 40);
3530 }
3531 // draw sensor and jammer ranges
3532 if (unitdef->onoffable || unitdef->activateWhenBuilt) {
3533 const float3& p = unit->pos;
3534 DrawSensorRange(unitdef->radarRadius, cmdColors.rangeRadar, p);
3535 DrawSensorRange(unitdef->sonarRadius, cmdColors.rangeSonar, p);
3536 DrawSensorRange(unitdef->seismicRadius, cmdColors.rangeSeismic, p);
3537 DrawSensorRange(unitdef->jammerRadius, cmdColors.rangeJammer, p);
3538 DrawSensorRange(unitdef->sonarJamRadius, cmdColors.rangeSonarJammer, p);
3539 }
3540 // draw interceptor range
3541 if (unitdef->maxCoverage > 0.0f) {
3542 const CWeapon* w = NULL; //will be checked if any missiles are ready
3543 if (!enemyUnit) {
3544 w = unit->stockpileWeapon;
3545 if (w != NULL && !w->weaponDef->interceptor) {
3546 w = NULL; //if this isn't the interceptor, then don't use it
3547 }
3548 } //shows as on if enemy, a non-stockpiled weapon, or if the stockpile has a missile
3549 if (enemyUnit || (w == NULL) || w->numStockpiled) {
3550 glColor4fv(cmdColors.rangeInterceptorOn);
3551 } else {
3552 glColor4fv(cmdColors.rangeInterceptorOff);
3553 }
3554 glSurfaceCircle(unit->pos, unitdef->maxCoverage, 40);
3555 }
3556 }
3557 }
3558
3559 // draw buildings we are about to build
3560 if ((inCommand >= 0) && ((size_t)inCommand < commands.size()) &&
3561 (commands[inCommand].type == CMDTYPE_ICON_BUILDING)) {
3562 { // limit the locking scope to avoid deadlock
3563 // draw build distance for all immobile builders during build commands
3564 const std::map<unsigned int, CBuilderCAI*>& builderCAIs = unitHandler->builderCAIs;
3565 std::map<unsigned int, CBuilderCAI*>::const_iterator bi;
3566
3567 for (bi = builderCAIs.begin(); bi != builderCAIs.end(); ++bi) {
3568 const CBuilderCAI* builderCAI = bi->second;
3569 const CUnit* builder = builderCAI->owner;
3570 const UnitDef* builderDef = builder->unitDef;
3571
3572 if ((builder == pointedAt) || (builder->team != gu->myTeam)) {
3573 continue;
3574 }
3575
3576 if (builderDef->builder && (!builderDef->canmove || selectedUnitsHandler.IsUnitSelected(builder))) {
3577 const float radius = builderDef->buildDistance;
3578 if (radius > 0.0f) {
3579 glDisable(GL_TEXTURE_2D);
3580 const float* color = cmdColors.rangeBuild;
3581 glColor4f(color[0], color[1], color[2], color[3] * 0.333f);
3582 glSurfaceCircle(builder->pos, radius, 40);
3583 }
3584 }
3585 }
3586 }
3587
3588 float dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
3589 if (dist > 0) {
3590 const UnitDef* unitdef = unitDefHandler->GetUnitDefByID(-commands[inCommand].id);
3591 if (unitdef) {
3592 // get the build information
3593 float3 pos = cameraPos+mouseDir*dist;
3594 std::vector<BuildInfo> buildPos;
3595 const CMouseHandler::ButtonPressEvt& bp = mouse->buttons[SDL_BUTTON_LEFT];
3596 if (GetQueueKeystate() && bp.pressed) {
3597 const float dist = CGround::LineGroundCol(bp.camPos, bp.camPos + bp.dir * globalRendering->viewRange * 1.4f, false);
3598 const float3 pos2 = bp.camPos + bp.dir * dist;
3599 buildPos = GetBuildPos(BuildInfo(unitdef, pos2, buildFacing),
3600 BuildInfo(unitdef, pos, buildFacing), cameraPos, mouseDir);
3601 } else {
3602 BuildInfo bi(unitdef, pos, buildFacing);
3603 buildPos = GetBuildPos(bi, bi, cameraPos, mouseDir);
3604 }
3605
3606 for (std::vector<BuildInfo>::iterator bpi = buildPos.begin(); bpi != buildPos.end(); ++bpi) {
3607 const float3& buildpos = bpi->pos;
3608 // draw weapon range
3609 if (!unitdef->weapons.empty()) {
3610 glDisable(GL_DEPTH_TEST);
3611 glColor4fv(cmdColors.rangeAttack);
3612 glBallisticCircle(buildpos, unitdef->weapons[0].def->range,
3613 NULL, 40, unitdef->weapons[0].def->heightmod);
3614 glEnable(GL_DEPTH_TEST);
3615 }
3616 // draw extraction range
3617 if (unitdef->extractRange > 0) {
3618 glColor4fv(cmdColors.rangeExtract);
3619 glSurfaceCircle(buildpos, unitdef->extractRange, 40);
3620 }
3621 // draw build range for immobile builders
3622 if (unitdef->builder) {
3623 const float radius = unitdef->buildDistance;
3624 if (radius > 0.0f) {
3625 glColor4fv(cmdColors.rangeBuild);
3626 glSurfaceCircle(buildpos, radius, 40);
3627 }
3628 }
3629 // draw shield range for immobile units
3630 if (unitdef->shieldWeaponDef) {
3631 glColor4fv(cmdColors.rangeShield);
3632 glSurfaceCircle(buildpos, unitdef->shieldWeaponDef->shieldRadius, 40);
3633 }
3634 // draw interceptor range
3635 const WeaponDef* wd = unitdef->stockpileWeaponDef;
3636 if ((wd != NULL) && wd->interceptor) {
3637 glColor4fv(cmdColors.rangeInterceptorOn);
3638 glSurfaceCircle(buildpos, wd->coverageRange, 40);
3639 }
3640 // draw sensor and jammer ranges
3641 if (unitdef->onoffable || unitdef->activateWhenBuilt) {
3642 const float3& p = buildpos;
3643 DrawSensorRange(unitdef->radarRadius, cmdColors.rangeRadar, p);
3644 DrawSensorRange(unitdef->sonarRadius, cmdColors.rangeSonar, p);
3645 DrawSensorRange(unitdef->seismicRadius, cmdColors.rangeSeismic, p);
3646 DrawSensorRange(unitdef->jammerRadius, cmdColors.rangeJammer, p);
3647 DrawSensorRange(unitdef->sonarJamRadius, cmdColors.rangeSonarJammer, p);
3648 }
3649
3650 std::vector<Command> cv;
3651 if (GetQueueKeystate()) {
3652
3653 Command c = bpi->CreateCommand();
3654 std::vector<Command> temp;
3655 CUnitSet::iterator ui = selectedUnitsHandler.selectedUnits.begin();
3656 for (; ui != selectedUnitsHandler.selectedUnits.end(); ++ui) {
3657 temp = (*ui)->commandAI->GetOverlapQueued(c);
3658 std::vector<Command>::iterator ti = temp.begin();
3659 for (; ti != temp.end(); ++ti) {
3660 cv.insert(cv.end(),*ti);
3661 }
3662 }
3663 }
3664 if (unitDrawer->ShowUnitBuildSquare(*bpi, cv)) {
3665 glColor4f(0.7f,1,1,0.4f);
3666 } else {
3667 glColor4f(1,0.5f,0.5f,0.4f);
3668 }
3669
3670 if (!onMinimap) {
3671 CUnitDrawer::DrawBuildingSample(bpi->def, gu->myTeam, buildpos, bpi->buildFacing);
3672
3673 glBlendFunc((GLenum)cmdColors.SelectedBlendSrc(), (GLenum)cmdColors.SelectedBlendDst());
3674 }
3675 }
3676 }
3677 }
3678 }
3679
3680 // draw range circles if attack orders are imminent
3681 int defcmd = GetDefaultCommand(mouse->lastx, mouse->lasty, cameraPos, mouseDir);
3682 if ((inCommand>=0 && (size_t)inCommand<commands.size() && commands[inCommand].id==CMD_ATTACK) ||
3683 (inCommand==-1 && defcmd>0 && commands[defcmd].id==CMD_ATTACK)
3684 ) {
3685 for(CUnitSet::iterator si=selectedUnitsHandler.selectedUnits.begin(); si!=selectedUnitsHandler.selectedUnits.end(); ++si) {
3686 CUnit* unit = *si;
3687 if (unit == pointedAt) {
3688 continue;
3689 }
3690 if (onMinimap && (unit->unitDef->speed > 0.0f)) {
3691 continue;
3692 }
3693 if(unit->maxRange>0 && ((unit->losStatus[gu->myAllyTeam] & LOS_INLOS) || gu->spectatingFullView)) {
3694 glDisable(GL_DEPTH_TEST);
3695 glColor4fv(cmdColors.rangeAttack);
3696 glBallisticCircle(unit->pos, unit->maxRange,
3697 unit->weapons.front(), 40);
3698 glEnable(GL_DEPTH_TEST);
3699 if (!onMinimap && gs->cheatEnabled && globalRendering->drawdebug) {
3700 DrawWeaponArc(unit);
3701 }
3702 }
3703 }
3704 }
3705
3706 glLineWidth(1.0f);
3707
3708 if (!onMinimap) {
3709 glDepthMask(GL_TRUE);
3710 glDisable(GL_BLEND);
3711 }
3712 }
3713
3714
DrawMiniMapMarker(const float3 & cameraPos)3715 void CGuiHandler::DrawMiniMapMarker(const float3& cameraPos)
3716 {
3717 const float w = 10.0f;
3718 const float h = 30.0f;
3719
3720 const float bc[4] = { 0.1f, 0.333f, 1.0f, 0.75f }; // base color
3721 const float d[8] = { 0.4f, 0.8f, 0.6f, 1.0f, 0.3f, 0.7f, 0.5f, 0.9f };
3722 const float colors[8][4] = {
3723 { bc[0] * d[0], bc[1] * d[0], bc[2] * d[0], bc[3] },
3724 { bc[0] * d[1], bc[1] * d[1], bc[2] * d[1], bc[3] },
3725 { bc[0] * d[2], bc[1] * d[2], bc[2] * d[2], bc[3] },
3726 { bc[0] * d[3], bc[1] * d[3], bc[2] * d[3], bc[3] },
3727 { bc[0] * d[4], bc[1] * d[4], bc[2] * d[4], bc[3] },
3728 { bc[0] * d[5], bc[1] * d[5], bc[2] * d[5], bc[3] },
3729 { bc[0] * d[6], bc[1] * d[6], bc[2] * d[6], bc[3] },
3730 { bc[0] * d[7], bc[1] * d[7], bc[2] * d[7], bc[3] },
3731 };
3732
3733 const float groundLevel = CGround::GetHeightAboveWater(cameraPos.x, cameraPos.z, false);
3734
3735 static float spinTime = 0.0f;
3736 spinTime = math::fmod(spinTime + globalRendering->lastFrameTime * 0.001f, 60.0f);
3737
3738 glPushMatrix();
3739 glTranslatef(cameraPos.x, groundLevel, cameraPos.z);
3740 glRotatef(360.0f * (spinTime / 2.0f), 0.0f, 1.0f, 0.0f);
3741
3742 glEnable(GL_BLEND);
3743 glShadeModel(GL_FLAT);
3744 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
3745 glBegin(GL_TRIANGLE_FAN);
3746 glVertex3f(0.0f, 0.0f, 0.0f);
3747 glVertex3f( +w, +h, 0.0f);
3748 glColor4fv(colors[4]); glVertex3f(0.0f, +h, +w);
3749 glColor4fv(colors[5]); glVertex3f( -w, +h, 0.0f);
3750 glColor4fv(colors[6]); glVertex3f(0.0f, +h, -w);
3751 glColor4fv(colors[7]); glVertex3f( +w, +h, 0.0f);
3752 glEnd();
3753 glBegin(GL_TRIANGLE_FAN);
3754 glVertex3f(0.0f, h * 2.0f, 0.0f);
3755 glVertex3f( +w, +h, 0.0f);
3756 glColor4fv(colors[3]); glVertex3f(0.0f, +h, -w);
3757 glColor4fv(colors[2]); glVertex3f( -w, +h, 0.0f);
3758 glColor4fv(colors[1]); glVertex3f(0.0f, +h, +w);
3759 glColor4fv(colors[0]); glVertex3f( +w, +h, 0.0f);
3760 glEnd();
3761 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3762 glShadeModel(GL_SMOOTH);
3763 glPopMatrix();
3764 }
3765
3766
DrawCentroidCursor()3767 void CGuiHandler::DrawCentroidCursor()
3768 {
3769 int cmd = -1;
3770 if ((inCommand >= 0) && ((size_t)inCommand < commands.size())) {
3771 cmd = commands[inCommand].id;
3772 } else {
3773 int defcmd;
3774 if (mouse->buttons[SDL_BUTTON_RIGHT].pressed &&
3775 ((activeReceiver == this) || (minimap->ProxyMode()))) {
3776 defcmd = defaultCmdMemory;
3777 } else {
3778 defcmd = GetDefaultCommand(mouse->lastx, mouse->lasty);
3779 }
3780 if ((defcmd >= 0) && ((size_t)defcmd < commands.size())) {
3781 cmd = commands[defcmd].id;
3782 }
3783 }
3784
3785 if ((cmd == CMD_MOVE) || (cmd == CMD_GATHERWAIT)) {
3786 if (!KeyInput::GetKeyModState(KMOD_CTRL) && !KeyInput::GetKeyModState(KMOD_ALT) && !KeyInput::GetKeyModState(KMOD_GUI)) {
3787 return;
3788 }
3789 } else if ((cmd == CMD_FIGHT) || (cmd == CMD_PATROL)) {
3790 if (!KeyInput::GetKeyModState(KMOD_CTRL)) {
3791 return;
3792 }
3793 } else {
3794 return;
3795 }
3796
3797 const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
3798 if (selUnits.size() < 2) {
3799 return;
3800 }
3801
3802 float3 pos;
3803 CUnitSet::const_iterator it;
3804 for (it = selUnits.begin(); it != selUnits.end(); ++it) {
3805 pos += (*it)->midPos;
3806 }
3807 pos /= (float)selUnits.size();
3808 const float3 winPos = camera->CalcWindowCoordinates(pos);
3809 if (winPos.z <= 1.0f) {
3810 const CMouseCursor* mc = mouse->FindCursor("Centroid");
3811 if (mc != NULL) {
3812 glDisable(GL_DEPTH_TEST);
3813 mc->Draw((int)winPos.x, globalRendering->viewSizeY - (int)winPos.y, 1.0f);
3814 glEnable(GL_DEPTH_TEST);
3815 }
3816 }
3817 }
3818
3819
DrawArea(float3 pos,float radius,const float * color)3820 void CGuiHandler::DrawArea(float3 pos, float radius, const float* color)
3821 {
3822 if (useStencil) {
3823 DrawSelectCircle(pos, radius, color);
3824 return;
3825 }
3826
3827 glDisable(GL_TEXTURE_2D);
3828 glEnable(GL_BLEND);
3829 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
3830 glColor4f(color[0], color[1], color[2], 0.25f);
3831
3832 glDisable(GL_DEPTH_TEST);
3833 glDisable(GL_FOG);
3834 glBegin(GL_TRIANGLE_FAN);
3835 glVertexf3(pos);
3836 for(int a=0;a<=40;++a){
3837 float3 p(fastmath::cos(a*2*PI/40)*radius,0,fastmath::sin(a*2*PI/40)*radius);
3838 p+=pos;
3839 p.y=CGround::GetHeightAboveWater(p.x, p.z, false);
3840 glVertexf3(p);
3841 }
3842 glEnd();
3843 glEnable(GL_DEPTH_TEST);
3844 glEnable(GL_FOG);
3845 }
3846
3847
DrawFront(int button,float maxSize,float sizeDiv,bool onMinimap,const float3 & cameraPos,const float3 & mouseDir)3848 void CGuiHandler::DrawFront(int button, float maxSize, float sizeDiv, bool onMinimap, const float3& cameraPos, const float3& mouseDir)
3849 {
3850 CMouseHandler::ButtonPressEvt& bp = mouse->buttons[button];
3851 if(bp.movement<5){
3852 return;
3853 }
3854 float dist = CGround::LineGroundCol(bp.camPos, bp.camPos + bp.dir * globalRendering->viewRange * 1.4f, false);
3855 if(dist<0){
3856 return;
3857 }
3858 float3 pos1=bp.camPos+bp.dir*dist;
3859 dist = CGround::LineGroundCol(cameraPos, cameraPos + mouseDir * globalRendering->viewRange * 1.4f, false);
3860 if(dist<0){
3861 return;
3862 }
3863 float3 pos2 = cameraPos + (mouseDir * dist);
3864
3865 ProcessFrontPositions(pos1, pos2);
3866
3867 float3 forward=(pos1-pos2).cross(UpVector);
3868 forward.ANormalize();
3869 float3 side=forward.cross(UpVector);
3870 if(pos1.SqDistance2D(pos2)>maxSize*maxSize){
3871 pos2=pos1+side*maxSize;
3872 pos2.y=CGround::GetHeightAboveWater(pos2.x, pos2.z, false);
3873 }
3874
3875 glColor4f(0.5f, 1.0f, 0.5f, 0.5f);
3876
3877 if (onMinimap) {
3878 pos1 += (pos1 - pos2);
3879 glLineWidth(2.0f);
3880 glBegin(GL_LINES);
3881 glVertexf3(pos1);
3882 glVertexf3(pos2);
3883 glEnd();
3884 return;
3885 }
3886
3887 glDisable(GL_TEXTURE_2D);
3888 glEnable(GL_BLEND);
3889 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3890
3891 glDisable(GL_DEPTH_TEST);
3892 glBegin(GL_QUADS);
3893 glVertexf3(pos1+side*25);
3894 glVertexf3(pos1-side*25);
3895 glVertexf3(pos1-side*25+forward*50);
3896 glVertexf3(pos1+side*25+forward*50);
3897
3898 glVertexf3(pos1+side*40+forward*50);
3899 glVertexf3(pos1-side*40+forward*50);
3900 glVertexf3(pos1+forward*100);
3901 glVertexf3(pos1+forward*100);
3902 glEnd();
3903 glEnable(GL_DEPTH_TEST);
3904
3905 pos1 += (pos1 - pos2);
3906 const int maxSteps = 256;
3907 const float frontLen = (pos1 - pos2).Length2D();
3908 const int steps = std::min(maxSteps, std::max(1, int(frontLen / 16.0f)));
3909
3910 glDisable(GL_FOG);
3911 glBegin(GL_QUAD_STRIP);
3912 const float3 delta = (pos2 - pos1) / (float)steps;
3913 for (int i = 0; i <= steps; i++) {
3914 float3 p;
3915 const float d = (float)i;
3916 p.x = pos1.x + (d * delta.x);
3917 p.z = pos1.z + (d * delta.z);
3918 p.y = CGround::GetHeightAboveWater(p.x, p.z, false);
3919 p.y -= 100.f; glVertexf3(p);
3920 p.y += 200.f; glVertexf3(p);
3921 }
3922 glEnd();
3923
3924 glEnable(GL_FOG);
3925 }
3926
3927
3928 /******************************************************************************/
3929 /******************************************************************************/
3930
3931 struct BoxData {
3932 float3 mins;
3933 float3 maxs;
3934 };
3935
3936
DrawBoxShape(const void * data)3937 static void DrawBoxShape(const void* data)
3938 {
3939 const BoxData* boxData = static_cast<const BoxData*>(data);
3940 const float3& mins = boxData->mins;
3941 const float3& maxs = boxData->maxs;
3942 glBegin(GL_QUADS);
3943 // the top
3944 glVertex3f(mins.x, maxs.y, mins.z);
3945 glVertex3f(mins.x, maxs.y, maxs.z);
3946 glVertex3f(maxs.x, maxs.y, maxs.z);
3947 glVertex3f(maxs.x, maxs.y, mins.z);
3948 // the bottom
3949 glVertex3f(mins.x, mins.y, mins.z);
3950 glVertex3f(maxs.x, mins.y, mins.z);
3951 glVertex3f(maxs.x, mins.y, maxs.z);
3952 glVertex3f(mins.x, mins.y, maxs.z);
3953 glEnd();
3954 glBegin(GL_QUAD_STRIP);
3955 // the sides
3956 glVertex3f(mins.x, maxs.y, mins.z);
3957 glVertex3f(mins.x, mins.y, mins.z);
3958 glVertex3f(mins.x, maxs.y, maxs.z);
3959 glVertex3f(mins.x, mins.y, maxs.z);
3960 glVertex3f(maxs.x, maxs.y, maxs.z);
3961 glVertex3f(maxs.x, mins.y, maxs.z);
3962 glVertex3f(maxs.x, maxs.y, mins.z);
3963 glVertex3f(maxs.x, mins.y, mins.z);
3964 glVertex3f(mins.x, maxs.y, mins.z);
3965 glVertex3f(mins.x, mins.y, mins.z);
3966 glEnd();
3967 }
3968
3969
DrawCornerPosts(const float3 & pos0,const float3 & pos1)3970 static void DrawCornerPosts(const float3& pos0, const float3& pos1)
3971 {
3972 const float3 lineVector(0.0f, 128.0f, 0.0f);
3973 const float3 corner0(pos0.x, CGround::GetHeightAboveWater(pos0.x, pos0.z, false), pos0.z);
3974 const float3 corner1(pos1.x, CGround::GetHeightAboveWater(pos1.x, pos1.z, false), pos1.z);
3975 const float3 corner2(pos0.x, CGround::GetHeightAboveWater(pos0.x, pos1.z, false), pos1.z);
3976 const float3 corner3(pos1.x, CGround::GetHeightAboveWater(pos1.x, pos0.z, false), pos0.z);
3977 glEnable(GL_BLEND);
3978 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3979 glLineWidth(2.0f);
3980 glBegin(GL_LINES);
3981 glColor4f(1.0f, 1.0f, 0.0f, 0.9f);
3982 glVertexf3(corner0); glVertexf3(corner0 + lineVector);
3983 glColor4f(0.0f, 1.0f, 0.0f, 0.9f);
3984 glVertexf3(corner1); glVertexf3(corner1 + lineVector);
3985 glColor4f(0.0f, 0.0f, 1.0f, 0.9f);
3986 glVertexf3(corner2); glVertexf3(corner2 + lineVector);
3987 glVertexf3(corner3); glVertexf3(corner3 + lineVector);
3988 glEnd();
3989 glLineWidth(1.0f);
3990 }
3991
3992
StencilDrawSelectBox(const float3 & pos0,const float3 & pos1,bool invColorSelect)3993 static void StencilDrawSelectBox(const float3& pos0, const float3& pos1,
3994 bool invColorSelect)
3995 {
3996 BoxData boxData;
3997 boxData.mins = float3(std::min(pos0.x, pos1.x), readMap->GetCurrMinHeight() - 250.0f, std::min(pos0.z, pos1.z));
3998 boxData.maxs = float3(std::max(pos0.x, pos1.x), readMap->GetCurrMaxHeight() + 10000.0f, std::max(pos0.z, pos1.z));
3999
4000 glDisable(GL_TEXTURE_2D);
4001 glDisable(GL_FOG);
4002
4003 glEnable(GL_BLEND);
4004
4005 if (!invColorSelect) {
4006 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
4007 glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
4008 glDrawVolume(DrawBoxShape, &boxData);
4009 } else {
4010 glEnable(GL_COLOR_LOGIC_OP);
4011 glLogicOp(GL_INVERT);
4012 glDrawVolume(DrawBoxShape, &boxData);
4013 glDisable(GL_COLOR_LOGIC_OP);
4014 }
4015
4016 DrawCornerPosts(pos0, pos1);
4017
4018 glEnable(GL_FOG);
4019 }
4020
4021
FullScreenDraw()4022 static void FullScreenDraw()
4023 {
4024 glMatrixMode(GL_PROJECTION);
4025 glPushMatrix();
4026 glLoadIdentity();
4027 glMatrixMode(GL_MODELVIEW);
4028 glLoadIdentity();
4029 glPushMatrix();
4030 glRectf(-1.0f, -1.0f, +1.0f, +1.0f);
4031 glMatrixMode(GL_PROJECTION);
4032 glPopMatrix();
4033 glMatrixMode(GL_MODELVIEW);
4034 glPopMatrix();
4035 }
4036
4037
DrawMinMaxBox(const float3 & mins,const float3 & maxs)4038 static void DrawMinMaxBox(const float3& mins, const float3& maxs)
4039 {
4040 glBegin(GL_QUADS);
4041 // the top
4042 glVertex3f(mins.x, maxs.y, mins.z);
4043 glVertex3f(maxs.x, maxs.y, mins.z);
4044 glVertex3f(maxs.x, maxs.y, maxs.z);
4045 glVertex3f(mins.x, maxs.y, maxs.z);
4046 glEnd();
4047 glBegin(GL_QUAD_STRIP);
4048 // the sides
4049 glVertex3f(mins.x, mins.y, mins.z);
4050 glVertex3f(mins.x, maxs.y, mins.z);
4051 glVertex3f(mins.x, mins.y, maxs.z);
4052 glVertex3f(mins.x, maxs.y, maxs.z);
4053 glVertex3f(maxs.x, mins.y, maxs.z);
4054 glVertex3f(maxs.x, maxs.y, maxs.z);
4055 glVertex3f(maxs.x, mins.y, mins.z);
4056 glVertex3f(maxs.x, maxs.y, mins.z);
4057 glVertex3f(mins.x, mins.y, mins.z);
4058 glVertex3f(mins.x, maxs.y, mins.z);
4059 glEnd();
4060 }
4061
4062
DrawSelectBox(const float3 & pos0,const float3 & pos1,const float3 & cameraPos)4063 void CGuiHandler::DrawSelectBox(const float3& pos0, const float3& pos1, const float3& cameraPos)
4064 {
4065 if (useStencil) {
4066 StencilDrawSelectBox(pos0, pos1, invColorSelect);
4067 return;
4068 }
4069
4070 const float3 mins(std::min(pos0.x, pos1.x), readMap->GetCurrMinHeight() - 250.0f, std::min(pos0.z, pos1.z));
4071 const float3 maxs(std::max(pos0.x, pos1.x), readMap->GetCurrMaxHeight() + 10000.0f, std::max(pos0.z, pos1.z));
4072
4073 glDisable(GL_TEXTURE_2D);
4074 glDisable(GL_FOG);
4075 glDisable(GL_BLEND);
4076
4077 glDepthMask(GL_FALSE);
4078 glEnable(GL_CULL_FACE);
4079
4080 glEnable(GL_COLOR_LOGIC_OP);
4081 glLogicOp(GL_INVERT);
4082
4083 // invert the color for objects within the box
4084 glCullFace(GL_FRONT); DrawMinMaxBox(mins, maxs);
4085 glCullFace(GL_BACK); DrawMinMaxBox(mins, maxs);
4086
4087 glDisable(GL_CULL_FACE);
4088
4089 // do a full screen inversion if the camera is within the box
4090 if ((cameraPos.x > mins.x) && (cameraPos.x < maxs.x) &&
4091 (cameraPos.y > mins.y) && (cameraPos.y < maxs.y) &&
4092 (cameraPos.z > mins.z) && (cameraPos.z < maxs.z)) {
4093 glDisable(GL_DEPTH_TEST);
4094 FullScreenDraw();
4095 glEnable(GL_DEPTH_TEST);
4096 }
4097
4098 glDisable(GL_COLOR_LOGIC_OP);
4099
4100 glEnable(GL_BLEND);
4101 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4102 DrawCornerPosts(pos0, pos1);
4103
4104 // glDepthMask(GL_TRUE);
4105 glEnable(GL_FOG);
4106 }
4107
4108
4109 /******************************************************************************/
4110
4111 struct CylinderData {
4112 int divs;
4113 float xc, zc, yp, yn;
4114 float radius;
4115 };
4116
4117
DrawCylinderShape(const void * data)4118 static void DrawCylinderShape(const void* data)
4119 {
4120 const CylinderData& cyl = *static_cast<const CylinderData*>(data);
4121 const float step = fastmath::PI2 / (float)cyl.divs;
4122 int i;
4123 glBegin(GL_QUAD_STRIP); // the sides
4124 for (i = 0; i <= cyl.divs; i++) {
4125 const float radians = step * float(i % cyl.divs);
4126 const float x = cyl.xc + (cyl.radius * fastmath::sin(radians));
4127 const float z = cyl.zc + (cyl.radius * fastmath::cos(radians));
4128 glVertex3f(x, cyl.yp, z);
4129 glVertex3f(x, cyl.yn, z);
4130 }
4131 glEnd();
4132 glBegin(GL_TRIANGLE_FAN); // the top
4133 for (i = 0; i < cyl.divs; i++) {
4134 const float radians = step * float(i);
4135 const float x = cyl.xc + (cyl.radius * fastmath::sin(radians));
4136 const float z = cyl.zc + (cyl.radius * fastmath::cos(radians));
4137 glVertex3f(x, cyl.yp, z);
4138 }
4139 glEnd();
4140 glBegin(GL_TRIANGLE_FAN); // the bottom
4141 for (i = (cyl.divs - 1); i >= 0; i--) {
4142 const float radians = step * float(i);
4143 const float x = cyl.xc + (cyl.radius * fastmath::sin(radians));
4144 const float z = cyl.zc + (cyl.radius * fastmath::cos(radians));
4145 glVertex3f(x, cyl.yn, z);
4146 }
4147 glEnd();
4148 }
4149
4150
DrawSelectCircle(const float3 & pos,float radius,const float * color)4151 void CGuiHandler::DrawSelectCircle(const float3& pos, float radius,
4152 const float* color)
4153 {
4154 CylinderData cylData;
4155 cylData.xc = pos.x;
4156 cylData.zc = pos.z;
4157 cylData.yp = readMap->GetCurrMaxHeight() + 10000.0f;
4158 cylData.yn = readMap->GetCurrMinHeight() - 250.0f;
4159 cylData.radius = radius;
4160 cylData.divs = 128;
4161
4162 glDisable(GL_TEXTURE_2D);
4163 glDisable(GL_FOG);
4164
4165 glEnable(GL_BLEND);
4166 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
4167 glColor4f(color[0], color[1], color[2], 0.25f);
4168
4169 glDrawVolume(DrawCylinderShape, &cylData);
4170
4171 // draw the center line
4172 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4173 glColor4f(color[0], color[1], color[2], 0.9f);
4174 glLineWidth(2.0f);
4175 const float3 base(pos.x, CGround::GetHeightAboveWater(pos.x, pos.z, false), pos.z);
4176 glBegin(GL_LINES);
4177 glVertexf3(base);
4178 glVertexf3(base + float3(0.0f, 128.0f, 0.0f));
4179 glEnd();
4180 glLineWidth(1.0f);
4181
4182 glEnable(GL_FOG);
4183 }
4184
4185
4186 /******************************************************************************/
4187 /******************************************************************************/
4188
SetBuildFacing(unsigned int facing)4189 void CGuiHandler::SetBuildFacing(unsigned int facing)
4190 {
4191 buildFacing = facing % NUM_FACINGS;
4192 }
4193
SetBuildSpacing(int spacing)4194 void CGuiHandler::SetBuildSpacing(int spacing)
4195 {
4196 buildSpacing = spacing;
4197 if (buildSpacing < 0)
4198 buildSpacing = 0;
4199 }
4200
4201 /******************************************************************************/
4202 /******************************************************************************/
4203