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