1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 // FIXME ostringstream is not so fast, move all string initialization into setup,
29 //       all ostringstream-related code should be called only one time in init
30 
31 // TODO translate comments
32 
33 #include "../game.h"
34 #include "../config/config.h"
35 #include "../platform/platform.h"
36 #include "../ui/font.h"
37 #include "../ui/cursor.h"
38 #include "../assets/audio.h"
39 #include "../assets/texture.h"
40 #include "../object3d/weapon/weapon.h"
41 #include "../object3d/space_ship/space_ship.h"
42 #include "../object3d/projectile/projectile.h"
43 #include <sstream>
44 #include <iomanip>
45 
46 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
47 namespace viewizard {
48 namespace astromenace {
49 
50 extern std::weak_ptr<cSpaceShip> WorkshopFighterGame;
51 extern cWeapon *WorkshopNewWeapon;
52 extern int	CurrentWorkshopNewWeapon;
53 extern float CurrentDeviationSum;
54 extern sVECTOR3D WorkShopPointCamera;
55 extern float CurrentAlert2;
56 extern float CurrentAlert3;
57 
58 void WorkshopCreateNewWeapon();
59 void WorkshopDrawShip(std::weak_ptr<cSpaceShip> &SpaceShip, int Mode);
60 void WorkshopDrawWeapon(cWeapon *Weapon);
61 
62 
63 // что рисовать в диалоге 6,7,8
64 extern cWeapon *DialogWeapon;
65 
66 
67 // флаг-тянем
68 bool DragWeapon = false;
69 // номер оружия, которое тянем
70 int DragWeaponNum = 0;
71 // уровень оружия, которое тянем
72 int DragWeaponLevel = 0;
73 // кол-во боекомплекта в оружии
74 int DragWeaponAmmo = 0;
75 // общее кол-во боекомплекта в заполненном боекомплекте
76 int DragWeaponAmmoStart = 0;
77 // управление
78 int DragWeaponControl = 0;
79 int DragWeaponAltControl = 0;
80 int DragWeaponAltControlData = 0;
81 
82 
83 // сюда вытягиваем данные
84 int	NewWeaponControlType = 0;
85 int	NewWeaponControlTypeData = 0;
86 
87 
88 
89 // вывод большого меню с настройкой оружия
90 // передаем только номер слота оружия
91 int WeaponSetupSlot = -1;
92 
93 // Номер, для проигрывания голосового сообщения об отсутствии снарядов в боекомплекте
94 unsigned int VoiceAmmoOut{0};
95 
96 
97 
98 
99 
100 
101 
NextWeaponGroup()102 int NextWeaponGroup()
103 {
104 	if (CurrentWorkshopNewWeapon>=1 && CurrentWorkshopNewWeapon<=4) return 5;
105 	if (CurrentWorkshopNewWeapon>=5 && CurrentWorkshopNewWeapon<=7) return 8;
106 	if (CurrentWorkshopNewWeapon>=8 && CurrentWorkshopNewWeapon<=10) return 11;
107 	if (CurrentWorkshopNewWeapon>=11 && CurrentWorkshopNewWeapon<=12) return 13;
108 	if (CurrentWorkshopNewWeapon>=13 && CurrentWorkshopNewWeapon<=13) return 14;
109 	if (CurrentWorkshopNewWeapon>=14 && CurrentWorkshopNewWeapon<=14) return 15;
110 	if (CurrentWorkshopNewWeapon>=15 && CurrentWorkshopNewWeapon<=15) return 16;
111 	if (CurrentWorkshopNewWeapon>=16 && CurrentWorkshopNewWeapon<=19) return 1;
112 
113 	return 0;
114 }
PrevWeaponGroup()115 int PrevWeaponGroup()
116 {
117 	if (CurrentWorkshopNewWeapon>=1 && CurrentWorkshopNewWeapon<=4) return 16;
118 	if (CurrentWorkshopNewWeapon>=5 && CurrentWorkshopNewWeapon<=7) return 1;
119 	if (CurrentWorkshopNewWeapon>=8 && CurrentWorkshopNewWeapon<=10) return 5;
120 	if (CurrentWorkshopNewWeapon>=11 && CurrentWorkshopNewWeapon<=12) return 8;
121 	if (CurrentWorkshopNewWeapon>=13 && CurrentWorkshopNewWeapon<=13) return 11;
122 	if (CurrentWorkshopNewWeapon>=14 && CurrentWorkshopNewWeapon<=14) return 13;
123 	if (CurrentWorkshopNewWeapon>=15 && CurrentWorkshopNewWeapon<=15) return 14;
124 	if (CurrentWorkshopNewWeapon>=16 && CurrentWorkshopNewWeapon<=19) return 15;
125 
126 	return 0;
127 }
128 
129 
130 
131 
132 
133 
GetWeaponBaseCost(int Num)134 int GetWeaponBaseCost(int Num)
135 {
136 	switch (Num) {
137 	case 1:
138 		return 150;
139 	case 2:
140 		return 300;
141 	case 3:
142 		return 1000;
143 	case 4:
144 		return 1500;
145 
146 	case 5:
147 		return 250;
148 	case 6:
149 		return 1000;
150 	case 7:
151 		return 1900;
152 
153 	case 8:
154 		return 1700;
155 	case 9:
156 		return 2500;
157 	case 10:
158 		return 4500;
159 
160 	case 11:
161 		return 2500;
162 	case 12:
163 		return 5000;
164 
165 	case 13:
166 		return 6000;
167 
168 	case 14:
169 		return 8000;
170 
171 	case 15:
172 		return 10000;
173 
174 	case 16:
175 		return 1500;
176 	case 17:
177 		return 4000;
178 	case 18:
179 		return 5000;
180 	case 19:
181 		return 8000;
182 
183 	default:
184 		return 0;
185 	}
186 }
GetWeaponCost(int Num,int Ammo,int AmmoStart)187 int GetWeaponCost(int Num, int Ammo, int AmmoStart)
188 {
189 	// получаем базовую стоимость оружия
190 	int Cost = GetWeaponBaseCost(Num);
191 	// находим базовую стоимость оружия
192 	int BaseCost = Cost/2;
193 	// берем половину стоимости, все четное, но на всякий случай так
194 	Cost -= BaseCost;
195 	// находим стоимость боекомплекта
196 	BaseCost += (int)(Cost*((Ammo*1.0f)/(AmmoStart*1.0f)));
197 
198 	return BaseCost;
199 }
GetWeaponReloadCost(int Num,int Ammo,int AmmoStart)200 int GetWeaponReloadCost(int Num, int Ammo, int AmmoStart)
201 {
202 	// получаем базовую стоимость оружия
203 	int Cost = GetWeaponBaseCost(Num);
204 	// находим базовую стоимость оружия
205 	int BaseCost = Cost/2;
206 	// берем половину стоимости, все четное, но на всякий случай так
207 	Cost -= BaseCost;
208 	// находим стоимость боекомплекта
209 	return Cost - (int)(Cost*((Ammo*1.0f)/(AmmoStart*1.0f)));
210 }
211 
212 
213 // название типа оружия землян
GetWeaponGroupTitle(int Num)214 const char *GetWeaponGroupTitle(int Num)
215 {
216 	switch (Num) {
217 	case 1:
218 	case 2:
219 	case 3:
220 	case 4:
221 		return "Kinetic";
222 	case 5:
223 	case 6:
224 	case 7:
225 		return "Ion";
226 	case 8:
227 	case 9:
228 	case 10:
229 		return "Plasma";
230 	case 11:
231 	case 12:
232 		return "Maser";
233 	case 13:
234 		return "Antimatter";
235 	case 14:
236 		return "Laser";
237 	case 15:
238 		return "Gauss";
239 
240 	case 16:
241 	case 17:
242 	case 18:
243 	case 19:
244 		return "Propelled";
245 
246 	default:
247 		std::cerr << __func__ << "(): " << "wrong Num.\n";
248 		break;
249 	}
250 	return nullptr;
251 }
252 
GetWeaponName(int Num)253 const char *GetWeaponName(int Num)
254 {
255 	switch (Num) {
256 	case 1:
257 		return "FireFox";
258 	case 2:
259 		return "FireBird";
260 	case 3:
261 		return "Meteor";
262 	case 4:
263 		return "Hasher";
264 
265 	case 5:
266 		return "Trammel";
267 	case 6:
268 		return "IonStorm";
269 	case 7:
270 		return "IonRipple";
271 
272 	case 8:
273 		return "Quasher";
274 	case 9:
275 		return "Destabilizer";
276 	case 10:
277 		return "Annihilator";
278 
279 	case 11:
280 		return "Devastator";
281 	case 12:
282 		return "DeathFlow";
283 
284 	case 13:
285 		return "Vaporizer";
286 
287 	case 14:
288 		return "Rapier";
289 
290 	case 15:
291 		return "Pandora";
292 
293 	case 16:
294 		return "Stinger";
295 	case 17:
296 		return "Swarm";
297 	case 18:
298 		return "Vengeance";
299 	case 19:
300 		return "BabyDoll";
301 
302 	default:
303 		std::cerr << __func__ << "(): " << "wrong Num.\n";
304 		break;
305 	}
306 
307 	return nullptr;
308 }
309 
310 
311 
312 
313 
GetWeaponIconName(int Num)314 std::string GetWeaponIconName(int Num)
315 {
316 	if ((Num < 1) ||
317 	    (Num > 19))
318 		std::cerr << __func__ << "(): " << "wrong Num.\n";
319 
320 	return "menu/weapon" + std::to_string(Num) + "_icon.tga";
321 }
322 
323 
324 
325 
326 // вся работа с еденичным слотом
ShipSlotWeapon(int SlotNum,int X,int Y)327 void ShipSlotWeapon(int SlotNum, int X, int Y)
328 {
329 	auto sharedWorkshopFighterGame = WorkshopFighterGame.lock();
330 	if (!sharedWorkshopFighterGame)
331 		return;
332 
333 	sRECT SrcRect(0, 0, 220, 128);
334 	int Xpos = X-45;
335 	int Ypos = Y-36;
336 	sRECT DstRect(Xpos, Ypos, Xpos + 220, Ypos + 128);
337 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/workshop_panel3.tga"), true, MenuContentTransp);
338 
339 
340 	// нужны предупреждения...
341 	bool WeaponAmmoOut = false;
342 
343 	// отображаем боекомплект
344 	if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
345 		int AmmoShow = (int)((56.0f*(sharedWeapon->AmmoStart-sharedWeapon->Ammo))/sharedWeapon->AmmoStart);
346 		// если меняли боекомплект и сделали его меньше, чтобы не вылазила линия боекомплекта...
347 		if (AmmoShow < 0)
348 			AmmoShow = 0;
349 
350 		SrcRect(0,AmmoShow,18,56);
351 		DstRect(Xpos+23,Ypos+40+AmmoShow,Xpos+18+23,Ypos+56+40);
352 		if (AmmoShow > 0)
353 			vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/ammo.tga"), true, CurrentAlert3*MenuContentTransp);
354 		else
355 			vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/ammo.tga"), true, MenuContentTransp);
356 
357 		if (sharedWeapon->Ammo == 0)
358 			WeaponAmmoOut = true;
359 	}
360 
361 	// запускаем голос...
362 	if (WeaponAmmoOut &&
363 	    !vw_IsSoundAvailable(VoiceAmmoOut)) // уже не играем, нужно запустить опять
364 		VoiceAmmoOut = PlayVoicePhrase(eVoicePhrase::WeaponMalfunction, 1.0f);
365 
366 
367 	// кнопка Setup
368 	int MouseX, MouseY;
369 	vw_GetMousePos(MouseX, MouseY);
370 
371 	// работаем с клавиатурой
372 	if ((MenuContentTransp >= 0.99f) && !isDialogBoxDrawing() && !DragWeapon) CurrentActiveMenuElement++;
373 	bool InFocusByKeyboard = false;
374 	if (CurrentKeyboardSelectMenuElement > 0) {
375 		if (CurrentKeyboardSelectMenuElement == CurrentActiveMenuElement) {
376 			InFocusByKeyboard = true;
377 		}
378 	}
379 
380 	SrcRect(0,0,18,56);
381 	DstRect(Xpos+23+154,Ypos+40,Xpos+18+23+154,Ypos+56+40);
382 	if  ((((DstRect.right  >= MouseX) &&
383 	       (DstRect.left<= MouseX) &&
384 	       (DstRect.bottom >= MouseY) &&
385 	       (DstRect.top<= MouseY)) || InFocusByKeyboard) && !isDialogBoxDrawing() && !DragWeapon) {
386 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset(vw_GetText("lang/en/menu/button_weaponry_in.tga")), true, MenuContentTransp);
387 		SetCursorStatus(eCursorStatus::ActionAllowed);
388 		if (vw_GetMouseLeftClick(true) || (InFocusByKeyboard && (vw_GetKeyStatus(SDLK_KP_ENTER) || vw_GetKeyStatus(SDLK_RETURN)))) {
389 			PlayMenuSFX(eMenuSFX::Click, 1.0f);
390 			WeaponSetupSlot = SlotNum;
391 			if (InFocusByKeyboard) {
392 				vw_SetKeyStatus(SDLK_KP_ENTER, false);
393 				vw_SetKeyStatus(SDLK_RETURN, false);
394 			}
395 		}
396 	} else
397 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset(vw_GetText("lang/en/menu/button_weaponry_out.tga")), true, MenuContentTransp);
398 
399 
400 
401 	// обработка перетягивания
402 	DstRect(X,Y,X+128,Y+64);
403 	if (vw_MouseOverRect(DstRect) && !isDialogBoxDrawing()) {
404 
405 		int Money = GameConfig().Profile[CurrentProfile].Money;
406 		if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock())
407 			Money += GetWeaponCost(sharedWeapon->InternalType,
408 					       sharedWeapon->Ammo,
409 					       sharedWeapon->AmmoStart);
410 
411 		// если отпустили тут
412 		if (!vw_GetMouseLeftClick(false) && DragWeapon) {
413 			// есть уровень слота соотв. уровню оружия
414 			if ((sharedWorkshopFighterGame->WeaponSlots[SlotNum].Type >= DragWeaponLevel) &&
415 			    // если стоимость меньше чем есть денег + стоимость оружия
416 			    (Money >= GetWeaponCost(DragWeaponNum, DragWeaponAmmo, DragWeaponAmmoStart))) {
417 				// звук устанавливаемого оружия
418 				PlayMenuSFX(eMenuSFX::DragInstallToSlot, 1.0f);
419 
420 				// если тут было оружие - сначало продаем его
421 				if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
422 					ChangeGameConfig().Profile[CurrentProfile].Money +=
423 						GetWeaponCost(sharedWeapon->InternalType,
424 							      sharedWeapon->Ammo,
425 							      sharedWeapon->AmmoStart);
426 					ReleaseWeapon(sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon);
427 					ChangeGameConfig().Profile[CurrentProfile].Weapon[SlotNum] = 0;
428 					ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = 0;
429 				}
430 
431 				// покупаем оружие
432 				ChangeGameConfig().Profile[CurrentProfile].Money -= GetWeaponCost(DragWeaponNum, DragWeaponAmmo, DragWeaponAmmoStart);
433 
434 				// все проверки сделали до этого, можем просто вызвать функцию, там 100% будет оружие
435 				SetEarthSpaceFighterWeapon(WorkshopFighterGame, SlotNum+1, DragWeaponNum);
436 				// убираем источник света
437 				if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
438 					if (auto sharedFire = sharedWeapon->Fire.lock())
439 						vw_ReleaseLight(sharedFire->Light);
440 
441 					ChangeGameConfig().Profile[CurrentProfile].Weapon[SlotNum] = DragWeaponNum;
442 					ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = DragWeaponAmmo;
443 					ChangeGameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] = DragWeaponControl;
444 					ChangeGameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] = DragWeaponAltControl;
445 					ChangeGameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum] = DragWeaponAltControlData;
446 					sharedWeapon->Ammo = DragWeaponAmmo;
447 					sharedWeapon->AmmoStart = DragWeaponAmmoStart;
448 
449 					// если не ракетная установка
450 					if (DragWeaponNum < 16) {
451 						sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum];
452 						sVECTOR3D NeedAngle = sharedWorkshopFighterGame->Rotation;
453 						NeedAngle.y += sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle;
454 						sharedWeapon->SetRotation(NeedAngle);
455 					} else
456 						ChangeGameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] = 0.0f;
457 				}
458 
459 				// чтобы оружие заняло свое место...
460 				sharedWorkshopFighterGame->Update(vw_GetTimeThread(0));
461 
462 				// сброс
463 				DragWeapon = false;
464 				DragWeaponNum = 0;
465 				DragWeaponLevel = 0;
466 				DragWeaponAmmo = 0;
467 				DragWeaponAmmoStart = 0;
468 				DragWeaponControl = 0;
469 				DragWeaponAltControl = 0;
470 				DragWeaponAltControlData = 0;
471 			} else {
472 				// особый случай - есть не соответствие, нужно проиграть звук неудачной установки
473 				PlayMenuSFX(eMenuSFX::DragError, 1.0f);
474 
475 				// сброс
476 				DragWeapon = false;
477 				DragWeaponNum = 0;
478 				DragWeaponLevel = 0;
479 				DragWeaponAmmo = 0;
480 				DragWeaponAmmoStart = 0;
481 				DragWeaponControl = 0;
482 				DragWeaponAltControl = 0;
483 				DragWeaponAltControlData = 0;
484 			}
485 		}
486 
487 
488 		// взяли оружие из слота, чтобы тащить - фактически продали его
489 		if (vw_GetMouseLeftClick(false) &&
490 		    !DragWeapon) {
491 			if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
492 				// звук снятия оружия из слота
493 				PlayMenuSFX(eMenuSFX::DragUninstallFromSlot, 1.0f);
494 
495 				DragWeapon = true;
496 				DragWeaponNum = sharedWeapon->InternalType;
497 				DragWeaponLevel = sharedWeapon->WeaponLevel;
498 				DragWeaponAmmo = sharedWeapon->Ammo;
499 				DragWeaponAmmoStart = sharedWeapon->AmmoStart;
500 				DragWeaponControl = GameConfig().Profile[CurrentProfile].WeaponControl[SlotNum];
501 				DragWeaponAltControl = GameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum];
502 				DragWeaponAltControlData = GameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum];
503 
504 				ChangeGameConfig().Profile[CurrentProfile].Money +=
505 					GetWeaponCost(sharedWeapon->InternalType,
506 						      sharedWeapon->Ammo,
507 						      sharedWeapon->AmmoStart);
508 				ReleaseWeapon(sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon);
509 				ChangeGameConfig().Profile[CurrentProfile].Weapon[SlotNum] = 0;
510 				ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = 0;
511 			}
512 		}
513 
514 
515 
516 		// если не тянем и в слоте что-то есть, показываем, что можем тянуть
517 		if (!DragWeapon &&
518 		    !sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.expired())
519 			SetCursorStatus(eCursorStatus::ActionAllowed);
520 	}
521 
522 
523 
524 
525 	// рисуем можем ставить в слот или нет
526 	bool CanOn = false;
527 	// проверка по уровню слота
528 	if (DragWeapon && (sharedWorkshopFighterGame->WeaponSlots[SlotNum].Type >= DragWeaponLevel))
529 		CanOn = true;
530 	if (!DragWeapon && (sharedWorkshopFighterGame->WeaponSlots[SlotNum].Type >= WorkshopNewWeapon->WeaponLevel))
531 		CanOn = true;
532 
533 	SrcRect(0,0,128,64);
534 	DstRect(X,Y,X+128,Y+64);
535 
536 	if (CanOn) {
537 		if (!WeaponAmmoOut) {
538 			bool NeedAlert = false;
539 			// если есть хоть 1 слот меньшего уровня
540 			for (unsigned i=0; i<sharedWorkshopFighterGame->WeaponSlots.size(); i++) {
541 				// если что-то тянем, смотрим по нему...
542 				if (DragWeapon) {
543 					if (sharedWorkshopFighterGame->WeaponSlots[i].Type < DragWeaponLevel) NeedAlert = true;
544 				} else {
545 					if (sharedWorkshopFighterGame->WeaponSlots[i].Type < WorkshopNewWeapon->WeaponLevel) NeedAlert = true;
546 				}
547 			}
548 
549 			if (NeedAlert)
550 				vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/weapon_on_icon.tga"), true, CurrentAlert3*MenuContentTransp, 0.0f, sRGBCOLOR{eRGBCOLOR::green});
551 			else
552 				vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/weapon_on_icon.tga"), true, MenuContentTransp, 0.0f, sRGBCOLOR{eRGBCOLOR::green});
553 		} else
554 			vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/weapon_on_icon.tga"), true, CurrentAlert3*MenuContentTransp, 0.0f, sRGBCOLOR{eRGBCOLOR::red});
555 	} else
556 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/weapon_on_icon.tga"), true, MenuContentTransp, 0.0f, sRGBCOLOR{eRGBCOLOR::orange});
557 
558 
559 
560 
561 
562 	// прорисовка
563 	if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
564 		SrcRect(0,0,128,64);
565 		DstRect(X,Y,X+128,Y+64);
566 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset(GetWeaponIconName(sharedWeapon->InternalType)), true, MenuContentTransp);
567 	} else {
568 		// пустой слот, рисуем его
569 
570 		DstRect(X,Y,X+128,Y+64);
571 
572 		int Size = vw_TextWidthUTF32(vw_GetTextUTF32("EMPTY"));
573 		float WScale = 0;
574 		if (Size > 88) {
575 			Size = 88;
576 			WScale = -88;
577 		}
578 		vw_DrawTextUTF32(DstRect.left+(DstRect.right-DstRect.left-Size)/2, DstRect.bottom-53, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, 0.7*MenuContentTransp, vw_GetTextUTF32("EMPTY"));
579 
580 		std::ostringstream tmpStream;
581 		tmpStream << std::fixed << std::setprecision(0)
582 			  << vw_GetText("level") << " " << sharedWorkshopFighterGame->WeaponSlots[SlotNum].Type;
583 		Size = vw_TextWidth(tmpStream.str());
584 		WScale = 0;
585 		if (Size > 88) {
586 			Size = 88;
587 			WScale = -88;
588 		}
589 		vw_DrawText(DstRect.left+(DstRect.right-DstRect.left-Size)/2, DstRect.bottom-32, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, 0.7*MenuContentTransp, tmpStream.str());
590 	}
591 
592 }
593 
594 
595 
596 
597 
598 
ShipSlotSetupWeapon(int SlotNum)599 void ShipSlotSetupWeapon(int SlotNum)
600 {
601 	auto sharedWorkshopFighterGame = WorkshopFighterGame.lock();
602 	if (!sharedWorkshopFighterGame)
603 		return;
604 
605 	sRECT SrcRect, DstRect;
606 
607 	// проверяем, нужно ли вытягивать что-то или нет...
608 	CheckMouseKeybJState();
609 
610 	SrcRect(0,0,404,570);
611 	int Xpos = GameConfig().InternalWidth / 2 + 55;
612 	int Ypos = 50-10;
613 	DstRect(Xpos,Ypos,Xpos+404,Ypos+570);
614 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/workshop_panel5.tga"), true, MenuContentTransp);
615 
616 
617 
618 	if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
619 		Ypos += 33;
620 		// выводим боекомплект   текущий/полный
621 		Xpos = GameConfig().InternalWidth / 2 + 55 + 50;
622 		vw_DrawTextUTF32(Xpos, Ypos, -170, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Weapon Ammo:"));
623 		std::ostringstream tmpStream;
624 		tmpStream << std::fixed << std::setprecision(0)
625 			  << sharedWeapon->Ammo << "/" << sharedWeapon->AmmoStart;
626 		Xpos = (GameConfig().InternalWidth/2+512)-55 - 50 - vw_TextWidth(tmpStream.str());
627 
628 
629 		// если все нормально - белым... иначе подмаргиваем
630 		float tmpTransp{MenuContentTransp};
631 		sRGBCOLOR tmpColor{eRGBCOLOR::white};
632 		if (sharedWeapon->Ammo < sharedWeapon->AmmoStart) {
633 			tmpTransp = MenuContentTransp *CurrentAlert3;
634 			tmpColor = sRGBCOLOR{eRGBCOLOR::orange};
635 		}
636 		vw_DrawText(Xpos, Ypos, 0, 0, 1.0f, tmpColor, tmpTransp, tmpStream.str());
637 
638 
639 		// стоимость перезарядки
640 		Xpos = GameConfig().InternalWidth / 2 + 55 + 50;
641 		Ypos += 30;
642 		vw_DrawTextUTF32(Xpos, Ypos, -230, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Weapon Reload Cost:"));
643 		// находим стоимость перезарядки
644 		int ReloadCost = GetWeaponReloadCost(sharedWeapon->InternalType,
645 						     sharedWeapon->Ammo,
646 						     sharedWeapon->AmmoStart);
647 		tmpTransp = MenuContentTransp;
648 		tmpColor = sRGBCOLOR{eRGBCOLOR::white};
649 		tmpStream.clear();
650 		tmpStream.str(std::string{});
651 		tmpStream << ReloadCost;
652 		Xpos = (GameConfig().InternalWidth/2+512)-55 - 50 - vw_TextWidth(tmpStream.str());
653 		if (ReloadCost != 0) {
654 			tmpTransp = MenuContentTransp * CurrentAlert3;
655 			tmpColor = sRGBCOLOR{eRGBCOLOR::orange};
656 		}
657 		vw_DrawText(Xpos, Ypos, 0, 0, 1.0f, tmpColor, tmpTransp, tmpStream.str());
658 
659 
660 		// кнопка перезарядить оружие
661 		Ypos += 40;
662 		if (DrawButton200_2(GameConfig().InternalWidth / 2 + 155, Ypos, vw_GetTextUTF32("Reload"), MenuContentTransp, (ReloadCost == 0) || GameConfig().Profile[CurrentProfile].Money<ReloadCost)) {
663 			sharedWeapon->Ammo = sharedWeapon->AmmoStart;
664 			ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = sharedWeapon->Ammo;
665 			ChangeGameConfig().Profile[CurrentProfile].Money -= ReloadCost;
666 		}
667 
668 
669 
670 		// настройка управления для этого оружия
671 
672 		bool Status1 = false;
673 		bool Status2 = false;
674 
675 		Xpos = GameConfig().InternalWidth/2+55+34 + 16;
676 		Ypos += 60;
677 		vw_DrawTextUTF32(Xpos, Ypos, -300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Weapon Fire Control:"));
678 		// вкл-выкл первичного управления
679 		if ((GameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] == 1) ||
680 		    (GameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] == 3))
681 			Status1 = true;
682 		Xpos = GameConfig().InternalWidth/2+55+54 + 16;
683 		Ypos += 30;
684 		DrawCheckBox(Xpos,Ypos, Status1, vw_GetTextUTF32("Primary Attack"), MenuContentTransp);
685 		// вкл-выкл вторичного управления
686 		if ((GameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] == 2) ||
687 		    (GameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] == 3))
688 			Status2 = true;
689 		Xpos = GameConfig().InternalWidth/2+55+54 + 16;
690 		Ypos += 40;
691 		DrawCheckBox(Xpos,Ypos, Status2, vw_GetTextUTF32("Secondary Attack"), MenuContentTransp);
692 		// получаем данны обратно
693 		ChangeGameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] = 0;
694 		if (Status1)
695 			ChangeGameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] += 1;
696 		if (Status2)
697 			ChangeGameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] += 2;
698 
699 		// получение альтернативного управления
700 		Xpos = GameConfig().InternalWidth/2+55+34 + 16;
701 		Ypos += 40;
702 		vw_DrawTextUTF32(Xpos, Ypos, -300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Alternative Fire Control:"));
703 
704 		Ypos += 40;
705 
706 		std::string TextTmp{"?"};
707 		// установка надписи на кнопке
708 		if (NeedCheck != 100) {
709 			if (NewWeaponControlType != 0) {
710 				ChangeGameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] = NewWeaponControlType;
711 				ChangeGameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum] = NewWeaponControlTypeData;
712 				NewWeaponControlType = 0;
713 				NewWeaponControlTypeData = 0;
714 			}
715 			if (GameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] != 0) {
716 				if (GameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] == 1)
717 					TextTmp = SDL_GetKeyName(GameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum]);
718 				if (GameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] == 2)
719 					TextTmp = MouseButtonName(GameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum]);
720 				if (GameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] == 3)
721 					TextTmp = JoystickButtonName(GameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum]);
722 
723 			} else
724 				TextTmp = vw_GetText("Click to setup");
725 		}
726 
727 		// собственно сама кнопка
728 		float Transp = 1.0f;
729 		bool Off = false;
730 		if (NeedCheck == 100) {
731 			Transp = But[1];
732 			Off = true;
733 		}
734 		if (DrawButton200_2(GameConfig().InternalWidth / 2+155, Ypos, ConvertUTF8.from_bytes(TextTmp), Transp * MenuContentTransp, Off)) {
735 			NeedCheck = 100;
736 			vw_ResetMouseButtons();
737 			NewWeaponControlType = 0;
738 			NewWeaponControlTypeData = 0;
739 		}
740 
741 
742 
743 		if (GameConfig().Profile[CurrentProfile].Weapon[SlotNum] < 16) {
744 
745 			// выводим угол поворота ствола
746 			Xpos = GameConfig().InternalWidth/2+55+34 + 16;
747 			Ypos += 60;
748 			tmpStream.clear();
749 			tmpStream.str(std::string{});
750 			tmpStream << vw_GetText("Weapon Angle:") << " "
751 				  << GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum];
752 			vw_DrawText(Xpos, Ypos, -300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, tmpStream.str());
753 			Ypos += 40;
754 
755 			float Min = 0.0f;
756 			float Max = 0.0f;
757 			GetShipWeaponSlotAngle(GameConfig().Profile[CurrentProfile].ShipHull, SlotNum, Min, Max);
758 
759 
760 			if (GameConfig().Profile[CurrentProfile].TargetingSystem <= 2) {
761 				vw_DrawTextUTF32(Xpos, Ypos-15, 300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, 1.0f, vw_GetTextUTF32("Custom Weapon Angle is use-"));
762 				vw_DrawTextUTF32(Xpos, Ypos+5, 300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, 1.0f, vw_GetTextUTF32("ful with optical computer"));
763 				vw_DrawTextUTF32(Xpos, Ypos+25, 300, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, 1.0f, vw_GetTextUTF32("system Neo or Supra only."));
764 			} else {
765 				if (DrawButton128_2(GameConfig().InternalWidth/2+118, Ypos, vw_GetTextUTF32("Left"), MenuContentTransp, GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] <= Min)) {
766 					ChangeGameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] -= 5.0f;
767 					sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum];
768 
769 					sharedWeapon->SetRotation(sharedWeapon->Rotation^(-1));
770 					sVECTOR3D NeedAngle = sharedWorkshopFighterGame->Rotation;
771 					NeedAngle.y += sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle;
772 					sharedWeapon->SetRotation(NeedAngle);
773 				}
774 				if (DrawButton128_2(GameConfig().InternalWidth/2+266, Ypos, vw_GetTextUTF32("Right"), MenuContentTransp, GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] >= Max)) {
775 					ChangeGameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] += 5.0f;
776 					sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum];
777 
778 					sharedWeapon->SetRotation(sharedWeapon->Rotation^(-1));
779 					sVECTOR3D NeedAngle = sharedWorkshopFighterGame->Rotation;
780 					NeedAngle.y += sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle;
781 					sharedWeapon->SetRotation(NeedAngle);
782 				}
783 			}
784 		}
785 	} else {
786 		Xpos += 74;
787 		Ypos += 128;
788 		// пустой слот, рисуем его
789 		SrcRect(0,0,256,256);
790 		DstRect(Xpos,Ypos,Xpos+256,Ypos+256);
791 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/weapon_empty_icon.tga"), true, MenuContentTransp);
792 
793 		int Size = vw_TextWidthUTF32(vw_GetTextUTF32("Empty Weapon Slot"));
794 		float WScale = 0;
795 		if (Size > 228) {
796 			Size = 228;
797 			WScale = -228;
798 		}
799 		vw_DrawTextUTF32(DstRect.left+(DstRect.right-DstRect.left-Size)/2, DstRect.bottom-40, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Empty Weapon Slot"));
800 	}
801 
802 
803 
804 
805 
806 	// обработка перетягивания
807 	Xpos = GameConfig().InternalWidth / 2 + 55;
808 	Ypos = 50-10;
809 	DstRect(Xpos+10,Ypos+10,Xpos+404-10,Ypos+570-10);
810 	if (vw_MouseOverRect(DstRect) && !isDialogBoxDrawing()) {
811 
812 		int Money = GameConfig().Profile[CurrentProfile].Money;
813 		if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock())
814 			Money += GetWeaponCost(sharedWeapon->InternalType,
815 					       sharedWeapon->Ammo,
816 					       sharedWeapon->AmmoStart);
817 
818 		// если отпустили тут
819 		if (!vw_GetMouseLeftClick(false) && DragWeapon) {
820 			// есть уровень слота соотв. уровню оружия
821 			if ((sharedWorkshopFighterGame->WeaponSlots[SlotNum].Type >= DragWeaponLevel) &&
822 			    // если стоимость меньше чем есть денег + стоимость оружия
823 			    (Money >= GetWeaponCost(DragWeaponNum, DragWeaponAmmo, DragWeaponAmmoStart))) {
824 				// звук устанавливаемого оружия
825 				PlayMenuSFX(eMenuSFX::DragInstallToSlot, 1.0f);
826 
827 				// если тут было оружие - сначало продаем его
828 				if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
829 					ChangeGameConfig().Profile[CurrentProfile].Money +=
830 							GetWeaponCost(sharedWeapon->InternalType,
831 								      sharedWeapon->Ammo,
832 								      sharedWeapon->AmmoStart);
833 					ReleaseWeapon(sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon);
834 					ChangeGameConfig().Profile[CurrentProfile].Weapon[SlotNum] = 0;
835 					ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = 0;
836 				}
837 
838 				// покупаем оружие
839 				ChangeGameConfig().Profile[CurrentProfile].Money -= GetWeaponCost(DragWeaponNum, DragWeaponAmmo, DragWeaponAmmoStart);
840 
841 				// все проверки сделали до этого, можем просто вызвать функцию, там 100% будет оружие
842 				SetEarthSpaceFighterWeapon(WorkshopFighterGame, SlotNum+1, DragWeaponNum);
843 				// убираем источник света
844 				if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[SlotNum].Weapon.lock()) {
845 					if (auto sharedFire = sharedWeapon->Fire.lock())
846 						vw_ReleaseLight(sharedFire->Light);
847 
848 					ChangeGameConfig().Profile[CurrentProfile].Weapon[SlotNum] = DragWeaponNum;
849 					ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[SlotNum] = DragWeaponAmmo;
850 					ChangeGameConfig().Profile[CurrentProfile].WeaponControl[SlotNum] = DragWeaponControl;
851 					ChangeGameConfig().Profile[CurrentProfile].WeaponAltControl[SlotNum] = DragWeaponAltControl;
852 					ChangeGameConfig().Profile[CurrentProfile].WeaponAltControlData[SlotNum] = DragWeaponAltControlData;
853 					sharedWeapon->Ammo = DragWeaponAmmo;
854 					sharedWeapon->AmmoStart = DragWeaponAmmoStart;
855 
856 					// если не ракетная установка
857 					if (DragWeaponNum < 16) {
858 						sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum];
859 						sVECTOR3D NeedAngle = sharedWorkshopFighterGame->Rotation;
860 						NeedAngle.y += sharedWorkshopFighterGame->WeaponSlots[SlotNum].YAngle;
861 						sharedWeapon->SetRotation(NeedAngle);
862 					} else
863 						ChangeGameConfig().Profile[CurrentProfile].WeaponSlotYAngle[SlotNum] = 0.0f;
864 				}
865 
866 				// чтобы оружие заняло свое место...
867 				sharedWorkshopFighterGame->Update(vw_GetTimeThread(0));
868 
869 				// сброс
870 				DragWeapon = false;
871 				DragWeaponNum = 0;
872 				DragWeaponLevel = 0;
873 				DragWeaponAmmo = 0;
874 				DragWeaponAmmoStart = 0;
875 				DragWeaponControl = 0;
876 				DragWeaponAltControl = 0;
877 				DragWeaponAltControlData = 0;
878 			} else {
879 				// особый случай - есть не соответствие, нужно проиграть звук неудачной установки
880 				PlayMenuSFX(eMenuSFX::DragError, 1.0f);
881 
882 				// сброс
883 				DragWeapon = false;
884 				DragWeaponNum = 0;
885 				DragWeaponLevel = 0;
886 				DragWeaponAmmo = 0;
887 				DragWeaponAmmoStart = 0;
888 				DragWeaponControl = 0;
889 				DragWeaponAltControl = 0;
890 				DragWeaponAltControlData = 0;
891 			}
892 		}
893 
894 	}
895 
896 
897 
898 
899 
900 	//кнопка закрыть
901 	if (DrawButton200_2(GameConfig().InternalWidth / 2 + 155, 533, vw_GetTextUTF32("Close"), MenuContentTransp, false)) {
902 		WeaponSetupSlot = -1;
903 		NeedCheck = 0;
904 	}
905 
906 
907 }
908 
909 
910 
911 
912 
913 
914 
915 
916 
917 
918 
919 
920 /*
921  * Draw weapon slots.
922  */
DrawWeaponSlots(std::weak_ptr<cSpaceShip> & SpaceShip)923 static void DrawWeaponSlots(std::weak_ptr<cSpaceShip> &SpaceShip)
924 {
925 	auto sharedSpaceShip = SpaceShip.lock();
926 	if (!sharedSpaceShip ||
927 	    sharedSpaceShip->WeaponSlots.empty())
928 		return;
929 
930 	// FIXME move this calculation to the "weapon slots" init, should be called one time only
931 	float tmpInvMatrix[9];
932 	memcpy(tmpInvMatrix,
933 	       sharedSpaceShip->CurrentRotationMat,
934 	       9 * sizeof(sharedSpaceShip->CurrentRotationMat[0]));
935 	vw_Matrix33InverseRotate(tmpInvMatrix);
936 
937 	sVECTOR3D tmpInitialLocation = sharedSpaceShip->WeaponSlots[0].Location;
938 	vw_Matrix33CalcPoint(tmpInitialLocation, tmpInvMatrix);
939 	float tmpLastX{tmpInitialLocation.x};
940 	float tmpLastZ{tmpInitialLocation.z};
941 	std::vector<std::vector<int>> Lines{{0}}; // lines with weapon slots indexes
942 	constexpr float Epsilon{0.01f}; // denote a small quantity, which will be taken to zero
943 
944 	// note, we already have properly sorted weapon slots array here, so, all we need:
945 	// 1) check for Z
946 	// 2) in case Z is the same, check for X (since slots may have same Z, but different abx(x))
947 	for (unsigned i = 1; i < sharedSpaceShip->WeaponSlots.size(); i++) {
948 		// revert back rotation + ship shaking effect, since we need initial weapon slot location
949 		// FIXME move this calculation to the "weapon slots" init, should be called one time only
950 		tmpInitialLocation = sharedSpaceShip->WeaponSlots[i].Location;
951 		vw_Matrix33CalcPoint(tmpInitialLocation, tmpInvMatrix);
952 
953 		if (std::fabs(tmpInitialLocation.z - tmpLastZ) > Epsilon) {
954 			tmpLastX = tmpInitialLocation.x;
955 			tmpLastZ = tmpInitialLocation.z;
956 			Lines.emplace_back();
957 		// in case of weapon slots pair, that located symmetrically, (tmpInitialLocation.x + tmpLastX) should be zero
958 		} else if (std::fabs(tmpInitialLocation.x + tmpLastX) > Epsilon) {
959 			tmpLastX = tmpInitialLocation.x;
960 			tmpLastZ = tmpInitialLocation.z;
961 			Lines.emplace_back();
962 		}
963 
964 		Lines.back().emplace_back(i);
965 	}
966 
967 	// FIXME hardcoded up to 3 lines only, max 2 slots per line
968 
969 	int tmpStartY{130};
970 	int tmpOffsetY{200};
971 	if (Lines.size() == 1)
972 		tmpStartY = 330;
973 	else if (Lines.size() == 2)
974 		tmpOffsetY = 400;
975 
976 	for (unsigned i = 0; i < Lines.size(); i++) {
977 		int tmpStartX{static_cast<int>(GameConfig().InternalWidth / 2) + 50};
978 		int tmpOffsetX{512 - 128 - 100};
979 		if (Lines[i].size() == 1)
980 			tmpStartX = GameConfig().InternalWidth / 2 + 256 - 64;
981 
982 		for (unsigned j = 0; j < Lines[i].size(); j++) {
983 			ShipSlotWeapon(Lines[i][j], tmpStartX + tmpOffsetX * j, tmpStartY + tmpOffsetY * i);
984 		}
985 	}
986 }
987 
988 
989 
GetShipWeaponsMaxSlotLevel()990 int GetShipWeaponsMaxSlotLevel()
991 {
992 	int max = 1;
993 
994 	if (auto sharedWorkshopFighterGame = WorkshopFighterGame.lock()) {
995 		for (const auto &tmpWeaponSlot : sharedWorkshopFighterGame->WeaponSlots) {
996 			if (tmpWeaponSlot.Type > max)
997 				max = tmpWeaponSlot.Type;
998 		}
999 	}
1000 
1001 	return max;
1002 }
1003 
1004 
1005 
1006 
1007 
1008 
1009 
1010 
1011 
1012 
1013 
1014 
1015 
1016 
1017 
1018 
1019 
1020 //------------------------------------------------------------------------------------
1021 // покупка-установка и ремонт вооружения корабля
1022 //------------------------------------------------------------------------------------
Workshop_Weaponry()1023 void Workshop_Weaponry()
1024 {
1025 	sRECT SrcRect, DstRect;
1026 
1027 
1028 
1029 	// если нажали - установка и тянем
1030 	DstRect(GameConfig().InternalWidth/2-416, 100+32, GameConfig().InternalWidth/2-96, 450-32);
1031 	if (vw_MouseOverRect(DstRect) && !isDialogBoxDrawing())
1032 		if (!DragWeapon) {
1033 			SetCursorStatus(eCursorStatus::ActionAllowed);
1034 
1035 			if (vw_GetMouseLeftClick(false)) {
1036 				// звук взяли оружие
1037 				PlayMenuSFX(eMenuSFX::DragUninstallFromSlot, 1.0f);
1038 
1039 				// установка
1040 				DragWeapon = true;
1041 				DragWeaponNum = CurrentWorkshopNewWeapon;
1042 				DragWeaponLevel = WorkshopNewWeapon->WeaponLevel;
1043 				DragWeaponAmmo = WorkshopNewWeapon->Ammo;
1044 				DragWeaponAmmoStart = WorkshopNewWeapon->AmmoStart;
1045 				// второй по умолчанию только для рокет
1046 				if (CurrentWorkshopNewWeapon<=15) DragWeaponControl = 1;
1047 				else DragWeaponControl = 2;
1048 				DragWeaponAltControl = 0;
1049 				DragWeaponAltControlData = 0;
1050 
1051 			}
1052 		}
1053 
1054 
1055 	// затемнение
1056 	SrcRect(0,0,256,256);
1057 	DstRect(GameConfig().InternalWidth/2-480, 100-32, GameConfig().InternalWidth/2-32, 450+32);
1058 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/back_spot2.tga"), true, 0.45f * MenuContentTransp);
1059 	DstRect(GameConfig().InternalWidth / 2, 0, GameConfig().InternalWidth/2+512, 622);
1060 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/back_spot.tga"), true, 0.35f * MenuContentTransp);
1061 
1062 
1063 	vw_End2DMode();
1064 	WorkshopDrawShip(WorkshopFighterGame, 4);
1065 	WorkshopDrawWeapon(WorkshopNewWeapon);
1066 	vw_Start2DMode(-1,1);
1067 
1068 
1069 	// вывод названия
1070 	vw_DrawTextUTF32(GameConfig().InternalWidth/2-438, 50+6, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, MenuContentTransp, vw_GetTextUTF32(GetWeaponName(CurrentWorkshopNewWeapon)));
1071 	if (DrawButton128_2(GameConfig().InternalWidth/2-197, 50, vw_GetTextUTF32("Info"), MenuContentTransp, false)) {
1072 		SetCurrentDialogBox(eDialogBox::ShowWeaponsInfo);
1073 		DialogWeapon = WorkshopNewWeapon;
1074 	}
1075 
1076 	std::ostringstream tmpStream;
1077 	tmpStream << std::fixed << std::setprecision(0)
1078 		  << vw_GetText("Weapon Type") << ": ";
1079 	vw_DrawText(GameConfig().InternalWidth/2-438, 110, -170, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, tmpStream.str());
1080 	vw_DrawTextUTF32(GameConfig().InternalWidth/2-438+175, 110, -184, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32(GetWeaponGroupTitle(CurrentWorkshopNewWeapon)));
1081 
1082 
1083 	int k2 = 0;
1084 	if (GetProjectileDamageKinetic(WorkshopNewWeapon->InternalType) > 0.0f) {
1085 		vw_DrawTextUTF32(GameConfig().InternalWidth/2-438, 130, -170, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Damage, Kinetic:"));
1086 		tmpStream.clear();
1087 		tmpStream.str(std::string{});
1088 		tmpStream << GetProjectileDamageKinetic(WorkshopNewWeapon->InternalType) << " ";
1089 		if ((WorkshopNewWeapon->InternalType == 11) ||
1090 		    (WorkshopNewWeapon->InternalType == 12) ||
1091 		    (WorkshopNewWeapon->InternalType == 14))
1092 			tmpStream << vw_GetText("units/sec");
1093 		else
1094 			tmpStream << vw_GetText("units/shot");
1095 		vw_DrawText(GameConfig().InternalWidth/2-438+175, 130, -184, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, tmpStream.str());
1096 
1097 		k2=20;
1098 	}
1099 	if (GetProjectileDamageEM(WorkshopNewWeapon->InternalType) > 0.0f) {
1100 		vw_DrawTextUTF32(GameConfig().InternalWidth/2-438, 130+k2, -170, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Damage, EM:"));
1101 		tmpStream.clear();
1102 		tmpStream.str(std::string{});
1103 		tmpStream << GetProjectileDamageEM(WorkshopNewWeapon->InternalType) << " ";
1104 		if ((WorkshopNewWeapon->InternalType == 11) ||
1105 		    (WorkshopNewWeapon->InternalType == 12) ||
1106 		    (WorkshopNewWeapon->InternalType == 14))
1107 			tmpStream << vw_GetText("units/sec");
1108 		else
1109 			tmpStream << vw_GetText("units/shot");
1110 		vw_DrawText(GameConfig().InternalWidth/2-438+175, 130+k2, -184, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, tmpStream.str());
1111 
1112 	}
1113 
1114 
1115 
1116 	// вывод уровня оружия
1117 	float tmpTransp{MenuContentTransp};
1118 	sRGBCOLOR tmpColor{eRGBCOLOR::green};
1119 	tmpStream.clear();
1120 	tmpStream.str(std::string{});
1121 	tmpStream << vw_GetText("Weapon Level") << ": " << WorkshopNewWeapon->WeaponLevel;
1122 	if (WorkshopNewWeapon->WeaponLevel > GetShipWeaponsMaxSlotLevel()) {
1123 		tmpTransp = MenuContentTransp * CurrentAlert3;
1124 		tmpColor = sRGBCOLOR{eRGBCOLOR::orange};
1125 	}
1126 	vw_DrawText(GameConfig().InternalWidth/2-438, 400, 0, 0, 1.0f, tmpColor, tmpTransp, tmpStream.str());
1127 
1128 	// вывод стоимости
1129 	tmpTransp = MenuContentTransp;
1130 	tmpColor = sRGBCOLOR{eRGBCOLOR::yellow};
1131 	tmpStream.clear();
1132 	tmpStream.str(std::string{});
1133 	tmpStream << vw_GetText("Weapon Cost") << ": " << GetWeaponBaseCost(CurrentWorkshopNewWeapon);
1134 	if (GameConfig().Profile[CurrentProfile].Money < GetWeaponBaseCost(CurrentWorkshopNewWeapon)) {
1135 		tmpTransp = MenuContentTransp * CurrentAlert3;
1136 		tmpColor = sRGBCOLOR{eRGBCOLOR::red};
1137 	}
1138 	vw_DrawText(GameConfig().InternalWidth/2-438, 420, 0, 0, 1.0f, tmpColor, tmpTransp, tmpStream.str());
1139 
1140 
1141 
1142 	// рамки
1143 	SrcRect(0,0,400,35 );
1144 	DstRect(GameConfig().InternalWidth/2-457, 100-11, GameConfig().InternalWidth/2-57, 100+35-11);
1145 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/workshop_panel4.tga"), true, MenuContentTransp);
1146 
1147 	SrcRect(0,0,400,173 );
1148 	DstRect(GameConfig().InternalWidth/2-457, 450-13, GameConfig().InternalWidth/2-57, 450+173-13);
1149 	vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/workshop_panel1.tga"), true, MenuContentTransp);
1150 
1151 
1152 
1153 
1154 
1155 	// проверяем колесо мышки
1156 	DstRect(GameConfig().InternalWidth/2-457, 100+35-11, GameConfig().InternalWidth/2-57, 450-13);
1157 	if (vw_MouseOverRect(DstRect)) {
1158 		if ((vw_GetWheelStatus() != 0) && !isDialogBoxDrawing()) {
1159 			CurrentWorkshopNewWeapon += vw_GetWheelStatus();
1160 
1161 			if (CurrentWorkshopNewWeapon < 1)
1162 				CurrentWorkshopNewWeapon = 19;
1163 			if (CurrentWorkshopNewWeapon > 19)
1164 				CurrentWorkshopNewWeapon = 1;
1165 			WorkshopCreateNewWeapon();
1166 
1167 			vw_ResetWheelStatus();
1168 		}
1169 	} else if (vw_GetWheelStatus() != 0) {
1170 		vw_ResetWheelStatus();
1171 	}
1172 
1173 
1174 	if (DrawButton128_2(GameConfig().InternalWidth/2-395,482, vw_GetTextUTF32("Prev"), MenuContentTransp, false)) {
1175 		CurrentWorkshopNewWeapon--;
1176 		if (CurrentWorkshopNewWeapon < 1)
1177 			CurrentWorkshopNewWeapon = 19;
1178 		WorkshopCreateNewWeapon();
1179 	}
1180 	if (DrawButton128_2(GameConfig().InternalWidth/2-247,482, vw_GetTextUTF32("Next"), MenuContentTransp, false)) {
1181 		CurrentWorkshopNewWeapon++;
1182 		if (CurrentWorkshopNewWeapon > 19)
1183 			CurrentWorkshopNewWeapon = 1;
1184 		WorkshopCreateNewWeapon();
1185 	}
1186 
1187 
1188 	if (DrawButton128_2(GameConfig().InternalWidth/2-395,533, vw_GetTextUTF32(GetWeaponGroupTitle(PrevWeaponGroup())), MenuContentTransp, false)) {
1189 		CurrentWorkshopNewWeapon = PrevWeaponGroup();
1190 		WorkshopCreateNewWeapon();
1191 	}
1192 	if (DrawButton128_2(GameConfig().InternalWidth/2-247,533, vw_GetTextUTF32(GetWeaponGroupTitle(NextWeaponGroup())), MenuContentTransp, false)) {
1193 		CurrentWorkshopNewWeapon = NextWeaponGroup();
1194 		WorkshopCreateNewWeapon();
1195 	}
1196 
1197 	vw_SetFontSize(24);
1198 	vw_DrawTextUTF32(GameConfig().InternalWidth/2-445, 600, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Weapon Stock"));
1199 	ResetFontSize();
1200 
1201 	if (WeaponSetupSlot == -1)
1202 		DrawWeaponSlots(WorkshopFighterGame);
1203 	else
1204 		ShipSlotSetupWeapon(WeaponSetupSlot);
1205 
1206 
1207 	// кнопка "перезарядить все" оружие
1208 	if (WeaponSetupSlot == -1) {
1209 		if (auto sharedWorkshopFighterGame = WorkshopFighterGame.lock()) {
1210 			int ReloadCost = 0;
1211 			// находим стоимость перезарядки
1212 			for (const auto &tmpWeaponSlot : sharedWorkshopFighterGame->WeaponSlots) {
1213 				if (auto sharedWeapon = tmpWeaponSlot.Weapon.lock())
1214 					ReloadCost += GetWeaponReloadCost(sharedWeapon->InternalType,
1215 									  sharedWeapon->Ammo,
1216 									  sharedWeapon->AmmoStart);
1217 			}
1218 			std::u32string ButtonName = ConvertUTF8.from_bytes(std::string(vw_GetText("Reload All")) + ": " + std::to_string(ReloadCost));
1219 
1220 			if (DrawButton200_2(GameConfig().InternalWidth/2+153, 50, ButtonName, MenuContentTransp, (ReloadCost == 0) || (GameConfig().Profile[CurrentProfile].Money < ReloadCost))) {
1221 				for (unsigned i = 0; i < sharedWorkshopFighterGame->WeaponSlots.size(); i++) {
1222 					if (auto sharedWeapon = sharedWorkshopFighterGame->WeaponSlots[i].Weapon.lock()) {
1223 						sharedWeapon->Ammo = sharedWeapon->AmmoStart;
1224 						ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[i] = sharedWeapon->Ammo;
1225 					}
1226 				}
1227 				ChangeGameConfig().Profile[CurrentProfile].Money -= ReloadCost;
1228 			}
1229 		}
1230 	}
1231 
1232 	vw_SetFontSize(24);
1233 	vw_DrawTextUTF32(GameConfig().InternalWidth/2+445-vw_TextWidthUTF32(vw_GetTextUTF32("Installed Weapons")), 600, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, MenuContentTransp, vw_GetTextUTF32("Installed Weapons"));
1234 	ResetFontSize();
1235 
1236 	// вывод информации
1237 	vw_SetFontSize(20);
1238 
1239 	tmpTransp = MenuContentTransp;
1240 	tmpColor = sRGBCOLOR{eRGBCOLOR::yellow};
1241 	tmpStream.clear();
1242 	tmpStream.str(std::string{});
1243 	tmpStream << vw_GetText("Money") << ": "
1244 		  << GameConfig().Profile[CurrentProfile].Money;
1245 	int SizeI = (GameConfig().InternalWidth - vw_TextWidth(tmpStream.str())) / 2;
1246 	if (GameConfig().Profile[CurrentProfile].Money < GetWeaponBaseCost(CurrentWorkshopNewWeapon)) {
1247 		tmpTransp = MenuContentTransp * CurrentAlert3;
1248 		tmpColor = sRGBCOLOR{eRGBCOLOR::red};
1249 	}
1250 	vw_DrawText(SizeI, 630, 0, 0, 1.0f, tmpColor, tmpTransp, tmpStream.str());
1251 
1252 	ResetFontSize();
1253 
1254 
1255 	// проверяем состояние, если тянули и отжали, и сюда пришли - значит никто не перехватил, нужно сделать сброс
1256 	if (!vw_GetMouseLeftClick(false) && DragWeapon) {
1257 		// звук пропадающего оружия
1258 		PlayMenuSFX(eMenuSFX::DragRelease, 1.0f);
1259 
1260 		// сброс
1261 		DragWeapon = false;
1262 		DragWeaponNum = 0;
1263 		DragWeaponLevel = 0;
1264 		DragWeaponAmmo = 0;
1265 		DragWeaponAmmoStart = 0;
1266 	}
1267 
1268 	if (DragWeapon) {
1269 		SetCursorDraggingItemIcon(GetPreloadedTextureAsset(GetWeaponIconName(DragWeaponNum)));
1270 		SetCursorStatus(eCursorStatus::DraggingItem);
1271 	}
1272 }
1273 
1274 } // astromenace namespace
1275 } // viewizard namespace
1276