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