1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name botpanel.cpp - The bottom panel. */
12 //
13 // (c) Copyright 1999-2019 by Lutz Sammer, Vladi Belperchinov-Shabanski,
14 // Jimmy Salmon, cybermind and Andrettin
15 //
16 // This program is free software; you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation; only version 2 of the License.
19 //
20 // This program is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with this program; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 // 02111-1307, USA.
29 //
30
31 //@{
32
33 /*----------------------------------------------------------------------------
34 -- Includes
35 ----------------------------------------------------------------------------*/
36
37 #include "stratagus.h"
38
39 #include "ui/ui.h"
40
41 #include "actions.h"
42 //Wyrmgus start
43 #include "action/action_research.h"
44 #include "action/action_train.h"
45 #include "action/action_upgradeto.h"
46 #include "character.h"
47 //Wyrmgus end
48 #include "commands.h"
49 #include "font.h"
50 //Wyrmgus start
51 #include "grand_strategy.h"
52 //Wyrmgus end
53 #include "guichan/key.h"
54 #include "guichan/sdl/sdlinput.h"
55 #include "map/map.h"
56 #include "map/map_layer.h"
57 #include "map/tileset.h"
58 //Wyrmgus start
59 #include "network.h"
60 //Wyrmgus end
61 #include "player.h"
62 //Wyrmgus start
63 #include "quest.h"
64 //Wyrmgus end
65 #include "sound.h"
66 #include "spells.h"
67 #include "translate.h"
68 #include "trigger.h"
69 #include "ui/button_action.h"
70 #include "ui/button_level.h"
71 #include "ui/interface.h"
72 #include "ui/popup.h"
73 #include "unit/unit.h"
74 //Wyrmgus start
75 #include "unit/unit_manager.h"
76 //Wyrmgus end
77 #include "unit/unittype.h"
78 #include "unit/unit_type_variation.h"
79 #include "upgrade/dependency.h"
80 #include "upgrade/upgrade.h"
81 #include "video.h"
82
83 #include <ctype.h>
84 #include <vector>
85 #include <sstream>
86
87 /*----------------------------------------------------------------------------
88 -- Defines
89 ----------------------------------------------------------------------------*/
90
91 /*----------------------------------------------------------------------------
92 -- Variables
93 ----------------------------------------------------------------------------*/
94
95 /// Last drawn popup : used to speed up drawing
96 ButtonAction *LastDrawnButtonPopup;
97 /// for unit buttons sub-menus etc.
98 CButtonLevel *CurrentButtonLevel = nullptr;
99 /// All buttons for units
100 std::vector<ButtonAction *> UnitButtonTable;
101 /// Pointer to current buttons
102 std::vector<ButtonAction> CurrentButtons;
103
104 /*----------------------------------------------------------------------------
105 -- Functions
106 ----------------------------------------------------------------------------*/
107
108 /**
109 ** Initialize the buttons.
110 */
InitButtons()111 void InitButtons()
112 {
113 // Resolve the icon names.
114 for (size_t i = 0; i != UnitButtonTable.size(); ++i) {
115 //Wyrmgus start
116 // UnitButtonTable[i]->Icon.Load();
117 if (!UnitButtonTable[i]->Icon.Name.empty()) {
118 UnitButtonTable[i]->Icon.Load();
119 }
120 //Wyrmgus end
121 }
122 CurrentButtons.clear();
123 }
124
125 /*----------------------------------------------------------------------------
126 -- Buttons structures
127 ----------------------------------------------------------------------------*/
128
129 /**
130 ** FIXME: docu
131 */
AddButton(int pos,CButtonLevel * level,const std::string & icon_ident,ButtonCmd action,const std::string & value,void * actionCb,const ButtonCheckFunc func,const std::string & allow,const int key,const std::string & hint,const std::string & descr,const std::string & sound,const std::string & cursor,const std::string & umask,const std::string & popup,bool alwaysShow,const std::string & mod_file)132 int AddButton(int pos, CButtonLevel *level, const std::string &icon_ident,
133 ButtonCmd action, const std::string &value, void* actionCb, const ButtonCheckFunc func,
134 const std::string &allow, const int key, const std::string &hint, const std::string &descr,
135 const std::string &sound, const std::string &cursor, const std::string &umask,
136 //Wyrmgus start
137 // const std::string &popup, bool alwaysShow)
138 const std::string &popup, bool alwaysShow, const std::string &mod_file)
139 //Wyrmgus end
140 {
141 std::string buf;
142 ButtonAction *ba = new ButtonAction;
143 Assert(ba);
144
145 ba->Pos = pos;
146 ba->Level = level;
147 ba->AlwaysShow = alwaysShow;
148 ba->Icon.Name = icon_ident;
149 ba->Payload = actionCb;
150 // FIXME: check if already initited
151 //ba->Icon.Load();
152 ba->Action = action;
153 if (!value.empty()) {
154 ba->ValueStr = value;
155 switch (action) {
156 case ButtonSpellCast:
157 ba->Value = CSpell::GetSpell(value)->Slot;
158 #ifdef DEBUG
159 if (ba->Value < 0) {
160 DebugPrint("Spell %s does not exist?\n" _C_ value.c_str());
161 Assert(ba->Value >= 0);
162 }
163 #endif
164 break;
165 case ButtonTrain:
166 ba->Value = UnitTypeIdByIdent(value);
167 break;
168 case ButtonResearch:
169 ba->Value = UpgradeIdByIdent(value);
170 break;
171 //Wyrmgus start
172 case ButtonLearnAbility:
173 ba->Value = UpgradeIdByIdent(value);
174 break;
175 case ButtonExperienceUpgradeTo:
176 ba->Value = UnitTypeIdByIdent(value);
177 break;
178 //Wyrmgus end
179 case ButtonUpgradeTo:
180 ba->Value = UnitTypeIdByIdent(value);
181 break;
182 case ButtonBuild:
183 ba->Value = UnitTypeIdByIdent(value);
184 break;
185 //Wyrmgus start
186 case ButtonProduceResource:
187 ba->Value = GetResourceIdByName(value.c_str());
188 break;
189 case ButtonSellResource:
190 ba->Value = GetResourceIdByName(value.c_str());
191 break;
192 case ButtonBuyResource:
193 ba->Value = GetResourceIdByName(value.c_str());
194 break;
195 //Wyrmgus end
196 case ButtonButton:
197 if (CButtonLevel::GetButtonLevel(value)) {
198 ba->Value = CButtonLevel::GetButtonLevel(value)->ID;
199 } else {
200 ba->Value = 0;
201 }
202 break;
203 default:
204 ba->Value = atoi(value.c_str());
205 break;
206 }
207 } else {
208 ba->ValueStr.clear();
209 ba->Value = 0;
210 }
211
212 ba->Allowed = func;
213 ba->AllowStr = allow;
214 ba->Key = key;
215 ba->Hint = hint;
216 ba->Description = descr;
217 ba->CommentSound.Name = sound;
218 if (!ba->CommentSound.Name.empty()) {
219 ba->CommentSound.MapSound();
220 }
221 if (!ba->Popup.empty()) {
222 CPopup *popup = PopupByIdent(ba->Popup);
223 if (!popup) {
224 fprintf(stderr, "Popup \"%s\" hasn't defined.\n ", ba->Popup.c_str());
225 Exit(1);
226 }
227 }
228 ba->ButtonCursor = cursor;
229 ba->Popup = popup;
230 // FIXME: here should be added costs to the hint
231 // FIXME: johns: show should be nice done?
232 if (umask[0] == '*') {
233 buf = umask;
234 } else {
235 buf = "," + umask + ",";
236 }
237 ba->UnitMask = buf;
238 //Wyrmgus start
239 ba->Mod = mod_file;
240 //Wyrmgus end
241 UnitButtonTable.push_back(ba);
242 // FIXME: check if already initited
243 //Assert(ba->Icon.Icon != nullptr);// just checks, that's why at the end
244 return 1;
245 }
246
247
248 /**
249 ** Cleanup buttons.
250 */
CleanButtons()251 void CleanButtons()
252 {
253 // Free the allocated buttons.
254 for (size_t i = 0; i != UnitButtonTable.size(); ++i) {
255 delete UnitButtonTable[i];
256 }
257 UnitButtonTable.clear();
258
259 CurrentButtonLevel = nullptr;
260 LastDrawnButtonPopup = nullptr;
261 CurrentButtons.clear();
262 }
263
264 /**
265 ** Return Status of button.
266 **
267 ** @param button button to check status
268 ** @param UnderCursor Current Button Under Cursor
269 **
270 ** @return status of button
271 ** @return Icon(Active | Selected | Clicked | AutoCast | Disabled).
272 **
273 ** @todo FIXME : add IconDisabled when needed.
274 ** @todo FIXME : Should show the rally action for training unit ? (NewOrder)
275 */
GetButtonStatus(const ButtonAction & button,int UnderCursor)276 static int GetButtonStatus(const ButtonAction &button, int UnderCursor)
277 {
278 unsigned int res = 0;
279
280 /* parallel drawing */
281 if (Selected.empty()) {
282 return res;
283 }
284
285 // cursor is on that button
286 if (ButtonAreaUnderCursor == ButtonAreaButton && UnderCursor == button.Pos - 1) {
287 res |= IconActive;
288 if (MouseButtons & LeftButton) {
289 // Overwrite IconActive.
290 res = IconClicked;
291 }
292 }
293
294 //Wyrmgus start
295 res |= IconCommandButton;
296
297 if (button.Action == ButtonProduceResource) {
298 size_t i;
299 for (i = 0; i < Selected.size(); ++i) {
300 if (Selected[i]->GivesResource != button.Value) {
301 break;
302 }
303 }
304 if (i == Selected.size()) {
305 res |= IconSelected;
306 }
307 }
308 //Wyrmgus end
309
310 unsigned int action = UnitActionNone;
311 switch (button.Action) {
312 case ButtonStop:
313 action = UnitActionStill;
314 break;
315 case ButtonStandGround:
316 action = UnitActionStandGround;
317 break;
318 case ButtonAttack:
319 action = UnitActionAttack;
320 break;
321 case ButtonAttackGround:
322 action = UnitActionAttackGround;
323 break;
324 case ButtonPatrol:
325 action = UnitActionPatrol;
326 break;
327 case ButtonHarvest:
328 case ButtonReturn:
329 action = UnitActionResource;
330 break;
331 default:
332 break;
333 }
334 // Simple case.
335 if (action != UnitActionNone) {
336 for (size_t i = 0; i != Selected.size(); ++i) {
337 if (Selected[i]->CurrentAction() != action) {
338 return res;
339 }
340 }
341 res |= IconSelected;
342 return res;
343 }
344 // other cases : manage AutoCast and different possible action.
345 size_t i;
346 switch (button.Action) {
347 case ButtonMove:
348 for (i = 0; i < Selected.size(); ++i) {
349 int saction = Selected[i]->CurrentAction();
350 if (saction != UnitActionMove &&
351 saction != UnitActionBuild &&
352 saction != UnitActionFollow &&
353 //Wyrmgus start
354 saction != UnitActionPickUp &&
355 //Wyrmgus end
356 saction != UnitActionDefend) {
357 break;
358 }
359 }
360 if (i == Selected.size()) {
361 res |= IconSelected;
362 }
363 break;
364 case ButtonSpellCast:
365 // FIXME : and IconSelected ?
366
367 // Autocast
368 for (i = 0; i < Selected.size(); ++i) {
369 Assert(Selected[i]->AutoCastSpell);
370 if (Selected[i]->AutoCastSpell[button.Value] != 1) {
371 break;
372 }
373 }
374 if (i == Selected.size()) {
375 res |= IconAutoCast;
376 }
377 break;
378 case ButtonRepair:
379 for (i = 0; i < Selected.size(); ++i) {
380 if (Selected[i]->CurrentAction() != UnitActionRepair) {
381 break;
382 }
383 }
384 if (i == Selected.size()) {
385 res |= IconSelected;
386 }
387 // Auto repair
388 for (i = 0; i < Selected.size(); ++i) {
389 if (Selected[i]->AutoRepair != 1) {
390 break;
391 }
392 }
393 if (i == Selected.size()) {
394 res |= IconAutoCast;
395 }
396 break;
397 // FIXME: must handle more actions
398 case ButtonSellResource:
399 if (std::find(ThisPlayer->AutosellResources.begin(), ThisPlayer->AutosellResources.end(), button.Value) != ThisPlayer->AutosellResources.end()) {
400 res |= IconAutoCast;
401 }
402 default:
403 break;
404 }
405 return res;
406 }
407
408 /**
409 ** Tell if we can show the popup content.
410 ** verify each sub condition for that.
411 **
412 ** @param condition condition to verify.
413 ** @param unit unit that certain condition can refer.
414 **
415 ** @return 0 if we can't show the content, else 1.
416 */
CanShowPopupContent(const PopupConditionPanel * condition,const ButtonAction & button,CUnitType * type)417 static bool CanShowPopupContent(const PopupConditionPanel *condition,
418 const ButtonAction &button,
419 CUnitType *type)
420 {
421 if (!condition) {
422 return true;
423 }
424
425 if (condition->HasHint && button.GetHint().empty()) {
426 return false;
427 }
428
429 if (condition->HasDescription && button.Description.empty()) {
430 return false;
431 }
432
433 if (condition->HasDependencies && PrintDependencies(*ThisPlayer, button).empty()) {
434 return false;
435 }
436
437 //Wyrmgus start
438 if (condition->Class && type && type->Class == -1 && !(type->BoolFlag[ITEM_INDEX].value && type->ItemClass != -1)) {
439 return false;
440 }
441
442 if (condition->UnitTypeClass != -1) {
443 if (!type || condition->UnitTypeClass != type->Class) {
444 return false;
445 }
446 }
447
448 if (condition->UnitTypeType != -1) {
449 if (!type || condition->UnitTypeType != type->UnitType) {
450 return false;
451 }
452 }
453
454 if (condition->CanStore != -1) {
455 if (!type || !type->CanStore[condition->CanStore]) {
456 return false;
457 }
458 }
459
460 if (condition->ImproveIncome != -1) {
461 if (!type || type->Stats[ThisPlayer->Index].ImproveIncomes[condition->ImproveIncome] <= CResource::Resources[condition->ImproveIncome]->DefaultIncome) {
462 return false;
463 }
464 }
465
466 if (condition->ChildResources != CONDITION_TRUE) {
467 if ((condition->ChildResources == CONDITION_ONLY) ^ (CResource::Resources[button.Value]->ChildResources.size() > 0)) {
468 return false;
469 }
470 }
471
472 if (condition->ImproveIncomes != CONDITION_TRUE) {
473 bool improve_incomes = false;
474 if (button.Action == ButtonProduceResource) {
475 if (ThisPlayer->Incomes[button.Value] > CResource::Resources[button.Value]->DefaultIncome) {
476 improve_incomes = true;
477 }
478 for (const CResource *child_resource : CResource::Resources[button.Value]->ChildResources) {
479 if (ThisPlayer->Incomes[child_resource->ID] > child_resource->DefaultIncome) {
480 improve_incomes = true;
481 break;
482 }
483 }
484 } else {
485 if (!type) {
486 return false;
487 }
488 for (int i = 1; i < MaxCosts; ++i) {
489 if (type->Stats[ThisPlayer->Index].ImproveIncomes[i] > CResource::Resources[i]->DefaultIncome) {
490 improve_incomes = true;
491 break;
492 }
493 }
494 }
495 if ((condition->ImproveIncomes == CONDITION_ONLY) ^ improve_incomes) {
496 return false;
497 }
498 }
499
500 if (condition->Description && type && type->Description.empty()) {
501 return false;
502 }
503
504 if (condition->Quote && type && type->Quote.empty() && !((button.Action == ButtonUnit || button.Action == ButtonBuy) && UnitManager.GetSlotUnit(button.Value).Unique && !UnitManager.GetSlotUnit(button.Value).Unique->Quote.empty()) && !((button.Action == ButtonUnit || button.Action == ButtonBuy) && UnitManager.GetSlotUnit(button.Value).Work != nullptr && !UnitManager.GetSlotUnit(button.Value).Work->Quote.empty() && UnitManager.GetSlotUnit(button.Value).Elixir != nullptr && !UnitManager.GetSlotUnit(button.Value).Elixir->Quote.empty())) {
505 return false;
506 }
507
508 if (condition->Encyclopedia && type && type->Description.empty() && type->Background.empty() && type->Quote.empty() && (!type->BoolFlag[ITEM_INDEX].value || type->ItemClass == -1)) {
509 return false;
510 }
511
512 if (condition->SettlementName && !(button.Action == ButtonUnit && UnitManager.GetSlotUnit(button.Value).Settlement)) {
513 return false;
514 }
515
516 if (condition->CanActiveHarvest && !(button.Action == ButtonUnit && Selected.size() > 0 && Selected[0]->CanHarvest(&UnitManager.GetSlotUnit(button.Value), false))) {
517 return false;
518 }
519
520 if (condition->FactionUpgrade != CONDITION_TRUE) {
521 if ((condition->FactionUpgrade == CONDITION_ONLY) ^ (button.Action == ButtonFaction)) {
522 return false;
523 }
524 }
525
526 if (condition->FactionCoreSettlements != CONDITION_TRUE) {
527 if ((condition->FactionCoreSettlements == CONDITION_ONLY) ^ (CurrentCampaign != nullptr && button.Action == ButtonFaction && button.Value != -1 && PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[button.Value]->Cores.size() > 0)) {
528 return false;
529 }
530 }
531
532 CUpgrade *upgrade = nullptr;
533 if (button.Action == ButtonResearch || button.Action == ButtonLearnAbility) {
534 upgrade = AllUpgrades[button.Value];
535 } else if (button.Action == ButtonFaction && !PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[button.Value]->FactionUpgrade.empty()) {
536 upgrade = CUpgrade::Get(PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[button.Value]->FactionUpgrade);
537 }
538
539 if (condition->UpgradeResearched != CONDITION_TRUE) {
540 if ((condition->UpgradeResearched == CONDITION_ONLY) ^ ((((button.Action == ButtonResearch || button.Action == ButtonFaction) && UpgradeIdAllowed(*ThisPlayer, upgrade->ID) == 'R') || (button.Action == ButtonLearnAbility && Selected[0]->GetIndividualUpgrade(upgrade) >= upgrade->MaxLimit)))) {
541 return false;
542 }
543 }
544
545 if (condition->ResearchedUpgrade) {
546 if (UpgradeIdAllowed(*ThisPlayer, condition->ResearchedUpgrade->ID) != 'R') {
547 return false;
548 }
549 }
550
551 if (condition->ResearchedUpgradeClass != -1) {
552 if (!ThisPlayer->HasUpgradeClass(condition->ResearchedUpgradeClass)) {
553 return false;
554 }
555 }
556
557 if (condition->Ability != CONDITION_TRUE) {
558 if ((condition->Ability == CONDITION_ONLY) ^ (upgrade && upgrade->Ability)) {
559 return false;
560 }
561 }
562
563 if (condition->LuxuryResource != CONDITION_TRUE) {
564 if ((condition->LuxuryResource == CONDITION_ONLY) ^ (button.Action == ButtonProduceResource && CResource::Resources[button.Value]->LuxuryResource)) {
565 return false;
566 }
567 }
568
569 if (condition->RequirementsString != CONDITION_TRUE) {
570 if ((condition->RequirementsString == CONDITION_ONLY) ^ ((button.Action == ButtonResearch || button.Action == ButtonLearnAbility || button.Action == ButtonFaction || button.Action == ButtonTrain || button.Action == ButtonBuild || button.Action == ButtonUpgradeTo || button.Action == ButtonBuy) && !IsButtonUsable(*Selected[0], button) && Selected[0]->Player == ThisPlayer && ((type && !type->RequirementsString.empty()) || ((button.Action == ButtonResearch || button.Action == ButtonLearnAbility || button.Action == ButtonFaction) && !upgrade->RequirementsString.empty())))) {
571 return false;
572 }
573 }
574
575 if (condition->ExperienceRequirementsString != CONDITION_TRUE) {
576 if ((condition->ExperienceRequirementsString == CONDITION_ONLY) ^ (button.Action == ButtonExperienceUpgradeTo && !IsButtonUsable(*Selected[0], button) && type && !type->ExperienceRequirementsString.empty())) {
577 return false;
578 }
579 }
580
581 if (condition->BuildingRulesString != CONDITION_TRUE) {
582 if ((condition->BuildingRulesString == CONDITION_ONLY) ^ (button.Action == ButtonBuild && type && !type->BuildingRulesString.empty())) {
583 return false;
584 }
585 }
586 //Wyrmgus end
587
588 if (condition->ButtonAction != -1 && button.Action != condition->ButtonAction) {
589 return false;
590 }
591
592 if (condition->ButtonValue.empty() == false && button.ValueStr != condition->ButtonValue) {
593 return false;
594 }
595
596 if (type && condition->BoolFlags && !type->CheckUserBoolFlags(condition->BoolFlags)) {
597 return false;
598 }
599
600 //Wyrmgus start
601 // if (condition->Variables && type) {
602 if (condition->Variables && type && button.Action != ButtonUnit && button.Action != ButtonBuy) {
603 //Wyrmgus end
604 for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
605 if (condition->Variables[i] != CONDITION_TRUE) {
606 if ((condition->Variables[i] == CONDITION_ONLY) ^ type->Stats[ThisPlayer->Index].Variables[i].Enable) {
607 return false;
608 }
609 }
610 }
611 //Wyrmgus start
612 } else if (condition->Variables && (button.Action == ButtonUnit || button.Action == ButtonBuy)) {
613 for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); ++i) {
614 if (condition->Variables[i] != CONDITION_TRUE) {
615 // if ((condition->Variables[i] == CONDITION_ONLY) ^ UnitManager.GetSlotUnit(button.Value).Variable[i].Enable) {
616 CUnit &unit = UnitManager.GetSlotUnit(button.Value);
617 if (unit.Type->BoolFlag[ITEM_INDEX].value && unit.Container != nullptr && unit.Container->HasInventory()) {
618 if (i == BASICDAMAGE_INDEX) {
619 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.Container->GetItemVariableChange(&unit, i) != 0 || unit.Container->GetItemVariableChange(&unit, PIERCINGDAMAGE_INDEX) != 0)) {
620 return false;
621 }
622 } else {
623 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.Container->GetItemVariableChange(&unit, i) != 0)) { //the former for some reason wasn't working with negative values
624 return false;
625 }
626 }
627 } else if (unit.Work || unit.Elixir) { //special case for literary works and elixirs that aren't in an inventory
628 if (i == BASICDAMAGE_INDEX) {
629 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.GetItemVariableChange(&unit, i) != 0 || unit.GetItemVariableChange(&unit, PIERCINGDAMAGE_INDEX) != 0)) {
630 return false;
631 }
632 } else {
633 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.GetItemVariableChange(&unit, i) != 0)) { //the former for some reason wasn't working with negative values
634 return false;
635 }
636 }
637 } else {
638 if (i == BASICDAMAGE_INDEX) {
639 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.Variable[i].Value != 0 || unit.Variable[PIERCINGDAMAGE_INDEX].Value != 0)) {
640 return false;
641 }
642 } else {
643 if ((condition->Variables[i] == CONDITION_ONLY) ^ (unit.Variable[i].Value != 0)) { //the former for some reason wasn't working with negative values
644 return false;
645 }
646 }
647 }
648 }
649 }
650 //Wyrmgus end
651 }
652
653 //Wyrmgus start
654 if (button.Action == ButtonSpellCast) {
655 if (condition->AutoCast != CONDITION_TRUE) {
656 if ((condition->AutoCast == CONDITION_ONLY) ^ (CSpell::Spells[button.Value]->AutoCast != nullptr)) {
657 return false;
658 }
659 }
660 }
661
662 if (button.Action == ButtonUnit || button.Action == ButtonBuy) {
663 CUnit &unit = UnitManager.GetSlotUnit(button.Value);
664 if (unit.Type->BoolFlag[ITEM_INDEX].value) {
665 if (condition->Equipped != CONDITION_TRUE) {
666 if ((condition->Equipped == CONDITION_ONLY) ^ (unit.Container != nullptr && unit.Container->HasInventory() && unit.Container->IsItemEquipped(&unit))) {
667 return false;
668 }
669 }
670 if (condition->Equippable != CONDITION_TRUE) {
671 if ((condition->Equippable == CONDITION_ONLY) ^ (unit.Container != nullptr && unit.Container->HasInventory() && unit.Container->CanEquipItem(&unit))) {
672 return false;
673 }
674 }
675 if (condition->Consumable != CONDITION_TRUE) {
676 if ((condition->Consumable == CONDITION_ONLY) ^ IsItemClassConsumable(unit.Type->ItemClass)) {
677 return false;
678 }
679 }
680 if (condition->Spell != CONDITION_TRUE) {
681 if ((condition->Spell == CONDITION_ONLY) ^ (unit.Spell != nullptr)) {
682 return false;
683 }
684 }
685 if (condition->Work != CONDITION_TRUE) {
686 if ((condition->Work == CONDITION_ONLY) ^ (unit.Work != nullptr)) {
687 return false;
688 }
689 }
690 if (condition->ReadWork != CONDITION_TRUE) {
691 if ((condition->ReadWork == CONDITION_ONLY) ^ (unit.Work != nullptr && (unit.Container != nullptr && unit.Container->HasInventory() && unit.Container->GetIndividualUpgrade(unit.Work)))) {
692 return false;
693 }
694 }
695 if (condition->Elixir != CONDITION_TRUE) {
696 if ((condition->Elixir == CONDITION_ONLY) ^ (unit.Elixir != nullptr)) {
697 return false;
698 }
699 }
700 if (condition->ConsumedElixir != CONDITION_TRUE) {
701 if ((condition->ConsumedElixir == CONDITION_ONLY) ^ (unit.Elixir != nullptr && (unit.Container != nullptr && unit.Container->HasInventory() && unit.Container->GetIndividualUpgrade(unit.Elixir)))) {
702 return false;
703 }
704 }
705 if (condition->CanUse != CONDITION_TRUE) {
706 if ((condition->CanUse == CONDITION_ONLY) ^ (unit.Container != nullptr && unit.Container->HasInventory() && unit.Container->CanUseItem(&unit))) {
707 return false;
708 }
709 }
710 if (condition->Bound != CONDITION_TRUE) {
711 if ((condition->Bound == CONDITION_ONLY) ^ unit.Bound) {
712 return false;
713 }
714 }
715 if (condition->Identified != CONDITION_TRUE) {
716 if ((condition->Identified == CONDITION_ONLY) ^ unit.Identified) {
717 return false;
718 }
719 }
720 if (condition->Weapon != CONDITION_TRUE) {
721 if ((condition->Weapon == CONDITION_ONLY) ^ (GetItemClassSlot(unit.Type->ItemClass) == WeaponItemSlot)) {
722 return false;
723 }
724 }
725 if (condition->Shield != CONDITION_TRUE) {
726 if ((condition->Shield == CONDITION_ONLY) ^ (GetItemClassSlot(unit.Type->ItemClass) == ShieldItemSlot)) {
727 return false;
728 }
729 }
730 if (condition->Boots != CONDITION_TRUE) {
731 if ((condition->Boots == CONDITION_ONLY) ^ (GetItemClassSlot(unit.Type->ItemClass) == BootsItemSlot)) {
732 return false;
733 }
734 }
735 if (condition->Arrows != CONDITION_TRUE) {
736 if ((condition->Arrows == CONDITION_ONLY) ^ (GetItemClassSlot(unit.Type->ItemClass) == ArrowsItemSlot)) {
737 return false;
738 }
739 }
740 if (condition->ItemClass != -1) {
741 if (condition->ItemClass != unit.Type->ItemClass) {
742 return false;
743 }
744 }
745 if (condition->Regeneration != CONDITION_TRUE) {
746 if (unit.Container != nullptr && unit.Container->HasInventory()) {
747 if ((condition->Regeneration == CONDITION_ONLY) ^ (unit.Container->GetItemVariableChange(&unit, HITPOINTBONUS_INDEX, true) != 0)) {
748 return false;
749 }
750 } else if (unit.Work || unit.Elixir) { //special case for literary works and elixirs that aren't in an inventory
751 if ((condition->Regeneration == CONDITION_ONLY) ^ (unit.GetItemVariableChange(&unit, HP_INDEX, true) != 0 || unit.GetItemVariableChange(&unit, HITPOINTBONUS_INDEX, true) != 0)) {
752 return false;
753 }
754 } else {
755 if ((condition->Regeneration == CONDITION_ONLY) ^ (unit.Variable[HP_INDEX].Increase != 0 || unit.Variable[HITPOINTBONUS_INDEX].Increase != 0)) {
756 return false;
757 }
758 }
759 }
760 }
761
762 if (!(button.Action == ButtonBuy && unit.Character) && condition->Opponent != CONDITION_TRUE) {
763 if ((condition->Opponent == CONDITION_ONLY) ^ ThisPlayer->IsEnemy(unit)) {
764 return false;
765 }
766 }
767 if (!(button.Action == ButtonBuy && unit.Character) && condition->Neutral != CONDITION_TRUE) {
768 if ((condition->Neutral == CONDITION_ONLY) ^ (!ThisPlayer->IsEnemy(unit) && !ThisPlayer->IsAllied(unit) && ThisPlayer != unit.Player && (unit.Container == nullptr || (!ThisPlayer->IsEnemy(*unit.Container) && !ThisPlayer->IsAllied(*unit.Container) && ThisPlayer != unit.Container->Player)))) {
769 return false;
770 }
771 }
772
773 if (condition->Affixed != CONDITION_TRUE) {
774 if ((condition->Affixed == CONDITION_ONLY) ^ (unit.Prefix != nullptr || unit.Suffix != nullptr)) {
775 return false;
776 }
777 }
778 if (condition->Unique != CONDITION_TRUE) {
779 if ((condition->Unique == CONDITION_ONLY) ^ (unit.Unique || unit.Character != nullptr)) {
780 return false;
781 }
782 }
783 if (condition->UniqueSet != CONDITION_TRUE) {
784 if ((condition->UniqueSet == CONDITION_ONLY) ^ (unit.Unique && unit.Unique->Set)) {
785 return false;
786 }
787 }
788 } else { // always return false for "Affixed" and "Unique" for buttons that aren't individual unit buttons
789 if (condition->Affixed != CONDITION_TRUE) {
790 if (condition->Affixed == CONDITION_ONLY) {
791 return false;
792 }
793 }
794 if (condition->Unique != CONDITION_TRUE) {
795 if (condition->Unique == CONDITION_ONLY) {
796 return false;
797 }
798 }
799 }
800 //Wyrmgus end
801
802 return true;
803 }
804
GetPopupSize(const CPopup & popup,const ButtonAction & button,int & popupWidth,int & popupHeight,int * Costs)805 static void GetPopupSize(const CPopup &popup, const ButtonAction &button,
806 int &popupWidth, int &popupHeight, int *Costs)
807 {
808 int contentWidth = popup.MarginX;
809 int contentHeight = 0;
810 int maxContentWidth = 0;
811 int maxContentHeight = 0;
812 popupWidth = popup.MarginX;
813 popupHeight = popup.MarginY;
814
815 for (std::vector<CPopupContentType *>::const_iterator it = popup.Contents.begin();
816 it != popup.Contents.end();
817 ++it) {
818 CPopupContentType &content = **it;
819
820 //Wyrmgus start
821 // if (CanShowPopupContent(content.Condition, button, UnitTypes[button.Value])) {
822 if (
823 (button.Action != ButtonUnit && button.Action != ButtonBuy && CanShowPopupContent(content.Condition, button, UnitTypes[button.Value]))
824 || ((button.Action == ButtonUnit || button.Action == ButtonBuy) && CanShowPopupContent(content.Condition, button, UnitTypes[UnitManager.GetSlotUnit(button.Value).Type->Slot]))
825 ) {
826 //Wyrmgus end
827 // Automatically write the calculated coordinates.
828 content.pos.x = contentWidth + content.MarginX;
829 content.pos.y = popupHeight + content.MarginY;
830
831 contentWidth += std::max(content.minSize.x, 2 * content.MarginX + content.GetWidth(button, Costs));
832 contentHeight = std::max(content.minSize.y, 2 * content.MarginY + content.GetHeight(button, Costs));
833 maxContentHeight = std::max(contentHeight, maxContentHeight);
834 if (content.Wrap) {
835 popupWidth += std::max(0, contentWidth - maxContentWidth);
836 popupHeight += maxContentHeight;
837 maxContentWidth = std::max(maxContentWidth, contentWidth);
838 contentWidth = popup.MarginX;
839 maxContentHeight = 0;
840 }
841 }
842 }
843
844 popupWidth += popup.MarginX;
845 popupHeight += popup.MarginY;
846 }
847
848 static struct PopupDrawCache {
849 int popupWidth;
850 int popupHeight;
851 } popupCache;
852
853 /**
854 ** Draw popup
855 */
DrawPopup(const ButtonAction & button,int x,int y,bool above)856 void DrawPopup(const ButtonAction &button, int x, int y, bool above)
857 {
858 CPopup *popup = PopupByIdent(button.Popup);
859 bool useCache = false;
860
861 if (!popup) {
862 return;
863 } else if (&button == LastDrawnButtonPopup) {
864 useCache = true;
865 } else {
866 LastDrawnButtonPopup = const_cast<ButtonAction *>(&button);
867 }
868
869 int popupWidth, popupHeight;
870 int Costs[ManaResCost + 1];
871 memset(Costs, 0, sizeof(Costs));
872
873 switch (button.Action) {
874 case ButtonResearch:
875 //Wyrmgus start
876 // memcpy(Costs, AllUpgrades[button.Value]->Costs, sizeof(AllUpgrades[button.Value]->Costs));
877 ThisPlayer->GetUpgradeCosts(AllUpgrades[button.Value], Costs);
878 //Wyrmgus end
879 break;
880 case ButtonSpellCast:
881 memcpy(Costs, CSpell::Spells[button.Value]->Costs, sizeof(CSpell::Spells[button.Value]->Costs));
882 Costs[ManaResCost] = CSpell::Spells[button.Value]->ManaCost;
883 break;
884 case ButtonBuild:
885 case ButtonTrain:
886 case ButtonUpgradeTo:
887 //Wyrmgus start
888 // memcpy(Costs, UnitTypes[button.Value]->Stats[ThisPlayer->Index].Costs,
889 // sizeof(UnitTypes[button.Value]->Stats[ThisPlayer->Index].Costs));
890 // Costs[FoodCost] = UnitTypes[button.Value]->Stats[ThisPlayer->Index].Variables[DEMAND_INDEX].Value;
891 int type_costs[MaxCosts];
892 ThisPlayer->GetUnitTypeCosts(UnitTypes[button.Value], type_costs, Selected[0]->Type->Stats[Selected[0]->Player->Index].GetUnitStock(UnitTypes[button.Value]) != 0);
893 memcpy(Costs, type_costs, sizeof(type_costs));
894 Costs[FoodCost] = UnitTypes[button.Value]->Stats[ThisPlayer->Index].Variables[DEMAND_INDEX].Value;
895 //Wyrmgus end
896 break;
897 //Wyrmgus start
898 case ButtonBuy:
899 Costs[FoodCost] = UnitManager.GetSlotUnit(button.Value).Variable[DEMAND_INDEX].Value;
900 Costs[CopperCost] = UnitManager.GetSlotUnit(button.Value).GetPrice();
901 break;
902 //Wyrmgus end
903 default:
904 break;
905 }
906
907 if (useCache) {
908 popupWidth = popupCache.popupWidth;
909 popupHeight = popupCache.popupHeight;
910 } else {
911 GetPopupSize(*popup, button, popupWidth, popupHeight, Costs);
912 popupWidth = std::max(popupWidth, popup->MinWidth);
913 popupHeight = std::max(popupHeight, popup->MinHeight);
914 popupCache.popupWidth = popupWidth;
915 popupCache.popupHeight = popupHeight;
916 }
917
918 x = std::min<int>(x, Video.Width - 1 - popupWidth);
919 clamp<int>(&x, 0, Video.Width - 1);
920 if (above) {
921 y = y - popupHeight - 10;
922 } else { //below
923 y = y + 10;
924 }
925 clamp<int>(&y, 0, Video.Height - 1);
926
927 // Background
928 Video.FillTransRectangle(popup->BackgroundColor, x, y, popupWidth, popupHeight, popup->BackgroundColor >> ASHIFT);
929 Video.DrawRectangle(popup->BorderColor, x, y, popupWidth, popupHeight);
930
931 // Contents
932 for (std::vector<CPopupContentType *>::const_iterator it = popup->Contents.begin();
933 it != popup->Contents.end(); ++it) {
934 const CPopupContentType &content = **it;
935
936 //Wyrmgus start
937 // if (CanShowPopupContent(content.Condition, button, UnitTypes[button.Value])) {
938 if (
939 (button.Action != ButtonUnit && button.Action != ButtonBuy && CanShowPopupContent(content.Condition, button, UnitTypes[button.Value]))
940 || ((button.Action == ButtonUnit || button.Action == ButtonBuy) && CanShowPopupContent(content.Condition, button, UnitTypes[UnitManager.GetSlotUnit(button.Value).Type->Slot]))
941 ) {
942 //Wyrmgus end
943 content.Draw(x + content.pos.x, y + content.pos.y, *popup, popupWidth, button, Costs);
944 }
945 }
946 }
947
948 //Wyrmgus start
949 /**
950 ** Draw popup
951 */
DrawGenericPopup(const std::string & popup_text,int x,int y,std::string text_color,std::string highlight_color,bool above)952 void DrawGenericPopup(const std::string &popup_text, int x, int y, std::string text_color, std::string highlight_color, bool above)
953 {
954 const CFont &font = GetGameFont();
955
956 int MaxWidth = std::max(512, Video.Width / 5);
957
958 int i;
959
960 //calculate content width
961 int content_width = 0;
962 std::string content_width_sub;
963 i = 1;
964 while (!(content_width_sub = GetLineFont(i++, popup_text, 0, &font)).empty()) {
965 int line_width = font.getWidth(content_width_sub);
966 int cost_symbol_pos = content_width_sub.find("COST_", 0);
967 if (cost_symbol_pos != std::string::npos) {
968 int res = std::stoi(content_width_sub.substr(cost_symbol_pos + 5, content_width_sub.find(" ", cost_symbol_pos) - (cost_symbol_pos + 5) + 1));
969 line_width -= font.getWidth("COST_" + std::to_string((long long) res));
970 line_width += UI.Resources[res].G->Width;
971 }
972 content_width = std::max(content_width, line_width);
973 }
974
975 if (MaxWidth) {
976 content_width = std::min(content_width, MaxWidth);
977 }
978
979 //calculate content height
980 int content_height = 0;
981 i = 1;
982 while ((GetLineFont(i++, popup_text, MaxWidth, &font)).length()) {
983 content_height += font.Height() + 2;
984 }
985
986 int popupWidth, popupHeight;
987
988 int contentWidth = MARGIN_X;
989 int contentHeight = 0;
990 int maxContentWidth = 0;
991 int maxContentHeight = 0;
992 popupWidth = MARGIN_X;
993 popupHeight = MARGIN_Y;
994 PixelPos pos(0, 0);
995
996 bool wrap = true;
997
998 // Automatically write the calculated coordinates.
999 pos.x = contentWidth + MARGIN_X;
1000 pos.y = popupHeight + MARGIN_Y;
1001
1002 contentWidth += std::max(0, 2 * MARGIN_X + content_width);
1003 contentHeight = std::max(0, 2 * MARGIN_Y + content_height);
1004 maxContentHeight = std::max(contentHeight, maxContentHeight);
1005 if (wrap) {
1006 popupWidth += std::max(0, contentWidth - maxContentWidth);
1007 popupHeight += maxContentHeight;
1008 maxContentWidth = std::max(maxContentWidth, contentWidth);
1009 contentWidth = MARGIN_X;
1010 maxContentHeight = 0;
1011 }
1012
1013 popupWidth += MARGIN_X;
1014 popupHeight += MARGIN_Y;
1015
1016 popupWidth = std::min(popupWidth, MaxWidth);
1017 popupHeight = std::max(popupHeight, 0);
1018
1019 x = std::min<int>(x, Video.Width - 1 - popupWidth);
1020 clamp<int>(&x, 0, Video.Width - 1);
1021 if (above) {
1022 y = y - popupHeight - 10;
1023 } else { //below
1024 y = y + 10;
1025 }
1026 clamp<int>(&y, 0, Video.Height - 1);
1027
1028 // Background
1029 IntColor BackgroundColor = Video.MapRGBA(TheScreen->format, 28, 28, 28, 208);
1030 IntColor BorderColor = Video.MapRGBA(TheScreen->format, 93, 93, 93, 160);
1031 #if defined(USE_OPENGL) || defined(USE_GLES)
1032 if (UseOpenGL) {
1033 Video.FillTransRectangle(BackgroundColor, x, y, popupWidth, popupHeight, BackgroundColor >> ASHIFT);
1034 } else
1035 #endif
1036 {
1037 Video.FillTransRectangle(BackgroundColor, x, y, popupWidth, popupHeight, 208);
1038 }
1039 Video.DrawRectangle(BorderColor, x, y, popupWidth, popupHeight);
1040
1041 if (text_color.empty()) {
1042 text_color = "white";
1043 }
1044 if (highlight_color.empty()) {
1045 highlight_color = "yellow";
1046 }
1047
1048 // Contents
1049 x += pos.x;
1050 y += pos.y;
1051 CLabel label(font, text_color, highlight_color);
1052 std::string sub;
1053 i = 0;
1054 int y_off = y;
1055 unsigned int width = MaxWidth
1056 ? std::min(MaxWidth, popupWidth - 2 * MARGIN_X)
1057 : 0;
1058 while ((sub = GetLineFont(++i, popup_text, width, &font)).length()) {
1059 if (sub.find("LINE", 0) != std::string::npos) {
1060 Video.FillRectangle(BorderColor, x - MARGIN_X + 1 - MARGIN_X,
1061 y_off, popupWidth - 2, 1);
1062 sub = sub.substr(sub.find("LINE", 0) + 4, sub.length());
1063 }
1064 int cost_symbol_pos = sub.find("COST_", 0);
1065 if (cost_symbol_pos != std::string::npos) {
1066 int x_offset = 0;
1067 int res = std::stoi(sub.substr(cost_symbol_pos + 5, sub.find(" ", cost_symbol_pos) - (cost_symbol_pos + 5) + 1));
1068 std::string sub_first = sub.substr(0, cost_symbol_pos);
1069 std::string sub_second = sub.substr(cost_symbol_pos + 5 + std::to_string((long long) res).length(), sub.length() - cost_symbol_pos - (5 + std::to_string((long long) res).length()));
1070 label.Draw(x, y_off, sub_first);
1071 x_offset += font.getWidth(sub_first);
1072 UI.Resources[res].G->DrawFrameClip(UI.Resources[res].IconFrame, x + x_offset, y + ((font.getHeight() - UI.Resources[res].G->Height) / 2), true);
1073 x_offset += UI.Resources[res].G->Width;
1074 label.Draw(x + x_offset, y_off, sub_second);
1075 } else {
1076 label.Draw(x, y_off, sub);
1077 }
1078 y_off += font.Height() + 2;
1079 }
1080 }
1081 //Wyrmgus end
1082
1083 /**
1084 ** Draw button panel.
1085 **
1086 ** Draw all action buttons.
1087 */
Draw()1088 void CButtonPanel::Draw()
1089 {
1090 // Draw background
1091 if (UI.ButtonPanel.G) {
1092 UI.ButtonPanel.G->DrawSubClip(0, 0,
1093 UI.ButtonPanel.G->Width, UI.ButtonPanel.G->Height,
1094 UI.ButtonPanel.X, UI.ButtonPanel.Y);
1095 }
1096
1097 // No buttons
1098 if (CurrentButtons.empty()) {
1099 return;
1100 }
1101 std::vector<ButtonAction> &buttons(CurrentButtons);
1102
1103 Assert(!Selected.empty());
1104 char buf[8];
1105
1106 // Draw all buttons.
1107 for (int i = 0; i < (int) UI.ButtonPanel.Buttons.size(); ++i) {
1108 if (buttons[i].Pos == -1) {
1109 continue;
1110 }
1111 Assert(buttons[i].Pos == i + 1);
1112 //Wyrmgus start
1113 //for neutral units, don't draw buttons that aren't training buttons (in other words, only draw buttons which are usable by neutral buildings)
1114 if (
1115 !buttons[i].AlwaysShow
1116 && Selected[0]->Player != ThisPlayer
1117 && !ThisPlayer->IsTeamed(*Selected[0])
1118 && ThisPlayer->HasBuildingAccess(*Selected[0]->Player, buttons[i].Action)
1119 && !IsNeutralUsableButtonAction(buttons[i].Action)
1120 ) {
1121 continue;
1122 }
1123 //Wyrmgus end
1124 bool gray = false;
1125 bool cooldownSpell = false;
1126 int maxCooldown = 0;
1127 for (size_t j = 0; j != Selected.size(); ++j) {
1128 if (!IsButtonAllowed(*Selected[j], buttons[i])) {
1129 gray = true;
1130 break;
1131 } else if (buttons[i].Action == ButtonSpellCast
1132 && (*Selected[j]).SpellCoolDownTimers[CSpell::Spells[buttons[i].Value]->Slot]) {
1133 Assert(CSpell::Spells[buttons[i].Value]->CoolDown > 0);
1134 cooldownSpell = true;
1135 maxCooldown = std::max(maxCooldown, (*Selected[j]).SpellCoolDownTimers[CSpell::Spells[buttons[i].Value]->Slot]);
1136 }
1137 }
1138 //
1139 // Tutorial show command key in icons
1140 //
1141 if (ShowCommandKey) {
1142 //Wyrmgus start
1143 // if (buttons[i].Key == gcn::Key::K_ESCAPE) {
1144 if (buttons[i].GetKey() == gcn::Key::K_ESCAPE) {
1145 //Wyrmgus end
1146 //Wyrmgus start
1147 // strcpy_s(buf, sizeof(buf), "ESC");
1148 strcpy_s(buf, sizeof(buf), "Esc");
1149 //Wyrmgus end
1150 //Wyrmgus start
1151 } else if (buttons[i].GetKey() == gcn::Key::K_PAGE_UP) {
1152 strcpy_s(buf, sizeof(buf), "PgUp");
1153 } else if (buttons[i].GetKey() == gcn::Key::K_PAGE_DOWN) {
1154 strcpy_s(buf, sizeof(buf), "PgDwn");
1155 } else if (buttons[i].GetKey() == gcn::Key::K_DELETE) {
1156 strcpy_s(buf, sizeof(buf), "Del");
1157 //Wyrmgus end
1158 } else {
1159 //Wyrmgus start
1160 // buf[0] = toupper(buttons[i].Key);
1161 buf[0] = toupper(buttons[i].GetKey());
1162 //Wyrmgus end
1163 buf[1] = '\0';
1164 }
1165 } else {
1166 buf[0] = '\0';
1167 }
1168
1169 //
1170 // Draw main Icon.
1171 //
1172 const PixelPos pos(UI.ButtonPanel.Buttons[i].X, UI.ButtonPanel.Buttons[i].Y);
1173
1174 //Wyrmgus start
1175 CIcon *button_icon = buttons[i].Icon.Icon;
1176
1177 // if there is a single unit selected, show the icon of its weapon/shield/boots/arrows equipped for the appropriate buttons
1178 if (buttons[i].Icon.Name.empty() && buttons[i].Action == ButtonAttack && Selected[0]->Type->CanTransport() && Selected[0]->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->BoardCount > 0 && Selected[0]->UnitInside != nullptr && Selected[0]->UnitInside->Type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value && Selected[0]->UnitInside->GetButtonIcon(buttons[i].Action) != nullptr) {
1179 button_icon = Selected[0]->UnitInside->GetButtonIcon(buttons[i].Action);
1180 } else if (buttons[i].Icon.Name.empty() && Selected[0]->GetButtonIcon(buttons[i].Action) != nullptr) {
1181 button_icon = Selected[0]->GetButtonIcon(buttons[i].Action);
1182 } else if (buttons[i].Action == ButtonExperienceUpgradeTo && Selected[0]->GetVariation() && UnitTypes[buttons[i].Value]->GetVariation(Selected[0]->GetVariation()->VariationId) != nullptr && !UnitTypes[buttons[i].Value]->GetVariation(Selected[0]->GetVariation()->VariationId)->Icon.Name.empty()) {
1183 button_icon = UnitTypes[buttons[i].Value]->GetVariation(Selected[0]->GetVariation()->VariationId)->Icon.Icon;
1184 } else if ((buttons[i].Action == ButtonTrain || buttons[i].Action == ButtonBuild || buttons[i].Action == ButtonUpgradeTo || buttons[i].Action == ButtonExperienceUpgradeTo) && buttons[i].Icon.Name.empty() && UnitTypes[buttons[i].Value]->GetDefaultVariation(*ThisPlayer) != nullptr && !UnitTypes[buttons[i].Value]->GetDefaultVariation(*ThisPlayer)->Icon.Name.empty()) {
1185 button_icon = UnitTypes[buttons[i].Value]->GetDefaultVariation(*ThisPlayer)->Icon.Icon;
1186 } else if ((buttons[i].Action == ButtonTrain || buttons[i].Action == ButtonBuild || buttons[i].Action == ButtonUpgradeTo || buttons[i].Action == ButtonExperienceUpgradeTo) && buttons[i].Icon.Name.empty() && !UnitTypes[buttons[i].Value]->Icon.Name.empty()) {
1187 button_icon = UnitTypes[buttons[i].Value]->Icon.Icon;
1188 } else if (buttons[i].Action == ButtonBuy) {
1189 button_icon = UnitManager.GetSlotUnit(buttons[i].Value).GetIcon().Icon;
1190 } else if (buttons[i].Action == ButtonResearch && buttons[i].Icon.Name.empty() && AllUpgrades[buttons[i].Value]->Icon) {
1191 button_icon = AllUpgrades[buttons[i].Value]->Icon;
1192 } else if (buttons[i].Action == ButtonFaction && buttons[i].Icon.Name.empty() && !PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[buttons[i].Value]->Icon.Name.empty()) {
1193 button_icon = PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[buttons[i].Value]->Icon.Icon;
1194 }
1195 //Wyrmgus end
1196
1197 if (cooldownSpell) {
1198 //Wyrmgus start
1199 // buttons[i].Icon.Icon->DrawCooldownSpellIcon(pos,
1200 button_icon->DrawCooldownSpellIcon(pos,
1201 //Wyrmgus end
1202 maxCooldown * 100 / CSpell::Spells[buttons[i].Value]->CoolDown);
1203 } else if (gray) {
1204 //Wyrmgus start
1205 // buttons[i].Icon.Icon->DrawGrayscaleIcon(pos);
1206 // button_icon->DrawGrayscaleIcon(pos); //better to not show it
1207 //Wyrmgus end
1208 } else {
1209 int player = -1;
1210 if (Selected.empty() == false && Selected[0]->IsAlive()) {
1211 //Wyrmgus start
1212 // player = Selected[0]->RescuedFrom ? Selected[0]->RescuedFrom->Index : Selected[0]->Player->Index;
1213 player = Selected[0]->GetDisplayPlayer();
1214
1215 //if is accessing a building of another player, set color to that of the person player (i.e. for training buttons)
1216 if (ThisPlayer->HasBuildingAccess(Players[player], buttons[i].Action)) {
1217 player = ThisPlayer->Index;
1218 }
1219 //Wyrmgus end
1220 }
1221
1222 if (IsButtonUsable(*Selected[0], buttons[i])) {
1223 button_icon->DrawUnitIcon(*UI.ButtonPanel.Buttons[i].Style,
1224 GetButtonStatus(buttons[i], ButtonUnderCursor),
1225 pos, buf, player, false, false, 100 - GetButtonCooldownPercent(*Selected[0], buttons[i]));
1226
1227 if (
1228 (buttons[i].Action == ButtonTrain && Selected[0]->Type->Stats[Selected[0]->Player->Index].GetUnitStock(UnitTypes[buttons[i].Value]) != 0)
1229 || buttons[i].Action == ButtonSellResource || buttons[i].Action == ButtonBuyResource
1230 ) {
1231 std::string number_string;
1232 if (buttons[i].Action == ButtonTrain && Selected[0]->Type->Stats[Selected[0]->Player->Index].GetUnitStock(UnitTypes[buttons[i].Value]) != 0) { //draw the quantity in stock for unit "training" cases which have it
1233 number_string = std::to_string((long long) Selected[0]->GetUnitStock(UnitTypes[buttons[i].Value])) + "/" + std::to_string((long long) Selected[0]->Type->Stats[Selected[0]->Player->Index].GetUnitStock(UnitTypes[buttons[i].Value]));
1234 } else if (buttons[i].Action == ButtonSellResource) {
1235 number_string = std::to_string((long long) Selected[0]->Player->GetEffectiveResourceSellPrice(buttons[i].Value));
1236 } else if (buttons[i].Action == ButtonBuyResource) {
1237 number_string = std::to_string((long long) Selected[0]->Player->GetEffectiveResourceBuyPrice(buttons[i].Value));
1238 }
1239 std::string oldnc;
1240 std::string oldrc;
1241 GetDefaultTextColors(oldnc, oldrc);
1242 CLabel label(GetGameFont(), oldnc, oldrc);
1243
1244 label.Draw(pos.x + 46 - GetGameFont().Width(number_string), pos.y + 0, number_string);
1245 }
1246 } else if ( //draw researched technologies (or acquired abilities) grayed
1247 buttons[i].Action == ButtonResearch && UpgradeIdentAllowed(*ThisPlayer, buttons[i].ValueStr) == 'R'
1248 || (buttons[i].Action == ButtonLearnAbility && Selected[0]->GetIndividualUpgrade(CUpgrade::Get(buttons[i].ValueStr)) == CUpgrade::Get(buttons[i].ValueStr)->MaxLimit)
1249 ) {
1250 button_icon->DrawUnitIcon(*UI.ButtonPanel.Buttons[i].Style,
1251 GetButtonStatus(buttons[i], ButtonUnderCursor),
1252 pos, buf, player, false, true);
1253 } else {
1254 button_icon->DrawUnitIcon(*UI.ButtonPanel.Buttons[i].Style,
1255 GetButtonStatus(buttons[i], ButtonUnderCursor),
1256 pos, buf, player, true);
1257 }
1258 }
1259 }
1260 //
1261 // Update status line for this button and draw popups
1262 //
1263 //Wyrmgus start
1264 /*
1265 for (int i = 0; i < (int) UI.ButtonPanel.Buttons.size(); ++i) {
1266 if (ButtonAreaUnderCursor == ButtonAreaButton &&
1267 //Wyrmgus start
1268 // ButtonUnderCursor == i && KeyState != KeyStateInput) {
1269 ButtonUnderCursor == i && KeyState != KeyStateInput
1270 && buttons[i].Level == CurrentButtonLevel) {
1271 //Wyrmgus end
1272 if (!Preference.NoStatusLineTooltips) {
1273 UpdateStatusLineForButton(buttons[i]);
1274 }
1275 DrawPopup(buttons[i], UI.ButtonPanel.Buttons[i].X,
1276 UI.ButtonPanel.Buttons[i].Y);
1277 }
1278 }
1279 */
1280 //Wyrmgus end
1281
1282 //Wyrmgus start
1283 if (ButtonAreaUnderCursor == ButtonAreaTransporting) {
1284 CUnit *uins = Selected[0]->UnitInside;
1285 size_t j = 0;
1286
1287 for (int i = 0; i < Selected[0]->InsideCount; ++i, uins = uins->NextContained) {
1288 if (!uins->Boarded || j >= UI.TransportingButtons.size() || (Selected[0]->Player != ThisPlayer && uins->Player != ThisPlayer)) {
1289 continue;
1290 }
1291 if (static_cast<size_t>(ButtonUnderCursor) == j) {
1292 std::string text_color;
1293 if (uins->Unique || uins->Character != nullptr) {
1294 text_color = "fire";
1295 } else if (uins->Prefix != nullptr || uins->Suffix != nullptr) {
1296 text_color = "light-blue";
1297 }
1298 DrawGenericPopup(uins->GetMessageName(), UI.TransportingButtons[j].X, UI.TransportingButtons[j].Y, text_color);
1299 }
1300 ++j;
1301 }
1302 }
1303 //Wyrmgus end
1304 }
1305
1306 /**
1307 ** Update the status line with hints from the button
1308 **
1309 ** @param button Button
1310 */
UpdateStatusLineForButton(const ButtonAction & button)1311 void UpdateStatusLineForButton(const ButtonAction &button)
1312 {
1313 //Wyrmgus start
1314 // UI.StatusLine.Set(button.Hint);
1315 UI.StatusLine.Set(button.GetHint());
1316 //Wyrmgus end
1317 switch (button.Action) {
1318 case ButtonBuild:
1319 case ButtonTrain:
1320 case ButtonUpgradeTo: {
1321 // FIXME: store pointer in button table!
1322 //Wyrmgus start
1323 // const CUnitStats &stats = UnitTypes[button.Value]->Stats[ThisPlayer->Index];
1324 // UI.StatusLine.SetCosts(0, stats.Variables[DEMAND_INDEX].Value, stats.Costs);
1325 int type_costs[MaxCosts];
1326 ThisPlayer->GetUnitTypeCosts(UnitTypes[button.Value], type_costs, Selected[0]->Type->Stats[Selected[0]->Player->Index].GetUnitStock(UnitTypes[button.Value]) != 0);
1327 UI.StatusLine.SetCosts(0, UnitTypes[button.Value]->Stats[ThisPlayer->Index].Variables[DEMAND_INDEX].Value * (UnitTypes[button.Value]->TrainQuantity ? UnitTypes[button.Value]->TrainQuantity : 1), type_costs);
1328 //Wyrmgus end
1329 break;
1330 }
1331 case ButtonResearch:
1332 UI.StatusLine.SetCosts(0, 0, AllUpgrades[button.Value]->Costs);
1333 break;
1334 case ButtonSpellCast:
1335 UI.StatusLine.SetCosts(CSpell::Spells[button.Value]->ManaCost, 0, CSpell::Spells[button.Value]->Costs);
1336 break;
1337 default:
1338 UI.StatusLine.ClearCosts();
1339 break;
1340 }
1341 }
1342 /*----------------------------------------------------------------------------
1343 -- Functions
1344 ----------------------------------------------------------------------------*/
1345
1346 /**
1347 ** Check if the button is allowed for the unit.
1348 **
1349 ** @param unit unit which checks for allow.
1350 ** @param buttonaction button to check if it is allowed.
1351 **
1352 ** @return 1 if button is allowed, 0 else.
1353 **
1354 ** @todo FIXME: better check. (dependency, resource, ...)
1355 ** @todo FIXME: make difference with impossible and not yet researched.
1356 */
IsButtonAllowed(const CUnit & unit,const ButtonAction & buttonaction)1357 bool IsButtonAllowed(const CUnit &unit, const ButtonAction &buttonaction)
1358 {
1359 if (buttonaction.AlwaysShow) {
1360 return true;
1361 }
1362
1363 bool res = false;
1364 if (buttonaction.Allowed) {
1365 res = buttonaction.Allowed(unit, buttonaction);
1366 if (!res) {
1367 return false;
1368 } else {
1369 res = false;
1370 }
1371 }
1372
1373 //Wyrmgus start
1374 if (!ThisPlayer->IsTeamed(*Selected[0]) && (!ThisPlayer->HasBuildingAccess(*Selected[0]->Player, buttonaction.Action) || !IsNeutralUsableButtonAction(buttonaction.Action))) {
1375 return false;
1376 }
1377 //Wyrmgus end
1378
1379 // Check button-specific cases
1380 switch (buttonaction.Action) {
1381 case ButtonStop:
1382 case ButtonStandGround:
1383 case ButtonButton:
1384 case ButtonMove:
1385 case ButtonCallbackAction:
1386 //Wyrmgus start
1387 case ButtonRallyPoint:
1388 case ButtonUnit:
1389 case ButtonEditorUnit:
1390 case ButtonProduceResource:
1391 case ButtonSellResource:
1392 case ButtonBuyResource:
1393 case ButtonSalvage:
1394 case ButtonEnterMapLayer:
1395 //Wyrmgus end
1396 res = true;
1397 break;
1398 case ButtonRepair:
1399 res = unit.Type->RepairRange > 0;
1400 break;
1401 case ButtonPatrol:
1402 res = unit.CanMove();
1403 break;
1404 case ButtonHarvest:
1405 if (!unit.CurrentResource
1406 || !(unit.ResourcesHeld > 0 && !unit.Type->ResInfo[unit.CurrentResource]->LoseResources)
1407 || (unit.ResourcesHeld != unit.Type->ResInfo[unit.CurrentResource]->ResourceCapacity
1408 && unit.Type->ResInfo[unit.CurrentResource]->LoseResources)) {
1409 res = true;
1410 }
1411 //Wyrmgus start
1412 if (unit.BoardCount) {
1413 res = false;
1414 }
1415 //Wyrmgus end
1416 break;
1417 case ButtonReturn:
1418 if (!(!unit.CurrentResource
1419 || !(unit.ResourcesHeld > 0 && !unit.Type->ResInfo[unit.CurrentResource]->LoseResources)
1420 || (unit.ResourcesHeld != unit.Type->ResInfo[unit.CurrentResource]->ResourceCapacity
1421 && unit.Type->ResInfo[unit.CurrentResource]->LoseResources))) {
1422 res = true;
1423 }
1424 break;
1425 case ButtonAttack:
1426 res = ButtonCheckAttack(unit, buttonaction);
1427 break;
1428 case ButtonAttackGround:
1429 if (unit.Type->BoolFlag[GROUNDATTACK_INDEX].value) {
1430 res = true;
1431 }
1432 break;
1433 case ButtonTrain:
1434 // Check if building queue is enabled
1435 if (!EnableTrainingQueue && unit.CurrentAction() == UnitActionTrain) {
1436 break;
1437 }
1438 if (unit.Player->Index == PlayerNumNeutral && !unit.CanHireMercenary(UnitTypes[buttonaction.Value])) {
1439 break;
1440 }
1441 // FALL THROUGH
1442 case ButtonUpgradeTo:
1443 case ButtonResearch:
1444 case ButtonBuild:
1445 if (buttonaction.Action == ButtonResearch) {
1446 res = CheckDependencies(AllUpgrades[buttonaction.Value], unit.Player, false, true, !ThisPlayer->IsTeamed(unit));
1447 if (res) {
1448 //Wyrmgus start
1449 // res = UpgradeIdentAllowed(*unit.Player, buttonaction.ValueStr) == 'A';
1450 res = (UpgradeIdentAllowed(*ThisPlayer, buttonaction.ValueStr) == 'A' || UpgradeIdentAllowed(*ThisPlayer, buttonaction.ValueStr) == 'R') && CheckDependencies(AllUpgrades[buttonaction.Value], ThisPlayer, false, true); //also check for the dependencies for this player (rather than the unit) as an extra for researches, so that the player doesn't research too advanced technologies at neutral buildings
1451 res = res && (!unit.Player->UpgradeTimers.Upgrades[UpgradeIdByIdent(buttonaction.ValueStr)] || unit.Player->UpgradeTimers.Upgrades[UpgradeIdByIdent(buttonaction.ValueStr)] == AllUpgrades[UpgradeIdByIdent(buttonaction.ValueStr)]->Costs[TimeCost]); //don't show if is being researched elsewhere
1452 //Wyrmgus end
1453 }
1454 } else {
1455 res = CheckDependencies(UnitTypes[buttonaction.Value], unit.Player, false, true, !ThisPlayer->IsTeamed(unit));
1456 }
1457 break;
1458 case ButtonExperienceUpgradeTo:
1459 res = CheckDependencies(UnitTypes[buttonaction.Value], &unit, true, true);
1460 if (res && unit.Character != nullptr) {
1461 res = std::find(unit.Character->ForbiddenUpgrades.begin(), unit.Character->ForbiddenUpgrades.end(), UnitTypes[buttonaction.Value]) == unit.Character->ForbiddenUpgrades.end();
1462 }
1463 break;
1464 case ButtonLearnAbility:
1465 res = unit.CanLearnAbility(CUpgrade::Get(buttonaction.ValueStr), true);
1466 break;
1467 case ButtonSpellCast:
1468 res = CSpell::Spells[buttonaction.Value]->IsAvailableForUnit(unit);
1469 break;
1470 case ButtonUnload:
1471 res = (Selected[0]->Type->CanTransport() && Selected[0]->BoardCount);
1472 break;
1473 case ButtonCancel:
1474 res = true;
1475 break;
1476 case ButtonCancelUpgrade:
1477 //Wyrmgus start
1478 // res = unit.CurrentAction() == UnitActionUpgradeTo
1479 // || unit.CurrentAction() == UnitActionResearch;
1480 //don't show the cancel button for a quick moment if the time cost is 0
1481 res = (unit.CurrentAction() == UnitActionUpgradeTo && static_cast<COrder_UpgradeTo *>(unit.CurrentOrder())->GetUnitType().Stats[unit.Player->Index].Costs[TimeCost] > 0)
1482 || (unit.CurrentAction() == UnitActionResearch && static_cast<COrder_Research *>(unit.CurrentOrder())->GetUpgrade().Costs[TimeCost] > 0);
1483 //Wyrmgus end
1484 break;
1485 case ButtonCancelTrain:
1486 //Wyrmgus start
1487 // res = unit.CurrentAction() == UnitActionTrain;
1488 res = unit.CurrentAction() == UnitActionTrain && static_cast<COrder_Train *>(unit.CurrentOrder())->GetUnitType().Stats[unit.Player->Index].Costs[TimeCost] > 0; //don't show the cancel button for a quick moment if the time cost is 0
1489 //Wyrmgus end
1490 break;
1491 case ButtonCancelBuild:
1492 res = unit.CurrentAction() == UnitActionBuilt;
1493 break;
1494 //Wyrmgus start
1495 case ButtonFaction:
1496 res = ThisPlayer->Faction != -1 && buttonaction.Value != -1 && buttonaction.Value < (int) PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo.size() && ThisPlayer->CanFoundFaction(PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[buttonaction.Value], true);
1497 break;
1498 case ButtonQuest:
1499 res = buttonaction.Value < (int) unit.Player->AvailableQuests.size() && unit.Player->CanAcceptQuest(unit.Player->AvailableQuests[buttonaction.Value]);
1500 break;
1501 case ButtonBuy:
1502 res = (buttonaction.Value != -1) && (&UnitManager.GetSlotUnit(buttonaction.Value) != nullptr);
1503 break;
1504 //Wyrmgus end
1505 }
1506 #if 0
1507 // there is a additional check function -- call it
1508 if (res && buttonaction.Disabled) {
1509 return buttonaction.Disabled(unit, buttonaction);
1510 }
1511 #endif
1512 return res;
1513 }
1514
1515 //Wyrmgus start
1516 /**
1517 ** Check if the button is usable for the unit.
1518 **
1519 ** @param unit unit which checks for allow.
1520 ** @param buttonaction button to check if it is usable.
1521 **
1522 ** @return 1 if button is usable, 0 else.
1523 */
IsButtonUsable(const CUnit & unit,const ButtonAction & buttonaction)1524 bool IsButtonUsable(const CUnit &unit, const ButtonAction &buttonaction)
1525 {
1526 if (!IsButtonAllowed(unit, buttonaction)) {
1527 return false;
1528 }
1529
1530 bool res = false;
1531 if (buttonaction.Allowed) {
1532 res = buttonaction.Allowed(unit, buttonaction);
1533 if (!res) {
1534 return false;
1535 } else {
1536 res = false;
1537 }
1538 }
1539
1540 // Check button-specific cases
1541 switch (buttonaction.Action) {
1542 case ButtonStop:
1543 case ButtonStandGround:
1544 case ButtonButton:
1545 case ButtonMove:
1546 case ButtonCallbackAction:
1547 case ButtonRallyPoint:
1548 case ButtonUnit:
1549 case ButtonEditorUnit:
1550 case ButtonRepair:
1551 case ButtonPatrol:
1552 case ButtonHarvest:
1553 case ButtonReturn:
1554 case ButtonAttack:
1555 case ButtonAttackGround:
1556 case ButtonSalvage:
1557 case ButtonEnterMapLayer:
1558 res = true;
1559 break;
1560 case ButtonTrain:
1561 case ButtonUpgradeTo:
1562 case ButtonResearch:
1563 case ButtonBuild:
1564 if (buttonaction.Action == ButtonResearch) {
1565 res = CheckDependencies(AllUpgrades[buttonaction.Value], unit.Player, false, false, !ThisPlayer->IsTeamed(unit));
1566 if (res) {
1567 res = UpgradeIdentAllowed(*ThisPlayer, buttonaction.ValueStr) == 'A' && CheckDependencies(AllUpgrades[buttonaction.Value], ThisPlayer, false, false); //also check for the dependencies of this player extra for researches, so that the player doesn't research too advanced technologies at neutral buildings
1568 }
1569 } else {
1570 res = CheckDependencies(UnitTypes[buttonaction.Value], unit.Player, false, false, !ThisPlayer->IsTeamed(unit));
1571 }
1572 break;
1573 case ButtonExperienceUpgradeTo:
1574 res = CheckDependencies(UnitTypes[buttonaction.Value], &unit, true, false) && unit.Variable[LEVELUP_INDEX].Value >= 1;
1575 break;
1576 case ButtonLearnAbility:
1577 res = unit.CanLearnAbility(CUpgrade::Get(buttonaction.ValueStr));
1578 break;
1579 case ButtonSpellCast:
1580 res = CSpell::Spells[buttonaction.Value]->IsAvailableForUnit(unit);
1581 break;
1582 case ButtonFaction:
1583 res = ThisPlayer->CanFoundFaction(PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[buttonaction.Value]);
1584 break;
1585 case ButtonBuy:
1586 res = true;
1587 if (UnitManager.GetSlotUnit(buttonaction.Value).Character != nullptr) {
1588 res = ThisPlayer->Heroes.size() < PlayerHeroMax;
1589 }
1590 break;
1591 case ButtonUnload:
1592 case ButtonCancel:
1593 case ButtonCancelUpgrade:
1594 case ButtonCancelTrain:
1595 case ButtonCancelBuild:
1596 case ButtonQuest:
1597 case ButtonProduceResource:
1598 case ButtonSellResource:
1599 case ButtonBuyResource:
1600 res = true;
1601 break;
1602 }
1603
1604 return res;
1605 }
1606
1607 /**
1608 ** Get the cooldown timer for the button (if any).
1609 **
1610 ** @param unit unit which checks for allow.
1611 ** @param buttonaction button to check if it is usable.
1612 **
1613 ** @return the cooldown for using the button.
1614 */
GetButtonCooldown(const CUnit & unit,const ButtonAction & buttonaction)1615 int GetButtonCooldown(const CUnit &unit, const ButtonAction &buttonaction)
1616 {
1617 int cooldown = 0;
1618
1619 // Check button-specific cases
1620 switch (buttonaction.Action) {
1621 case ButtonBuy:
1622 if (buttonaction.Value != -1 && UnitManager.GetSlotUnit(buttonaction.Value).Character != nullptr) {
1623 cooldown = ThisPlayer->HeroCooldownTimer;
1624 }
1625 break;
1626 }
1627
1628 return cooldown;
1629 }
1630
1631 /**
1632 ** Get the cooldown timer for the button, in percent.
1633 **
1634 ** @param unit unit which checks for allow.
1635 ** @param buttonaction button to check if it is usable.
1636 **
1637 ** @return the cooldown for using the button.
1638 */
GetButtonCooldownPercent(const CUnit & unit,const ButtonAction & buttonaction)1639 int GetButtonCooldownPercent(const CUnit &unit, const ButtonAction &buttonaction)
1640 {
1641 int cooldown = 0;
1642
1643 // Check button-specific cases
1644 switch (buttonaction.Action) {
1645 case ButtonBuy:
1646 if (buttonaction.Value != -1 && UnitManager.GetSlotUnit(buttonaction.Value).Character != nullptr) {
1647 cooldown = ThisPlayer->HeroCooldownTimer * 100 / HeroCooldownCycles;
1648 }
1649 break;
1650 }
1651
1652 return cooldown;
1653 }
1654 //Wyrmgus end
1655
1656 /**
1657 ** Update bottom panel for multiple units.
1658 **
1659 ** @return array of UI.ButtonPanel.NumButtons buttons to show.
1660 **
1661 ** @todo FIXME : make UpdateButtonPanelMultipleUnits more configurable.
1662 ** @todo show all possible buttons or just same button...
1663 */
UpdateButtonPanelMultipleUnits(std::vector<ButtonAction> * buttonActions)1664 static void UpdateButtonPanelMultipleUnits(std::vector<ButtonAction> *buttonActions)
1665 {
1666 buttonActions->resize(UI.ButtonPanel.Buttons.size());
1667 for (size_t z = 0; z < UI.ButtonPanel.Buttons.size(); ++z) {
1668 (*buttonActions)[z].Pos = -1;
1669 }
1670 char unit_ident[128];
1671 //Wyrmgus start
1672 char individual_unit_ident[200][128]; // the 200 there is the max selectable quantity; not nice to hardcode it like this, should be changed in the future
1673 //Wyrmgus end
1674
1675 sprintf(unit_ident, ",%s-group,", PlayerRaces.Name[ThisPlayer->Race].c_str());
1676
1677 //Wyrmgus start
1678 for (size_t i = 0; i != Selected.size(); ++i) {
1679 sprintf(individual_unit_ident[i], ",%s,", Selected[i]->Type->Ident.c_str());
1680 }
1681 //Wyrmgus end
1682
1683 for (size_t z = 0; z < UnitButtonTable.size(); ++z) {
1684 if (UnitButtonTable[z]->Level != CurrentButtonLevel) {
1685 continue;
1686 }
1687
1688 //Wyrmgus start
1689 bool used_by_all = true;
1690 for (size_t i = 0; i != Selected.size(); ++i) {
1691 if (!strstr(UnitButtonTable[z]->UnitMask.c_str(), individual_unit_ident[i])) {
1692 used_by_all = false;
1693 break;
1694 }
1695 }
1696 //Wyrmgus end
1697
1698 // any unit or unit in list
1699 if (UnitButtonTable[z]->UnitMask[0] != '*'
1700 //Wyrmgus start
1701 // && !strstr(UnitButtonTable[z]->UnitMask.c_str(), unit_ident)) {
1702 && !strstr(UnitButtonTable[z]->UnitMask.c_str(), unit_ident) && !used_by_all) {
1703 //Wyrmgus end
1704 continue;
1705 }
1706
1707 bool allow = true;
1708 if (UnitButtonTable[z]->AlwaysShow == false) {
1709 for (size_t i = 0; i != Selected.size(); ++i) {
1710 if (!IsButtonAllowed(*Selected[i], *UnitButtonTable[z])) {
1711 allow = false;
1712 break;
1713 }
1714 }
1715 }
1716
1717 Assert(1 <= UnitButtonTable[z]->Pos);
1718 Assert(UnitButtonTable[z]->Pos <= (int)UI.ButtonPanel.Buttons.size());
1719
1720 // is button allowed after all?
1721 if (allow) {
1722 // OverWrite, So take last valid button.
1723 (*buttonActions)[UnitButtonTable[z]->Pos - 1] = *UnitButtonTable[z];
1724 }
1725 }
1726 }
1727
1728 /**
1729 ** Update bottom panel for single unit.
1730 ** or unit group with the same type.
1731 **
1732 ** @param unit unit which has actions shown with buttons.
1733 **
1734 ** @return array of UI.ButtonPanel.NumButtons buttons to show.
1735 **
1736 ** @todo FIXME : Remove Hack for cancel button.
1737 */
UpdateButtonPanelSingleUnit(const CUnit & unit,std::vector<ButtonAction> * buttonActions)1738 static void UpdateButtonPanelSingleUnit(const CUnit &unit, std::vector<ButtonAction> *buttonActions)
1739 {
1740 buttonActions->resize(UI.ButtonPanel.Buttons.size());
1741
1742 for (size_t i = 0; i != UI.ButtonPanel.Buttons.size(); ++i) {
1743 (*buttonActions)[i].Pos = -1;
1744 }
1745 char unit_ident[128];
1746
1747 //
1748 // FIXME: johns: some hacks for cancel buttons
1749 //
1750 if (unit.CurrentAction() == UnitActionBuilt) {
1751 // Trick 17 to get the cancel-build button
1752 strcpy_s(unit_ident, sizeof(unit_ident), ",cancel-build,");
1753 } else if (unit.CurrentAction() == UnitActionUpgradeTo) {
1754 // Trick 17 to get the cancel-upgrade button
1755 strcpy_s(unit_ident, sizeof(unit_ident), ",cancel-upgrade,");
1756 } else if (unit.CurrentAction() == UnitActionResearch) {
1757 // Trick 17 to get the cancel-upgrade button
1758 strcpy_s(unit_ident, sizeof(unit_ident), ",cancel-upgrade,");
1759 } else {
1760 sprintf(unit_ident, ",%s,", unit.Type->Ident.c_str());
1761 }
1762 for (size_t i = 0; i != UnitButtonTable.size(); ++i) {
1763 ButtonAction &buttonaction = *UnitButtonTable[i];
1764 Assert(0 < buttonaction.Pos && buttonaction.Pos <= (int)UI.ButtonPanel.Buttons.size());
1765
1766 // Same level
1767 if (buttonaction.Level != CurrentButtonLevel) {
1768 continue;
1769 }
1770
1771 // any unit or unit in list
1772 if (buttonaction.UnitMask[0] != '*'
1773 && !strstr(buttonaction.UnitMask.c_str(), unit_ident)) {
1774 continue;
1775 }
1776 //Wyrmgus start
1777 // int allow = IsButtonAllowed(unit, buttonaction);
1778 bool allow = true; // check all selected units, as different units of the same type may have different allowed buttons
1779 if (UnitButtonTable[i]->AlwaysShow == false) {
1780 for (size_t j = 0; j != Selected.size(); ++j) {
1781 if (!IsButtonAllowed(*Selected[j], *UnitButtonTable[i])) {
1782 allow = false;
1783 break;
1784 }
1785 }
1786 }
1787 //Wyrmgus end
1788 int pos = buttonaction.Pos;
1789
1790 // Special case for researches
1791 int researchCheck = true;
1792 if (buttonaction.AlwaysShow && !allow && buttonaction.Action == ButtonResearch
1793 //Wyrmgus start
1794 // && UpgradeIdentAllowed(*unit.Player, buttonaction.ValueStr) == 'R') {
1795 && UpgradeIdentAllowed(*ThisPlayer, buttonaction.ValueStr) == 'R') {
1796 //Wyrmgus end
1797 researchCheck = false;
1798 }
1799
1800 // is button allowed after all?
1801 if ((buttonaction.AlwaysShow && (*buttonActions)[pos - 1].Pos == -1 && researchCheck) || allow) {
1802 // OverWrite, So take last valid button.
1803 (*buttonActions)[pos - 1] = buttonaction;
1804 }
1805 }
1806 }
1807
1808 /**
1809 ** Update button panel.
1810 **
1811 ** @internal Affect CurrentButtons with buttons to show.
1812 */
Update()1813 void CButtonPanel::Update()
1814 {
1815 //Wyrmgus start
1816 // if (Selected.empty()) {
1817 if (Selected.empty() || (!GameRunning && !GameEstablishing)) {
1818 //Wyrmgus end
1819 CurrentButtons.clear();
1820 return;
1821 }
1822
1823 CUnit &unit = *Selected[0];
1824 // foreign unit
1825 //Wyrmgus start
1826 // if (unit.Player != ThisPlayer && !ThisPlayer->IsTeamed(unit)) {
1827 if (unit.Player != ThisPlayer && !ThisPlayer->IsTeamed(unit) && !ThisPlayer->HasBuildingAccess(*unit.Player)) {
1828 //Wyrmgus end
1829 CurrentButtons.clear();
1830 return;
1831 }
1832
1833 //Wyrmgus start
1834 //update the sold item buttons
1835 if (GameRunning || GameEstablishing) {
1836 unsigned int sold_unit_count = 0;
1837 unsigned int potential_faction_count = 0;
1838 for (int i = 0; i < (int) UnitButtonTable.size(); ++i) {
1839 if (UnitButtonTable[i]->Action != ButtonFaction && UnitButtonTable[i]->Action != ButtonBuy) {
1840 continue;
1841 }
1842 char unit_ident[128];
1843 sprintf(unit_ident, ",%s,", unit.Type->Ident.c_str());
1844 if (UnitButtonTable[i]->UnitMask[0] != '*' && !strstr(UnitButtonTable[i]->UnitMask.c_str(), unit_ident)) {
1845 continue;
1846 }
1847
1848 if (UnitButtonTable[i]->Action == ButtonFaction) {
1849 if (ThisPlayer->Faction == -1 || potential_faction_count >= PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo.size()) {
1850 UnitButtonTable[i]->Value = -1;
1851 } else {
1852 UnitButtonTable[i]->Value = potential_faction_count;
1853 UnitButtonTable[i]->Hint = "Found ";
1854 if (PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[potential_faction_count]->DefiniteArticle) {
1855 UnitButtonTable[i]->Hint += "the ";
1856 }
1857 UnitButtonTable[i]->Hint += PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[potential_faction_count]->Name;
1858 UnitButtonTable[i]->Description = "Changes your faction to ";
1859 if (PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[potential_faction_count]->DefiniteArticle) {
1860 UnitButtonTable[i]->Description += "the ";
1861 }
1862 UnitButtonTable[i]->Description += PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[potential_faction_count]->Name;
1863 }
1864 potential_faction_count += 1;
1865 } else if (UnitButtonTable[i]->Action == ButtonBuy) {
1866 if (sold_unit_count >= unit.SoldUnits.size()) {
1867 UnitButtonTable[i]->Value = -1;
1868 } else {
1869 UnitButtonTable[i]->Value = UnitNumber(*unit.SoldUnits[sold_unit_count]);
1870 if (unit.SoldUnits[sold_unit_count]->Character != nullptr) {
1871 UnitButtonTable[i]->Hint = "Recruit " + unit.SoldUnits[sold_unit_count]->GetName();
1872 } else {
1873 if (!unit.SoldUnits[sold_unit_count]->Name.empty()) {
1874 UnitButtonTable[i]->Hint = "Buy " + unit.SoldUnits[sold_unit_count]->GetName();
1875 } else {
1876 UnitButtonTable[i]->Hint = "Buy " + unit.SoldUnits[sold_unit_count]->GetTypeName();
1877 }
1878 }
1879 }
1880 sold_unit_count += 1;
1881 }
1882 }
1883 }
1884 //Wyrmgus end
1885
1886 bool sameType = true;
1887 // multiple selected
1888 for (size_t i = 1; i < Selected.size(); ++i) {
1889 if (Selected[i]->Type != unit.Type) {
1890 sameType = false;
1891 break;
1892 }
1893 }
1894
1895 // We have selected different units types
1896 if (!sameType) {
1897 UpdateButtonPanelMultipleUnits(&CurrentButtons);
1898 } else {
1899 // We have same type units selected
1900 // -- continue with setting buttons as for the first unit
1901 UpdateButtonPanelSingleUnit(unit, &CurrentButtons);
1902 }
1903 }
1904
DoClicked_SelectTarget(int button)1905 void CButtonPanel::DoClicked_SelectTarget(int button)
1906 {
1907 // Select target.
1908 CursorState = CursorStateSelect;
1909 if (CurrentButtons[button].ButtonCursor.length() && CursorByIdent(CurrentButtons[button].ButtonCursor)) {
1910 GameCursor = CursorByIdent(CurrentButtons[button].ButtonCursor);
1911 CustomCursor = CurrentButtons[button].ButtonCursor;
1912 } else {
1913 GameCursor = UI.YellowHair.Cursor;
1914 }
1915 CursorAction = CurrentButtons[button].Action;
1916 CursorValue = CurrentButtons[button].Value;
1917 CurrentButtonLevel = CButtonLevel::CancelButtonLevel; // the cancel-only button level
1918 UI.ButtonPanel.Update();
1919 UI.StatusLine.Set(_("Select Target"));
1920 }
1921
DoClicked_Unload(int button)1922 void CButtonPanel::DoClicked_Unload(int button)
1923 {
1924 const int flush = !(KeyModifiers & ModifierShift);
1925 //
1926 // Unload on coast, transporter standing, unload all units right now.
1927 // That or a bunker.
1928 // Unload on coast valid only for sea units
1929 //
1930 if ((Selected.size() == 1 && Selected[0]->CurrentAction() == UnitActionStill
1931 && Selected[0]->Type->UnitType == UnitTypeNaval && Selected[0]->MapLayer->Field(Selected[0]->tilePos)->CoastOnMap())
1932 || !Selected[0]->CanMove()) {
1933 SendCommandUnload(*Selected[0], Selected[0]->tilePos, NoUnitP, flush, Selected[0]->MapLayer->ID);
1934 return ;
1935 }
1936 DoClicked_SelectTarget(button);
1937 }
1938
DoClicked_SpellCast(int button)1939 void CButtonPanel::DoClicked_SpellCast(int button)
1940 {
1941 const int spellId = CurrentButtons[button].Value;
1942 if (KeyModifiers & ModifierControl) {
1943 int autocast = 0;
1944
1945 if (!CSpell::Spells[spellId]->AutoCast) {
1946 PlayGameSound(GameSounds.PlacementError[ThisPlayer->Race].Sound, MaxSampleVolume);
1947 return;
1948 }
1949
1950 //autocast = 0;
1951 // If any selected unit doesn't have autocast on turn it on
1952 // for everyone
1953 for (size_t i = 0; i != Selected.size(); ++i) {
1954 if (Selected[i]->AutoCastSpell[spellId] == 0) {
1955 autocast = 1;
1956 break;
1957 }
1958 }
1959 for (size_t i = 0; i != Selected.size(); ++i) {
1960 if (Selected[i]->AutoCastSpell[spellId] != autocast) {
1961 SendCommandAutoSpellCast(*Selected[i], spellId, autocast);
1962 }
1963 }
1964 return;
1965 }
1966 if (CSpell::Spells[spellId]->IsCasterOnly()) {
1967 const int flush = !(KeyModifiers & ModifierShift);
1968
1969 for (size_t i = 0; i != Selected.size(); ++i) {
1970 CUnit &unit = *Selected[i];
1971 // CursorValue here holds the spell type id
1972 SendCommandSpellCast(unit, unit.tilePos, &unit, spellId, flush, unit.MapLayer->ID);
1973 }
1974 return;
1975 }
1976 DoClicked_SelectTarget(button);
1977 }
1978
DoClicked_Repair(int button)1979 void CButtonPanel::DoClicked_Repair(int button)
1980 {
1981 if (KeyModifiers & ModifierControl) {
1982 unsigned autorepair = 0;
1983 // If any selected unit doesn't have autocast on turn it on
1984 // for everyone
1985 for (size_t i = 0; i != Selected.size(); ++i) {
1986 if (Selected[i]->AutoRepair == 0) {
1987 autorepair = 1;
1988 break;
1989 }
1990 }
1991 for (size_t i = 0; i != Selected.size(); ++i) {
1992 if (Selected[i]->AutoRepair != autorepair) {
1993 SendCommandAutoRepair(*Selected[i], autorepair);
1994 }
1995 }
1996 return;
1997 }
1998 DoClicked_SelectTarget(button);
1999 }
2000
DoClicked_Return()2001 void CButtonPanel::DoClicked_Return()
2002 {
2003 for (size_t i = 0; i != Selected.size(); ++i) {
2004 SendCommandReturnGoods(*Selected[i], NoUnitP, !(KeyModifiers & ModifierShift));
2005 }
2006 }
2007
DoClicked_Stop()2008 void CButtonPanel::DoClicked_Stop()
2009 {
2010 for (size_t i = 0; i != Selected.size(); ++i) {
2011 SendCommandStopUnit(*Selected[i]);
2012 }
2013 }
2014
DoClicked_StandGround()2015 void CButtonPanel::DoClicked_StandGround()
2016 {
2017 for (size_t i = 0; i != Selected.size(); ++i) {
2018 SendCommandStandGround(*Selected[i], !(KeyModifiers & ModifierShift));
2019 }
2020 }
2021
DoClicked_Button(int button)2022 void CButtonPanel::DoClicked_Button(int button)
2023 {
2024 CurrentButtonLevel = CButtonLevel::GetButtonLevel(CurrentButtons[button].ValueStr);
2025 LastDrawnButtonPopup = nullptr;
2026 UI.ButtonPanel.Update();
2027 }
2028
DoClicked_CancelUpgrade()2029 void CButtonPanel::DoClicked_CancelUpgrade()
2030 {
2031 if (Selected.size() == 1) {
2032 switch (Selected[0]->CurrentAction()) {
2033 case UnitActionUpgradeTo:
2034 SendCommandCancelUpgradeTo(*Selected[0]);
2035 break;
2036 case UnitActionResearch:
2037 SendCommandCancelResearch(*Selected[0]);
2038 break;
2039 default:
2040 break;
2041 }
2042 }
2043 UI.StatusLine.Clear();
2044 UI.StatusLine.ClearCosts();
2045 CurrentButtonLevel = nullptr;
2046 UI.ButtonPanel.Update();
2047 GameCursor = UI.Point.Cursor;
2048 CursorBuilding = nullptr;
2049 CursorState = CursorStatePoint;
2050 }
2051
DoClicked_CancelTrain()2052 void CButtonPanel::DoClicked_CancelTrain()
2053 {
2054 Assert(Selected[0]->CurrentAction() == UnitActionTrain);
2055 SendCommandCancelTraining(*Selected[0], -1, nullptr);
2056 UI.StatusLine.Clear();
2057 UI.StatusLine.ClearCosts();
2058 }
2059
DoClicked_CancelBuild()2060 void CButtonPanel::DoClicked_CancelBuild()
2061 {
2062 // FIXME: johns is this not sure, only building should have this?
2063 Assert(Selected[0]->CurrentAction() == UnitActionBuilt);
2064 if (Selected.size() == 1) {
2065 SendCommandDismiss(*Selected[0]);
2066 }
2067 UI.StatusLine.Clear();
2068 UI.StatusLine.ClearCosts();
2069 }
2070
DoClicked_Build(int button)2071 void CButtonPanel::DoClicked_Build(int button)
2072 {
2073 // FIXME: store pointer in button table!
2074 CUnitType &type = *UnitTypes[CurrentButtons[button].Value];
2075 if (!ThisPlayer->CheckUnitType(type)) {
2076 UI.StatusLine.Set(_("Select Location"));
2077 UI.StatusLine.ClearCosts();
2078 CursorBuilding = &type;
2079 // FIXME: check is this check necessary?
2080 CurrentButtonLevel = CButtonLevel::CancelButtonLevel; // the cancel-only button level
2081 UI.ButtonPanel.Update();
2082 }
2083 }
2084
DoClicked_Train(int button)2085 void CButtonPanel::DoClicked_Train(int button)
2086 {
2087 // FIXME: store pointer in button table!
2088 CUnitType &type = *UnitTypes[CurrentButtons[button].Value];
2089 // FIXME: Johns: I want to place commands in queue, even if not
2090 // FIXME: enough resources are available.
2091 // FIXME: training queue full check is not correct for network.
2092 // FIXME: this can be correct written, with a little more code.
2093 //Wyrmgus start
2094 /*
2095 if (Selected[0]->CurrentAction() == UnitActionTrain && !EnableTrainingQueue) {
2096 //Wyrmgus start
2097 // Selected[0]->Player->Notify(NotifyYellow, Selected[0]->tilePos, "%s", _("Unit training queue is full"));
2098 ThisPlayer->Notify(NotifyYellow, Selected[0]->tilePos, "%s", _("Unit training queue is full"));
2099 //Wyrmgus end
2100 //Wyrmgus start
2101 // } else if (Selected[0]->Player->CheckLimits(type) >= 0 && !Selected[0]->Player->CheckUnitType(type)) {
2102 } else if (ThisPlayer->CheckLimits(type) >= 0 && !ThisPlayer->CheckUnitType(type)) {
2103 //Wyrmgus end
2104 //PlayerSubUnitType(player,type);
2105 //Wyrmgus start
2106 // SendCommandTrainUnit(*Selected[0], type, !(KeyModifiers & ModifierShift));
2107 SendCommandTrainUnit(*Selected[0], type, ThisPlayer->Index, !(KeyModifiers & ModifierShift));
2108 //Wyrmgus end
2109 UI.StatusLine.Clear();
2110 UI.StatusLine.ClearCosts();
2111 //Wyrmgus start
2112 // } else if (Selected[0]->Player->CheckLimits(type) == -3) {
2113 // if (GameSounds.NotEnoughFood[Selected[0]->Player->Race].Sound) {
2114 // PlayGameSound(GameSounds.NotEnoughFood[Selected[0]->Player->Race].Sound, MaxSampleVolume);
2115 // }
2116 } else if (ThisPlayer->CheckLimits(type) == -3) {
2117 if (GameSounds.NotEnoughFood[ThisPlayer->Race].Sound) {
2118 PlayGameSound(GameSounds.NotEnoughFood[ThisPlayer->Race].Sound, MaxSampleVolume);
2119 }
2120 //Wyrmgus end
2121 }
2122 */
2123 int best_training_place = 0;
2124 int lowest_queue = Selected[0]->Orders.size();
2125
2126 for (size_t i = 0; i != Selected.size(); ++i) {
2127 if (Selected[i]->Type == Selected[0]->Type) {
2128 int selected_queue = 0;
2129 for (size_t j = 0; j < Selected[i]->Orders.size(); ++j) {
2130 if (Selected[i]->Orders[j]->Action == UnitActionTrain) {
2131 selected_queue += 1;
2132 }
2133 }
2134 if (selected_queue < lowest_queue) {
2135 lowest_queue = selected_queue;
2136 best_training_place = i;
2137 }
2138 }
2139 }
2140
2141 if (type.BoolFlag[RAIL_INDEX].value) {
2142 bool has_adjacent_rail = Selected[best_training_place]->HasAdjacentRailForUnitType(&type);
2143 if (!has_adjacent_rail) {
2144 ThisPlayer->Notify(NotifyYellow, Selected[best_training_place]->tilePos, Selected[best_training_place]->MapLayer->ID, "%s", _("The unit requires railroads to be placed on"));
2145 PlayGameSound(GameSounds.PlacementError[ThisPlayer->Race].Sound, MaxSampleVolume);
2146 return;
2147 }
2148 }
2149
2150 int unit_count = 1;
2151 if (KeyModifiers & ModifierShift) {
2152 unit_count = 5;
2153 }
2154
2155 for (int i = 0; i < unit_count; ++i) {
2156 if (Selected[best_training_place]->CurrentAction() == UnitActionTrain && !EnableTrainingQueue) {
2157 ThisPlayer->Notify(NotifyYellow, Selected[best_training_place]->tilePos, Selected[best_training_place]->MapLayer->ID, "%s", _("Unit training queue is full"));
2158 return;
2159 } else if (ThisPlayer->CheckLimits(type) >= 0 && !ThisPlayer->CheckUnitType(type, Selected[best_training_place]->Type->Stats[Selected[best_training_place]->Player->Index].GetUnitStock(UnitTypes[type.Slot]) != 0)) {
2160 SendCommandTrainUnit(*Selected[best_training_place], type, ThisPlayer->Index, FlushCommands);
2161 UI.StatusLine.Clear();
2162 UI.StatusLine.ClearCosts();
2163 } else if (ThisPlayer->CheckLimits(type) == -3) {
2164 if (GameSounds.NotEnoughFood[ThisPlayer->Race].Sound) {
2165 PlayGameSound(GameSounds.NotEnoughFood[ThisPlayer->Race].Sound, MaxSampleVolume);
2166 }
2167 return;
2168 }
2169 }
2170 //Wyrmgus end
2171 }
2172
DoClicked_UpgradeTo(int button)2173 void CButtonPanel::DoClicked_UpgradeTo(int button)
2174 {
2175 // FIXME: store pointer in button table!
2176 CUnitType &type = *UnitTypes[CurrentButtons[button].Value];
2177 for (size_t i = 0; i != Selected.size(); ++i) {
2178 if (Selected[i]->Player->CheckLimits(type) != -6 && !Selected[i]->Player->CheckUnitType(type)) {
2179 if (Selected[i]->CurrentAction() != UnitActionUpgradeTo) {
2180 SendCommandUpgradeTo(*Selected[i], type, !(KeyModifiers & ModifierShift));
2181 UI.StatusLine.Clear();
2182 UI.StatusLine.ClearCosts();
2183 }
2184 } else {
2185 break;
2186 }
2187 }
2188 }
2189
2190 //Wyrmgus start
DoClicked_ExperienceUpgradeTo(int button)2191 void CButtonPanel::DoClicked_ExperienceUpgradeTo(int button)
2192 {
2193 // FIXME: store pointer in button table!
2194 CUnitType &type = *UnitTypes[CurrentButtons[button].Value];
2195 for (size_t i = 0; i != Selected.size(); ++i) {
2196 if (Selected[0]->Player->GetUnitTotalCount(type) < Selected[0]->Player->Allow.Units[type.Slot] || Selected[0]->Player->CheckLimits(type) != -6) { //ugly way to make the checklimits message only appear when it should
2197 if (Selected[i]->CurrentAction() != UnitActionUpgradeTo) {
2198 Selected[i]->Variable[LEVELUP_INDEX].Value -= 1;
2199 Selected[i]->Variable[LEVELUP_INDEX].Max = Selected[i]->Variable[LEVELUP_INDEX].Value;
2200 if (!IsNetworkGame() && Selected[i]->Character != nullptr) { //save the unit-type experience upgrade for persistent characters
2201 if (Selected[i]->Character->Type->Slot != type.Slot) {
2202 if (Selected[i]->Player->AiEnabled == false) {
2203 Selected[i]->Character->Type = UnitTypes[CurrentButtons[button].Value];
2204 SaveHero(Selected[i]->Character);
2205 CheckAchievements();
2206 }
2207 }
2208 }
2209 SendCommandTransformInto(*Selected[i], type, !(KeyModifiers & ModifierShift));
2210 UI.StatusLine.Clear();
2211 UI.StatusLine.ClearCosts();
2212 Selected[i]->Player->UpdateLevelUpUnits();
2213 }
2214 } else {
2215 break;
2216 }
2217 }
2218
2219 LastDrawnButtonPopup = nullptr;
2220 UI.ButtonPanel.Update();
2221
2222 if (Selected[0]->Player == ThisPlayer) {
2223 SelectedUnitChanged();
2224 }
2225 }
2226 //Wyrmgus end
2227
DoClicked_Research(int button)2228 void CButtonPanel::DoClicked_Research(int button)
2229 {
2230 const int index = CurrentButtons[button].Value;
2231 //Wyrmgus start
2232 int upgrade_costs[MaxCosts];
2233 ThisPlayer->GetUpgradeCosts(AllUpgrades[index], upgrade_costs);
2234 // if (!Selected[0]->Player->CheckCosts(AllUpgrades[index]->Costs)) {
2235 if (!ThisPlayer->CheckCosts(upgrade_costs)) {
2236 //Wyrmgus end
2237 //PlayerSubCosts(player,Upgrades[i].Costs);
2238 //Wyrmgus start
2239 // SendCommandResearch(*Selected[0], *AllUpgrades[index], !(KeyModifiers & ModifierShift));
2240 SendCommandResearch(*Selected[0], *AllUpgrades[index], ThisPlayer->Index, !(KeyModifiers & ModifierShift));
2241 //Wyrmgus end
2242 UI.StatusLine.Clear();
2243 UI.StatusLine.ClearCosts();
2244 //Wyrmgus start
2245 LastDrawnButtonPopup = nullptr;
2246 ButtonUnderCursor = -1;
2247 OldButtonUnderCursor = -1;
2248 UI.ButtonPanel.Update();
2249 //Wyrmgus end
2250 }
2251 }
2252
2253 //Wyrmgus start
DoClicked_LearnAbility(int button)2254 void CButtonPanel::DoClicked_LearnAbility(int button)
2255 {
2256 const int index = CurrentButtons[button].Value;
2257 SendCommandLearnAbility(*Selected[0], *AllUpgrades[index]);
2258 UI.StatusLine.Clear();
2259 UI.StatusLine.ClearCosts();
2260 LastDrawnButtonPopup = nullptr;
2261
2262 if (Selected[0]->Player == ThisPlayer) {
2263 SelectedUnitChanged();
2264 }
2265 }
2266
DoClicked_Faction(int button)2267 void CButtonPanel::DoClicked_Faction(int button)
2268 {
2269 const int index = CurrentButtons[button].Value;
2270 SendCommandSetFaction(ThisPlayer->Index, PlayerRaces.Factions[ThisPlayer->Faction]->DevelopsTo[index]->ID);
2271 ButtonUnderCursor = -1;
2272 OldButtonUnderCursor = -1;
2273 LastDrawnButtonPopup = nullptr;
2274 if (Selected[0]->Player == ThisPlayer) {
2275 SelectedUnitChanged();
2276 }
2277 }
2278
DoClicked_Quest(int button)2279 void CButtonPanel::DoClicked_Quest(int button)
2280 {
2281 const int index = CurrentButtons[button].Value;
2282 SendCommandQuest(*Selected[0], Selected[0]->Player->AvailableQuests[index]);
2283 ButtonUnderCursor = -1;
2284 OldButtonUnderCursor = -1;
2285 LastDrawnButtonPopup = nullptr;
2286 if (Selected[0]->Player == ThisPlayer) {
2287 SelectedUnitChanged();
2288 }
2289 }
2290
DoClicked_Buy(int button)2291 void CButtonPanel::DoClicked_Buy(int button)
2292 {
2293 int buy_costs[MaxCosts];
2294 memset(buy_costs, 0, sizeof(buy_costs));
2295 buy_costs[CopperCost] = UnitManager.GetSlotUnit(CurrentButtons[button].Value).GetPrice();
2296 if (!ThisPlayer->CheckCosts(buy_costs) && ThisPlayer->CheckLimits(*UnitManager.GetSlotUnit(CurrentButtons[button].Value).Type) >= 0) {
2297 SendCommandBuy(*Selected[0], &UnitManager.GetSlotUnit(CurrentButtons[button].Value), ThisPlayer->Index);
2298 ButtonUnderCursor = -1;
2299 OldButtonUnderCursor = -1;
2300 LastDrawnButtonPopup = nullptr;
2301 if (IsOnlySelected(*Selected[0])) {
2302 SelectedUnitChanged();
2303 }
2304 } else if (ThisPlayer->CheckLimits(*UnitManager.GetSlotUnit(CurrentButtons[button].Value).Type) == -3) {
2305 if (GameSounds.NotEnoughFood[ThisPlayer->Race].Sound) {
2306 PlayGameSound(GameSounds.NotEnoughFood[ThisPlayer->Race].Sound, MaxSampleVolume);
2307 }
2308 }
2309 }
2310
DoClicked_ProduceResource(int button)2311 void CButtonPanel::DoClicked_ProduceResource(int button)
2312 {
2313 const int resource = CurrentButtons[button].Value;
2314 if (resource != Selected[0]->GivesResource) {
2315 SendCommandProduceResource(*Selected[0], resource);
2316 } else if (!Selected[0]->Type->GivesResource) { //if resource production button was clicked when it was already active, then this means it should be toggled off; only do this if the building's type doesn't have a default produced resource, though, since in those cases it should always produce a resource
2317 SendCommandProduceResource(*Selected[0], 0);
2318 }
2319 }
2320
DoClicked_SellResource(int button)2321 void CButtonPanel::DoClicked_SellResource(int button)
2322 {
2323 const bool toggle_autosell = (KeyModifiers & ModifierControl) != 0;
2324 const int resource = CurrentButtons[button].Value;
2325
2326 if (toggle_autosell && Selected[0]->Player == ThisPlayer) {
2327 SendCommandAutosellResource(ThisPlayer->Index, resource);
2328 } else {
2329 int sell_resource_costs[MaxCosts];
2330 memset(sell_resource_costs, 0, sizeof(sell_resource_costs));
2331 sell_resource_costs[resource] = 100;
2332 if (!ThisPlayer->CheckCosts(sell_resource_costs)) {
2333 SendCommandSellResource(*Selected[0], resource, ThisPlayer->Index);
2334 }
2335 }
2336 }
2337
DoClicked_BuyResource(int button)2338 void CButtonPanel::DoClicked_BuyResource(int button)
2339 {
2340 const int resource = CurrentButtons[button].Value;
2341 int buy_resource_costs[MaxCosts];
2342 memset(buy_resource_costs, 0, sizeof(buy_resource_costs));
2343 buy_resource_costs[CopperCost] = Selected[0]->Player->GetEffectiveResourceBuyPrice(resource);
2344 if (!ThisPlayer->CheckCosts(buy_resource_costs)) {
2345 SendCommandBuyResource(*Selected[0], resource, ThisPlayer->Index);
2346 }
2347 }
2348
DoClicked_Salvage()2349 void CButtonPanel::DoClicked_Salvage()
2350 {
2351 for (int i = (Selected.size() - 1); i >= 0; --i) {
2352 SendCommandDismiss(*Selected[i], true);
2353 }
2354 }
2355
2356 /**
2357 ** @brief Enter the map layer that the selected unit leads to.
2358 */
DoClicked_EnterMapLayer()2359 void CButtonPanel::DoClicked_EnterMapLayer()
2360 {
2361 for (size_t i = 0; i < Selected.size(); ++i) {
2362 CUnit *connection_destination = Selected[i]->ConnectingDestination;
2363 if (connection_destination != nullptr) {
2364 PlayUnitSound(*connection_destination, VoiceUsed);
2365 Selected[i]->Blink = 4;
2366 connection_destination->Blink = 4;
2367 UnSelectUnit(*Selected[i]);
2368 if (connection_destination->IsVisible(*ThisPlayer)) {
2369 SelectUnit(*connection_destination);
2370 }
2371 SelectionChanged();
2372 ChangeCurrentMapLayer(connection_destination->MapLayer->ID);
2373 UI.SelectedViewport->Center(connection_destination->GetMapPixelPosCenter());
2374 break;
2375 }
2376 }
2377 }
2378
DoClicked_CallbackAction(int button)2379 void CButtonPanel::DoClicked_CallbackAction(int button)
2380 {
2381 LuaCallback* callback = (LuaCallback*)(CurrentButtons[button].Payload);
2382 callback->pushPreamble();
2383 callback->pushInteger(UnitNumber(*Selected[0]));
2384 callback->run();
2385 }
2386
2387 /**
2388 ** Handle bottom button clicked.
2389 **
2390 ** @param button Button that was clicked.
2391 */
DoClicked(int button)2392 void CButtonPanel::DoClicked(int button)
2393 {
2394 Assert(0 <= button && button < (int)UI.ButtonPanel.Buttons.size());
2395 // no buttons
2396 if (CurrentButtons.empty()) {
2397 return;
2398 }
2399 if (IsButtonAllowed(*Selected[0], CurrentButtons[button]) == false) {
2400 return;
2401 }
2402 //
2403 // Button not available.
2404 // or Not Teamed
2405 //
2406 //Wyrmgus start
2407 // if (CurrentButtons[button].Pos == -1 || !ThisPlayer->IsTeamed(*Selected[0])) {
2408 if (CurrentButtons[button].Pos == -1 || (!CurrentButtons[button].AlwaysShow && !ThisPlayer->IsTeamed(*Selected[0]) && !ThisPlayer->HasBuildingAccess(*Selected[0]->Player, CurrentButtons[button].Action)) || (!CurrentButtons[button].AlwaysShow && !ThisPlayer->IsTeamed(*Selected[0]) && ThisPlayer->HasBuildingAccess(*Selected[0]->Player, CurrentButtons[button].Action) && !IsNeutralUsableButtonAction(CurrentButtons[button].Action))) { //allow neutral units to be used (but only for training or as transporters)
2409 //Wyrmgus end
2410 return;
2411 }
2412
2413 //Wyrmgus start
2414 if (!IsButtonUsable(*Selected[0], CurrentButtons[button])) {
2415 if (
2416 (CurrentButtons[button].Action == ButtonResearch && UpgradeIdentAllowed(*ThisPlayer, CurrentButtons[button].ValueStr) == 'R')
2417 || (CurrentButtons[button].Action == ButtonLearnAbility && Selected[0]->GetIndividualUpgrade(CUpgrade::Get(CurrentButtons[button].ValueStr)) == CUpgrade::Get(CurrentButtons[button].ValueStr)->MaxLimit)
2418 ) {
2419 ThisPlayer->Notify(NotifyYellow, Selected[0]->tilePos, Selected[0]->MapLayer->ID, "%s", _("The upgrade has already been acquired"));
2420 } else if (CurrentButtons[button].Action == ButtonBuy && ThisPlayer->Heroes.size() >= PlayerHeroMax && CurrentButtons[button].Value != -1 && UnitManager.GetSlotUnit(CurrentButtons[button].Value).Character != nullptr) {
2421 ThisPlayer->Notify(NotifyYellow, Selected[0]->tilePos, Selected[0]->MapLayer->ID, "%s", _("The hero limit has been reached"));
2422 } else {
2423 ThisPlayer->Notify(NotifyYellow, Selected[0]->tilePos, Selected[0]->MapLayer->ID, "%s", _("The requirements have not been fulfilled"));
2424 }
2425 PlayGameSound(GameSounds.PlacementError[ThisPlayer->Race].Sound, MaxSampleVolume);
2426 return;
2427 }
2428
2429 if (GetButtonCooldown(*Selected[0], CurrentButtons[button]) > 0) {
2430 ThisPlayer->Notify(NotifyYellow, Selected[0]->tilePos, Selected[0]->MapLayer->ID, "%s", _("The cooldown is active"));
2431 PlayGameSound(GameSounds.PlacementError[ThisPlayer->Race].Sound, MaxSampleVolume);
2432 return;
2433 }
2434 //Wyrmgus end
2435
2436 PlayGameSound(GameSounds.Click.Sound, MaxSampleVolume);
2437 if (CurrentButtons[button].CommentSound.Sound) {
2438 PlayGameSound(CurrentButtons[button].CommentSound.Sound, MaxSampleVolume);
2439 }
2440
2441 // Handle action on button.
2442 switch (CurrentButtons[button].Action) {
2443 case ButtonUnload: { DoClicked_Unload(button); break; }
2444 case ButtonSpellCast: { DoClicked_SpellCast(button); break; }
2445 case ButtonRepair: { DoClicked_Repair(button); break; }
2446 case ButtonMove: // Follow Next
2447 case ButtonPatrol: // Follow Next
2448 case ButtonHarvest: // Follow Next
2449 case ButtonAttack: // Follow Next
2450 //Wyrmgus start
2451 case ButtonRallyPoint:
2452 case ButtonUnit:
2453 case ButtonEditorUnit:
2454 //Wyrmgus end
2455 case ButtonAttackGround: { DoClicked_SelectTarget(button); break; }
2456 case ButtonReturn: { DoClicked_Return(); break; }
2457 case ButtonStop: { DoClicked_Stop(); break; }
2458 case ButtonStandGround: { DoClicked_StandGround(); break; }
2459 case ButtonButton: { DoClicked_Button(button); break; }
2460 case ButtonCancel: // Follow Next
2461 case ButtonCancelUpgrade: { DoClicked_CancelUpgrade(); break; }
2462 case ButtonCancelTrain: { DoClicked_CancelTrain(); break; }
2463 case ButtonCancelBuild: { DoClicked_CancelBuild(); break; }
2464 case ButtonBuild: { DoClicked_Build(button); break; }
2465 case ButtonTrain: { DoClicked_Train(button); break; }
2466 case ButtonUpgradeTo: { DoClicked_UpgradeTo(button); break; }
2467 case ButtonResearch: { DoClicked_Research(button); break; }
2468 case ButtonCallbackAction: { DoClicked_CallbackAction(button); break; }
2469 //Wyrmgus start
2470 case ButtonLearnAbility: { DoClicked_LearnAbility(button); break; }
2471 case ButtonExperienceUpgradeTo: { DoClicked_ExperienceUpgradeTo(button); break; }
2472 case ButtonFaction: { DoClicked_Faction(button); break; }
2473 case ButtonQuest: { DoClicked_Quest(button); break; }
2474 case ButtonBuy: { DoClicked_Buy(button); break; }
2475 case ButtonProduceResource: { DoClicked_ProduceResource(button); break; }
2476 case ButtonSellResource: { DoClicked_SellResource(button); break; }
2477 case ButtonBuyResource: { DoClicked_BuyResource(button); break; }
2478 case ButtonSalvage: { DoClicked_Salvage(); break; }
2479 case ButtonEnterMapLayer: { DoClicked_EnterMapLayer(); break; }
2480 //Wyrmgus end
2481 }
2482 }
2483
2484 /**
2485 ** Lookup key for bottom panel buttons.
2486 **
2487 ** @param key Internal key symbol for pressed key.
2488 **
2489 ** @return True, if button is handled (consumed).
2490 */
DoKey(int key)2491 int CButtonPanel::DoKey(int key)
2492 {
2493 SDL_keysym keysym;
2494 memset(&keysym, 0, sizeof(keysym));
2495 keysym.sym = (SDLKey)key;
2496 gcn::Key k = gcn::SDLInput::convertKeyCharacter(keysym);
2497 key = k.getValue();
2498
2499 if (!CurrentButtons.empty()) {
2500 // This is required for action queues SHIFT+M should be `m'
2501 if (isascii(key) && isupper(key)) {
2502 key = tolower(key);
2503 }
2504
2505 for (int i = 0; i < (int)UI.ButtonPanel.Buttons.size(); ++i) {
2506 //Wyrmgus start
2507 // if (CurrentButtons[i].Pos != -1 && key == CurrentButtons[i].Key) {
2508 if (CurrentButtons[i].Pos != -1 && key == CurrentButtons[i].GetKey()) {
2509 //Wyrmgus end
2510 UI.ButtonPanel.DoClicked(i);
2511 return 1;
2512 }
2513 }
2514 }
2515 return 0;
2516 }
2517
2518 //@}
2519