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 // TODO translate comments
29 
30 // NOTE in future, use make_unique() to make unique_ptr-s (since C++14)
31 
32 #include "../core/core.h"
33 #include "../config/config.h"
34 #include "../ui/cursor.h"
35 #include "../ui/game_speed.h"
36 #include "../ui/game/text.h"
37 #include "../ui/game/stopwatch.h"
38 #include "../assets/audio.h"
39 #include "../assets/texture.h"
40 #include "../gfx/star_system.h"
41 #include "../gfx/shadow_map.h"
42 #include "../script/script.h"
43 #include "../object3d/space_ship/space_ship.h"
44 #include "../command.h"
45 #include "../game/camera.h"
46 #include "../game/weapon_panel.h"
47 #include "../game/hud.h"
48 #include "../game.h" // FIXME "game.h" should be replaced by individual headers
49 #include <sstream>
50 #include <iomanip>
51 
52 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
53 namespace viewizard {
54 namespace astromenace {
55 
56 namespace {
57 
58 std::unique_ptr<cMissionScript> MissionScript{};
59 
60 eCommand GameExitCommand{eCommand::DO_NOTHING};
61 
62 } // unnamed namespace
63 
64 
65 
66 
67 
68 
69 
70 // замедление снарядов NPC ... 1-3...
71 int	GameEnemyWeaponPenalty = 1;
72 // ум. защиты NPC объектов
73 int	GameEnemyArmorPenalty = 1;
74 // "замедление" наведения NPC ... 1-4
75 int	GameEnemyTargetingSpeedPenalty = 1;
76 // 0-ограничено, 1-нет
77 int	GameUnlimitedAmmo = 0;
78 // 0-может быть уничтожено, 1-нет
79 int	GameUndestroyableWeapon = 0;
80 // 1-аркада, 0-симулятор
81 int	GameWeaponTargetingMode = 0;
82 // 1-аркада, 0-симулятор
83 int	GameSpaceShipControlMode = 0;
84 
85 int	GameEngineSystem = 1;
86 int	GameTargetingSystem = 1;
87 int	GameAdvancedProtectionSystem = 0;
88 int	GamePowerSystem = 1;
89 int	GameTargetingMechanicSystem = 1;
90 
91 // присваиваем в профайл только по завершению уровня!
92 float	GameMoney = 0;
93 float	GameExperience = 0;
94 
95 int	AlienShipsKillQuant;
96 float	AlienShipsKillBonus;
97 int	AlienMotherShipsKillQuant;
98 float	AlienMotherShipsKillBonus;
99 int	PirateShipsKillQuant;
100 float	PirateShipsKillBonus;
101 int	PirateVehiclesKillQuant;
102 float	PirateVehiclesKillBonus;
103 int	PirateBuildingsKillQuant;
104 float	PirateBuildingsKillBonus;
105 int	AsteroidsKillQuant;
106 float	AsteroidsKillBonus;
107 
108 
109 // статус завершена игра или нет
110 bool GameMissionCompleteStatus = false;
111 bool GameMissionCompleteStatusShowDialog = false;
112 
113 // собственно сам файтер
114 std::weak_ptr<cSpaceShip> PlayerFighter{};
115 
116 
117 
118 // флаг отображения меню
119 bool GameMenu = false;
120 float GameContentTransp = 0.0f;
121 float LastGameUpdateTime = 0.0f;
122 eGameMenuStatus GameMenuStatus = eGameMenuStatus::GAME_MENU;
123 
124 float GameButton1Transp = 1.0f;
125 float LastGameButton1UpdateTime = 0.0f;
126 float GameButton2Transp = 1.0f;
127 float LastGameButton2UpdateTime = 0.0f;
128 float GameButton3Transp = 1.0f;
129 float LastGameButton3UpdateTime = 0.0f;
130 float GameButton4Transp = 1.0f;
131 float LastGameButton4UpdateTime = 0.0f;
132 bool NeedShowGameMenu = false;
133 bool NeedHideGameMenu = false;
134 
135 
136 // работа с морганием вывода
137 extern float CurrentAlert2;
138 extern float CurrentAlert3;
139 extern float CurrentTime;
140 
141 // работа с кораблем игрока
142 void InitGamePlayerShip();
143 void GamePlayerShip();
144 float GetShipMaxEnergy(int Num);
145 extern float CurrentPlayerShipEnergy;
146 extern int LastMouseX;
147 extern int LastMouseY;
148 extern int LastMouseXR;
149 extern int LastMouseYR;
150 
151 // щит или дефлектор
152 extern std::weak_ptr<cParticleSystem> Shild1;
153 extern std::weak_ptr<cParticleSystem> Shild2;
154 
155 // для звука открытия-закрытия меню в игре
156 unsigned int SoundShowHideMenu{0};
157 
158 
159 float LastGameOnOffUpdateTime = 0.0f;
160 float GameBlackTransp = 0.0f;
161 bool NeedOnGame = false;
162 bool NeedOffGame = false;
163 
164 
165 
166 
167 //------------------------------------------------------------------------------------
168 // Инициализация игровой части
169 //------------------------------------------------------------------------------------
InitGame()170 void InitGame()
171 {
172 	ShadowMap_SizeSetup(eShadowMapSetup::Game);
173 
174 	if ((CurrentProfile < 0) || (CurrentProfile > 4))
175 		CurrentProfile = 0;
176 	if (CurrentMission == -1)
177 		CurrentMission = 0;
178 
179 
180 	GameEnemyWeaponPenalty = GameConfig().Profile[CurrentProfile].EnemyWeaponPenalty;
181 	GameEnemyArmorPenalty = GameConfig().Profile[CurrentProfile].EnemyArmorPenalty;
182 	GameEnemyTargetingSpeedPenalty = GameConfig().Profile[CurrentProfile].EnemyTargetingSpeedPenalty;
183 	GameUnlimitedAmmo = GameConfig().Profile[CurrentProfile].UnlimitedAmmo;
184 	GameUndestroyableWeapon = GameConfig().Profile[CurrentProfile].UndestroyableWeapon;
185 	GameWeaponTargetingMode = GameConfig().Profile[CurrentProfile].WeaponTargetingMode;
186 	GameSpaceShipControlMode = GameConfig().Profile[CurrentProfile].SpaceShipControlMode;
187 
188 	GameEngineSystem = GameConfig().Profile[CurrentProfile].EngineSystem;
189 	// если симулятивный режим, ставим 1...
190 	if (GameSpaceShipControlMode == 1)
191 		if (GameEngineSystem == 0) GameEngineSystem = 1;
192 
193 	GameTargetingSystem = GameConfig().Profile[CurrentProfile].TargetingSystem;
194 	GameAdvancedProtectionSystem = GameConfig().Profile[CurrentProfile].AdvancedProtectionSystem;
195 	GamePowerSystem = GameConfig().Profile[CurrentProfile].PowerSystem;
196 	GameTargetingMechanicSystem = GameConfig().Profile[CurrentProfile].TargetingMechanicSystem;
197 	GameMoney = GameConfig().Profile[CurrentProfile].Money * 1.0f;
198 
199 	// убираем данные этого уровня
200 	GameExperience = (GameConfig().Profile[CurrentProfile].Experience - GameConfig().Profile[CurrentProfile].ByMissionExperience[CurrentMission]) * 1.0f;
201 
202 	// grab mouse control for both - windows and fullscren mode (need this for multi-monitor systems)
203 	SDL_SetWindowGrab(vw_GetSDLWindow(), SDL_TRUE);
204 
205 
206 	// сбрасываем все кнопки мыши
207 	vw_ResetMouseButtons();
208 	// установка мышки на центр
209 	float tmpViewportWidth, tmpViewportHeight;
210 	vw_GetViewport(nullptr, nullptr, &tmpViewportWidth, &tmpViewportHeight);
211 	SDL_WarpMouseInWindow(vw_GetSDLWindow(), tmpViewportWidth / 2, tmpViewportHeight / 2);
212 	SetShowGameCursor(false);
213 
214 	LastMouseXR = 0;
215 	LastMouseYR = 0;
216 	SDL_GetMouseState(&LastMouseXR, &LastMouseYR);
217 
218 
219 
220 	cGameSpeed::GetInstance().InitGameSpeed();
221 	cStopwatch::GetInstance().Reset(false); // should be after cGameSpeed init
222 
223 
224 
225 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
226 	// иним камеру, всегда до работы со скриптом (!!!)
227 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
228 	InitCamera();
229 
230 
231 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
232 	// иним корабль
233 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
234 	InitGamePlayerShip();
235 
236 
237 
238 	// !!! пока загрузка идет полная на уровень, и наверно так и нужно оставить
239 	// иначе нужно выносить перечень загружаемого в скрипт (менять не смогут уровни)
240 
241 	StarSystemInitByType(eDrawType::GAME); // should be before RunScript()
242 
243 	MissionScript.reset(new cMissionScript);
244 	if (!MissionScript->RunScript(GetCurrentMissionFileName(), vw_GetTimeThread(1)))
245 		MissionScript.reset();
246 
247 
248 	SetupMissionNumberText(3.0f, CurrentMission + 1);
249 	SetupMissionFailedText(0.0f); // reset previous status on game restart after fail
250 
251 
252 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
253 	// немного "прокручиваем", чтобы сразу по появлению было заполнено
254 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
255 	vw_ResizeScene(45.0f, GameConfig().InternalWidth / GameConfig().InternalHeight, 1.0f, 2000.0f);
256 	vw_SetCameraLocation(sVECTOR3D{0.0f, 65.0f, -100.0f + 10.0f});
257 	vw_SetCameraMoveAroundPoint(sVECTOR3D{0.0f, 0.0f, 10.0f}, 0.0f, sVECTOR3D{0.0f, 0.0f, 0.0f});
258 
259 
260 	InitHUD(PlayerFighter,
261 		GamePowerSystem ? (CurrentPlayerShipEnergy / GetShipMaxEnergy(GamePowerSystem)) : 0.0f,
262 		GameConfig().Profile[CurrentProfile].Experience -
263 		GameConfig().Profile[CurrentProfile].ByMissionExperience[CurrentMission],
264 		GameConfig().Profile[CurrentProfile].Money);
265 
266 
267 
268 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
269 	// инициализация игрового меню
270 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
271 	GameContentTransp = 0.0f;
272 	LastGameUpdateTime = vw_GetTimeThread(0);
273 	GameButton1Transp = 1.0f;
274 	LastGameButton1UpdateTime = 0.0f;
275 	GameButton2Transp = 1.0f;
276 	LastGameButton2UpdateTime = 0.0f;
277 	GameButton3Transp = 1.0f;
278 	LastGameButton3UpdateTime = 0.0f;
279 	GameButton4Transp = 1.0f;
280 	LastGameButton4UpdateTime = 0.0f;
281 	GameMenu = false;
282 	NeedShowGameMenu = false;
283 	NeedHideGameMenu = false;
284 
285 	GameMenuStatus = eGameMenuStatus::GAME_MENU;
286 
287 	AlienShipsKillQuant = 0;
288 	AlienShipsKillBonus = 0.0f;
289 	AlienMotherShipsKillQuant = 0;
290 	AlienMotherShipsKillBonus = 0.0f;
291 	PirateShipsKillQuant = 0;
292 	PirateShipsKillBonus = 0.0f;
293 	PirateVehiclesKillQuant = 0;
294 	PirateVehiclesKillBonus = 0.0f;
295 	PirateBuildingsKillQuant = 0;
296 	PirateBuildingsKillBonus = 0.0f;
297 	AsteroidsKillQuant = 0;
298 	AsteroidsKillBonus = 0.0f;
299 
300 	CurrentTime = vw_GetTimeThread(0);
301 	CurrentAlert2 = 1.0f;
302 	CurrentAlert3 = 1.0f;
303 
304 	MenuStatus = eMenuStatus::GAME;
305 
306 	GameMissionCompleteStatus = false;
307 	GameMissionCompleteStatusShowDialog = false;
308 
309 	SoundShowHideMenu = 0;
310 
311 	LastGameOnOffUpdateTime = vw_GetTimeThread(0);
312 	GameBlackTransp = 1.0f;
313 	NeedOnGame = true;
314 }
315 
316 
317 
318 
319 
320 
321 
322 //------------------------------------------------------------------------------------
323 // Завершаем игру
324 //------------------------------------------------------------------------------------
ExitGame(eCommand Command)325 void ExitGame(eCommand Command)
326 {
327 	GameExitCommand = Command;
328 	NeedOffGame = true;
329 	LastGameOnOffUpdateTime = vw_GetTimeThread(0);
330 
331 	// убираем меню
332 	if (GameMenu) {
333 		GameMenu = false;
334 		NeedShowGameMenu = false;
335 		NeedHideGameMenu = true;
336 		SetShowGameCursor(false);
337 		// установка в последюю точку указателя
338 		SDL_WarpMouseInWindow(vw_GetSDLWindow(), LastMouseXR, LastMouseYR);
339 	}
340 }
RealExitGame()341 void RealExitGame()
342 {
343 	// удаляем корабль игрока
344 	ReleaseSpaceShip(PlayerFighter);
345 
346 	vw_ReleaseAllParticleSystems2D();
347 
348 	vw_ReleaseParticleSystem(Shild1);
349 	vw_ReleaseParticleSystem(Shild2);
350 
351 	// release mouse control
352 	SDL_SetWindowGrab(vw_GetSDLWindow(), SDL_FALSE);
353 }
354 
355 //------------------------------------------------------------------------------------
356 // Завершаем игру, нужно сохранить параметры
357 //------------------------------------------------------------------------------------
ExitGameWithSave(eCommand Command)358 void ExitGameWithSave(eCommand Command)
359 {
360 	// данные по деньгам и опыту
361 	ChangeGameConfig().Profile[CurrentProfile].Money = static_cast<int>(GameMoney);
362 
363 	// если получили больше опыта
364 	if (GameConfig().Profile[CurrentProfile].Experience < static_cast<int>(GameExperience)) {
365 		int Incr = static_cast<int>(GameExperience) - GameConfig().Profile[CurrentProfile].Experience;
366 		ChangeGameConfig().Profile[CurrentProfile].ByMissionExperience[CurrentMission] += Incr;
367 		ChangeGameConfig().Profile[CurrentProfile].Experience += Incr;
368 	}
369 	// увеличиваем счетчик пройденной миссии
370 	ChangeGameConfig().Profile[CurrentProfile].MissionReplayCount[CurrentMission]++;
371 
372 	if (auto sharedPlayerFighter = PlayerFighter.lock()) {
373 		// состояние корпуса коробля
374 		ChangeGameConfig().Profile[CurrentProfile].ArmorStatus = sharedPlayerFighter->ArmorCurrentStatus;
375 
376 		// учет состояния оружия
377 		for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) {
378 			if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) {
379 				if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) {
380 					// если оружие было уничтожено во время игры
381 					if (sharedWeapon->ArmorCurrentStatus <= 0.0f) {
382 						ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[i] = 0;
383 						ChangeGameConfig().Profile[CurrentProfile].Weapon[i] = 0;
384 					} else {
385 						// если все ок, нужно запомнить сколько осталось в боекомплекте
386 						ChangeGameConfig().Profile[CurrentProfile].WeaponAmmo[i] = sharedWeapon->Ammo;
387 					}
388 				}
389 			}
390 		}
391 	}
392 
393 	// ставим следующую миссию
394 	CurrentMission++;
395 	// перемещаем ограничитель дальше, если это нужно
396 	if (GameConfig().Profile[CurrentProfile].OpenLevelNum < CurrentMission)
397 		ChangeGameConfig().Profile[CurrentProfile].OpenLevelNum = CurrentMission;
398 
399 	// если дальше уже ничего нет, просто снимаем все... пусть игрок сам выберет
400 	if (CurrentMission > (AllMission - 1)) {
401 		CurrentMission = -1;
402 		// это была последняя миссия, показываем список авторов
403 		Command = eCommand::SWITCH_FROM_GAME_TO_CREDITS;
404 	}
405 
406 	vw_ResetWheelStatus();
407 	// ставим нужный лист миссий
408 	StartMission = 0;
409 	EndMission = 4;
410 	if (CurrentMission != -1)
411 		if (CurrentMission > 2) { // нужно сдвинуть лист, чтобы выбранный элемент был по середине списка
412 			StartMission = CurrentMission-2;
413 			EndMission = CurrentMission+2;
414 
415 			if (CurrentMission >= AllMission-2) {
416 				StartMission = AllMission-5;
417 				EndMission = AllMission-1;
418 			}
419 		}
420 
421 
422 	ChangeGameConfig().Profile[CurrentProfile].LastMission = CurrentMission;
423 
424 	ExitGame(Command);
425 }
426 
427 
428 
429 
430 
431 
432 //------------------------------------------------------------------------------------
433 // Завершение игры, выиграли
434 //------------------------------------------------------------------------------------
SetGameMissionComplete()435 void SetGameMissionComplete()
436 {
437 	// если убили, не устанавливаем!
438 	if (PlayerFighter.expired())
439 		return;
440 	GameMissionCompleteStatus = true;
441 	GameMissionCompleteStatusShowDialog = true;
442 }
443 
444 
445 
446 
447 
448 
449 //------------------------------------------------------------------------------------
450 // прорисовка игровой части
451 //------------------------------------------------------------------------------------
DrawGame()452 void DrawGame()
453 {
454 
455 	float TimeDelta = vw_GetTimeThread(0) - CurrentTime;
456 	CurrentTime = vw_GetTimeThread(0);
457 
458 
459 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
460 	// просчитываем индикацию
461 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
462 	CurrentAlert2 -= TimeDelta;
463 	if (CurrentAlert2 < 0.1f)
464 		CurrentAlert2 = 1.0f;
465 	CurrentAlert3 -= 1.9f * TimeDelta;
466 	if (CurrentAlert3 < 0.1f)
467 		CurrentAlert3 = 1.0f;
468 
469 
470 
471 
472 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
473 	// Работа с 3д частью... прорисовка, просчет
474 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
475 	CameraUpdate(vw_GetTimeThread(1));
476 	vw_CameraLookAt();
477 
478 
479 	// всегда первым рисуем скайбокс и "далекое" окружение
480 	StarSystemDraw(eDrawType::GAME);
481 
482 
483 	// рисуем все 3д объекты
484 	DrawAllObject3D(eDrawType::GAME);
485 
486 
487 	// после полной прорисовки делаем обновление данных
488 	UpdateAllObject3D(vw_GetTimeThread(1));
489 	vw_UpdateAllParticleSystems(vw_GetTimeThread(1));
490 
491 	// проверяем на столкновения
492 	if (GameContentTransp < 0.99f) // не нужно проверять коллизии, включено меню
493 		DetectCollisionAllObject3D();
494 
495 
496 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
497 	// работаем со скриптом, пока он есть
498 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
499 	if (MissionScript &&
500 	    (!MissionScript->Update(vw_GetTimeThread(1))))
501 		MissionScript.reset();
502 
503 
504 
505 
506 
507 
508 
509 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
510 	// 2д часть
511 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
512 	vw_Start2DMode(-1,1);
513 
514 
515 
516 
517 
518 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
519 	// Обработка состояния корабля игрока
520 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
521 	GamePlayerShip();
522 
523 
524 	UpdateHUD(PlayerFighter,
525 		  GamePowerSystem ? (CurrentPlayerShipEnergy / GetShipMaxEnergy(GamePowerSystem)) : 0.0f);
526 	DrawHUD();
527 	DrawWeaponPanels(PlayerFighter); // (?) part of HUD
528 
529 	cGameSpeed::GetInstance().Draw();
530 
531 
532 	DrawMissionNumberText();
533 	DrawMissionFailedText();
534 
535 
536 
537 
538 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
539 	// Рисуем меню, всегда самое последнее в игровой 2д части
540 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
541 	// делаем плавное появление меню
542 	if (NeedShowGameMenu) {
543 		GameContentTransp += 2.0f*(vw_GetTimeThread(0)-LastGameUpdateTime);
544 		if (GameContentTransp >= 1.0f) {
545 			GameContentTransp = 1.0f;
546 			NeedShowGameMenu = false;
547 			SetShowGameCursor(true);
548 
549 			// release mouse control
550 			SDL_SetWindowGrab(vw_GetSDLWindow(), SDL_FALSE);
551 			SDL_WarpMouseInWindow(vw_GetSDLWindow(), LastMouseXR, LastMouseYR);
552 		}
553 		// плавно возвращаем игре сокрость
554 		if (GameContentTransp != 0.0f)
555 			cGameSpeed::GetInstance().SetThreadSpeed((1.0f - GameContentTransp) * GameConfig().GameSpeed);
556 	}
557 	// делаем полавное угасание меню
558 	if (NeedHideGameMenu) {
559 		GameContentTransp -= vw_GetTimeThread(0) - LastGameUpdateTime;
560 		if (GameContentTransp <= 0.0f) {
561 			GameContentTransp = 0.0f;
562 			NeedHideGameMenu = false;
563 			GameMenuStatus = eGameMenuStatus::GAME_MENU;
564 
565 			// grab mouse control for both - windows and fullscren mode (need this for multi-monitor systems)
566 			SDL_SetWindowGrab(vw_GetSDLWindow(), SDL_TRUE);
567 			SDL_WarpMouseInWindow(vw_GetSDLWindow(), LastMouseXR, LastMouseYR);
568 		}
569 		// останавливаем игру
570 		cGameSpeed::GetInstance().SetThreadSpeed((1.0f - GameContentTransp) * GameConfig().GameSpeed);
571 	}
572 	LastGameUpdateTime = vw_GetTimeThread(0);
573 
574 	// если можем - рисуем игровое меню
575 	if (GameContentTransp > 0.0f) {
576 		if (GameMissionCompleteStatus) {
577 			// выводим подложку меню
578 			sRECT SrcRect(2, 2, 564-2, 564-2);
579 			sRECT DstRect(GameConfig().InternalWidth / 2 - 256 - 26, 128 - 28, GameConfig().InternalWidth / 2 - 256 + 534, 128 + 532);
580 			vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/dialog512_512.tga"),
581 					   true, GameContentTransp);
582 			// название меню
583 			int Size = vw_TextWidthUTF32(vw_GetTextUTF32("Mission Complete"));
584 			float WScale = 0;
585 			if (Size > 190) {
586 				Size = 190;
587 				WScale = -190;
588 			}
589 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 123 - Size / 2, 128+21, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, 0.7f * GameContentTransp, vw_GetTextUTF32("Mission Complete"));
590 
591 
592 			int Y = 128+90;
593 			int Prir = 36;
594 
595 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, 0.5f * GameContentTransp, vw_GetTextUTF32("Type"));
596 			Size = vw_TextWidthUTF32(vw_GetTextUTF32("Killed"));
597 			WScale = 0;
598 			if (Size > 70) {
599 				Size = 70;
600 				WScale = -70;
601 			}
602 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 31 + Size / 2, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, 0.5f * GameContentTransp, vw_GetTextUTF32("Killed"));
603 			Size = vw_TextWidthUTF32(vw_GetTextUTF32("Bonus"));
604 			WScale = 0;
605 			if (Size > 70) {
606 				Size = 70;
607 				WScale = -70;
608 			}
609 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 + 97 + Size / 2, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, 0.5f * GameContentTransp, vw_GetTextUTF32("Bonus"));
610 			Y += Prir;
611 
612 			WScale = -210;
613 
614 			// FIXME ostringstream is not so fast, move all string initialization into setup,
615 			//       all ostringstream-related code should be called only one time in dialog init
616 			std::ostringstream tmpStream;
617 
618 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << AlienShipsKillQuant;
619 			std::string AlienShipsKillQuantString{tmpStream.str()};
620 			tmpStream.clear();
621 			tmpStream.str(std::string{});
622 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << AlienShipsKillBonus;
623 			std::string AlienShipsKillBonusString{tmpStream.str()};
624 			tmpStream.clear();
625 			tmpStream.str(std::string{});
626 
627 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << AlienMotherShipsKillQuant;
628 			std::string AlienMotherShipsKillQuantString{tmpStream.str()};
629 			tmpStream.clear();
630 			tmpStream.str(std::string{});
631 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << AlienMotherShipsKillBonus;
632 			std::string AlienMotherShipsKillBonusString{tmpStream.str()};
633 			tmpStream.clear();
634 			tmpStream.str(std::string{});
635 
636 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << PirateShipsKillQuant;
637 			std::string PirateShipsKillQuantString{tmpStream.str()};
638 			tmpStream.clear();
639 			tmpStream.str(std::string{});
640 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << PirateShipsKillBonus;
641 			std::string PirateShipsKillBonusString{tmpStream.str()};
642 			tmpStream.clear();
643 			tmpStream.str(std::string{});
644 
645 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << PirateVehiclesKillQuant;
646 			std::string PirateVehiclesKillQuantString{tmpStream.str()};
647 			tmpStream.clear();
648 			tmpStream.str(std::string{});
649 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << PirateVehiclesKillBonus;
650 			std::string PirateVehiclesKillBonusString{tmpStream.str()};
651 			tmpStream.clear();
652 			tmpStream.str(std::string{});
653 
654 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << PirateBuildingsKillQuant;
655 			std::string PirateBuildingsKillQuantString{tmpStream.str()};
656 			tmpStream.clear();
657 			tmpStream.str(std::string{});
658 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << PirateBuildingsKillBonus;
659 			std::string PirateBuildingsKillBonusString{tmpStream.str()};
660 			tmpStream.clear();
661 			tmpStream.str(std::string{});
662 
663 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << AsteroidsKillQuant;
664 			std::string AsteroidsKillQuantString{tmpStream.str()};
665 			tmpStream.clear();
666 			tmpStream.str(std::string{});
667 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << AsteroidsKillBonus;
668 			std::string AsteroidsKillBonusString{tmpStream.str()};
669 			tmpStream.clear();
670 			tmpStream.str(std::string{});
671 
672 			tmpStream << std::fixed << std::setw(4) << std::setfill('0') << AlienShipsKillQuant +
673 				     AlienMotherShipsKillQuant + PirateShipsKillQuant + PirateVehiclesKillQuant +
674 				     PirateBuildingsKillQuant + AsteroidsKillQuant;
675 			std::string SummaryQuantString{tmpStream.str()};
676 			tmpStream.clear();
677 			tmpStream.str(std::string{});
678 			tmpStream << std::fixed << std::setw(6) << std::setprecision(0) << std::setfill('0') << GameMoney - GameConfig().Profile[CurrentProfile].Money;
679 			std::string SummaryBonusString{tmpStream.str()};
680 			tmpStream.clear();
681 			tmpStream.str(std::string{});
682 
683 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp,  vw_GetTextUTF32("Alien Spaceships"));
684 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AlienShipsKillQuantString);
685 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AlienShipsKillBonusString);
686 			Y += Prir;
687 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, vw_GetTextUTF32("Alien Motherships"));
688 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AlienMotherShipsKillQuantString);
689 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AlienMotherShipsKillBonusString);
690 			Y += Prir;
691 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, vw_GetTextUTF32("Pirate Spaceships"));
692 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateShipsKillQuantString);
693 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateShipsKillBonusString);
694 			Y += Prir;
695 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, vw_GetTextUTF32("Pirate Vehicles"));
696 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateVehiclesKillQuantString);
697 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateVehiclesKillBonusString);
698 			Y += Prir;
699 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, vw_GetTextUTF32("Pirate Buildings"));
700 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateBuildingsKillQuantString);
701 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, PirateBuildingsKillBonusString);
702 			Y += Prir;
703 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, vw_GetTextUTF32("Asteroids"));
704 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AsteroidsKillQuantString);
705 			vw_DrawText(GameConfig().InternalWidth / 2 + 126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, AsteroidsKillBonusString);
706 
707 			Y += (int)(Prir*1.5);
708 			vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256+38, Y, WScale, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, GameContentTransp, vw_GetTextUTF32("Total"));
709 			vw_DrawText(GameConfig().InternalWidth / 2 + 10, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, SummaryQuantString);
710 			vw_DrawText(GameConfig().InternalWidth / 2+126, Y, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::white}, GameContentTransp, SummaryBonusString);
711 
712 			// выводим кнопки меню
713 			int X = GameConfig().InternalWidth / 2 - 192;
714 			Y = 545;
715 			// продолжение игры
716 			if (DrawButton384(X,Y, vw_GetTextUTF32("NEXT"), GameContentTransp, GameButton4Transp, LastGameButton4UpdateTime))
717 				ExitGameWithSave(eCommand::SWITCH_FROM_GAME_TO_MISSION_MENU);
718 		} else {
719 			switch (GameMenuStatus) {
720 			// основное меню игры
721 			case eGameMenuStatus::GAME_MENU: {
722 				// выводим подложку меню
723 				sRECT SrcRect(2, 2, 564-2, 564-2);
724 				sRECT DstRect(GameConfig().InternalWidth / 2 - 256+4-30, 128+2-30, GameConfig().InternalWidth / 2 - 256+564-30, 128+564-2-30);
725 				vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/dialog512_512.tga"),
726 					  true, GameContentTransp);
727 				// название меню
728 				int SizeI = 17 + (234-vw_TextWidthUTF32(vw_GetTextUTF32("GAME MENU")))/2;
729 				vw_DrawTextUTF32(GameConfig().InternalWidth / 2 - 256 + SizeI, 128+22, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::yellow}, 0.7f * GameContentTransp, vw_GetTextUTF32("GAME MENU"));
730 
731 				// выводим кнопки меню
732 
733 
734 				int X = GameConfig().InternalWidth / 2 - 192;
735 				int Y = 225;
736 				int Prir = 100;
737 
738 				// продолжаем игру
739 				if (DrawButton384(X,Y, vw_GetTextUTF32("RESUME"), GameContentTransp, GameButton1Transp, LastGameButton1UpdateTime)) {
740 					GameMenu = false;
741 					NeedShowGameMenu = false;
742 					NeedHideGameMenu = true;
743 					SetShowGameCursor(false);
744 					// установка в последюю точку указателя
745 					SDL_WarpMouseInWindow(vw_GetSDLWindow(), LastMouseXR, LastMouseYR);
746 
747 					if (vw_IsSoundAvailable(SoundShowHideMenu))
748 						vw_StopSound(SoundShowHideMenu, 150);
749 					SoundShowHideMenu = PlayMenuSFX(eMenuSFX::MissionHideMenu, 1.0f);
750 				}
751 
752 				// выход в настройки
753 				Y = Y+Prir;
754 				if (DrawButton384(X,Y, vw_GetTextUTF32("OPTIONS"), GameContentTransp, GameButton2Transp, LastGameButton2UpdateTime)) {
755 					SetOptionsMenu(eMenuStatus::OPTIONS);
756 					GameMenuStatus = eGameMenuStatus::OPTIONS;
757 				}
758 
759 				// прерываем игру
760 				Y = Y+Prir;
761 				if (DrawButton384(X,Y, vw_GetTextUTF32("RESTART"), GameContentTransp, GameButton3Transp, LastGameButton3UpdateTime)) {
762 					// если убили, выводить диалог не нужно
763 					if (PlayerFighter.expired())
764 						ExitGame(eCommand::SWITCH_FROM_MENU_TO_GAME);
765 					else
766 						SetCurrentDialogBox(eDialogBox::RestartLevelNoSave);
767 				}
768 
769 				// выход из игры
770 				Y = Y+Prir;
771 				if (DrawButton384(X,Y, vw_GetTextUTF32("QUIT"), GameContentTransp, GameButton4Transp, LastGameButton4UpdateTime)) {
772 					// если убили, выводить диалог не нужно
773 					if (PlayerFighter.expired())
774 						ExitGame(eCommand::SWITCH_FROM_GAME_TO_MAIN_MENU);
775 					else
776 						SetCurrentDialogBox(eDialogBox::QuiToMenuNoSave);
777 				}
778 
779 				break;
780 			}
781 
782 			// основное меню настроек
783 			case eGameMenuStatus::OPTIONS:
784 				OptionsMenu(GameContentTransp, GameButton1Transp, LastGameButton1UpdateTime, GameButton2Transp, LastGameButton2UpdateTime);
785 				break;
786 			// меню продвинутых настроек
787 			case eGameMenuStatus::OPTIONS_ADVANCED:
788 				OptionsAdvMenu(GameContentTransp, GameButton1Transp, LastGameButton1UpdateTime, GameButton2Transp, LastGameButton2UpdateTime);
789 				break;
790 			// меню настройки интерфейса
791 			case eGameMenuStatus::INTERFACE:
792 				InterfaceMenu(GameContentTransp, GameButton1Transp, LastGameButton1UpdateTime);
793 				break;
794 			// меню настройки управления
795 			case eGameMenuStatus::CONFCONTROL:
796 				ConfControlMenu(GameContentTransp, GameButton1Transp, LastGameButton1UpdateTime);
797 				break;
798 
799 
800 			}
801 
802 
803 			// вывод надписи пауза
804 			sRECT SrcRect(0, 0, 256, 64);
805 			sRECT DstRect(GameConfig().InternalWidth - 256 + 60, 768 - 54, GameConfig().InternalWidth + 60, 768 + 10);
806 			if (GameContentTransp == 1.0f)
807 				vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset(vw_GetText("lang/en/game/pause.tga")), true, CurrentAlert2*GameContentTransp);
808 			else
809 				vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset(vw_GetText("lang/en/game/pause.tga")), true, GameContentTransp);
810 
811 		}
812 
813 	}
814 
815 
816 
817 
818 
819 
820 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
821 	// открываем меню
822 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
823 	// если в игре - меню, если в меню - выход
824 	if (!isDialogBoxDrawing()) {
825 		if (!PlayerFighter.expired()) { // если не убили
826 			if (vw_GetKeyStatus(SDLK_ESCAPE) || GameMissionCompleteStatusShowDialog) {
827 				bool NeedPlaySfx = true;
828 				if (GameMissionCompleteStatusShowDialog) {
829 					// если уже было открыто меню и появляется меню окончания миссии, не проигрываем sfx
830 					if (GameMenu)
831 						NeedPlaySfx = false;
832 					else
833 						GameMenu = true;
834 				} else
835 					GameMenu = !GameMenu;
836 
837 				if (GameMenu && (!GameMissionCompleteStatus || GameMissionCompleteStatusShowDialog)) { // открытываем меню с результатом миссии и больше не даем ничего открывать, после завершения миссии
838 					NeedShowGameMenu = true;
839 					NeedHideGameMenu = false;
840 					if (NeedPlaySfx && vw_IsSoundAvailable(SoundShowHideMenu))
841 						vw_StopSound(SoundShowHideMenu, 150);
842 					if (NeedPlaySfx)
843 						SoundShowHideMenu = PlayMenuSFX(eMenuSFX::MissionShowMenu, 1.0f);
844 					// сброс кнопки мышки, чтобы случайно не нажали
845 					vw_GetMouseLeftClick(true);
846 				} else if (!GameMenu && !GameMissionCompleteStatus) { // открыто меню с выводом результата миссии, нельзя давать его закрывать
847 					NeedShowGameMenu = false;
848 					NeedHideGameMenu = true;
849 					// установка в последюю точку указателя
850 					SDL_WarpMouseInWindow(vw_GetSDLWindow(), LastMouseXR, LastMouseYR);
851 
852 					if (NeedPlaySfx && vw_IsSoundAvailable(SoundShowHideMenu))
853 						vw_StopSound(SoundShowHideMenu, 150);
854 					if (NeedPlaySfx)
855 						SoundShowHideMenu = PlayMenuSFX(eMenuSFX::MissionHideMenu, 1.0f);
856 					SetShowGameCursor(false);
857 				}
858 
859 				if (GameMissionCompleteStatus && !GameMissionCompleteStatusShowDialog) // в процессе вывода результатов разрешаем только выход в основное меню (отображение диалога)
860 					SetCurrentDialogBox(eDialogBox::QuiToMenuNoSave);
861 				GameMissionCompleteStatusShowDialog = false;
862 				vw_SetKeyStatus(SDLK_ESCAPE, false);
863 			}
864 		}
865 	}
866 
867 
868 
869 
870 	// черное затемнение, если нужно
871 	if (NeedOnGame) {
872 		GameBlackTransp = 1.0f - 2.4f*(vw_GetTimeThread(0) - LastGameOnOffUpdateTime);
873 		if (GameBlackTransp <= 0.0f) {
874 			GameBlackTransp = 0.0f;
875 			NeedOnGame = false;
876 		}
877 
878 		sRECT SrcRect(0, 0, 2, 2);
879 		sRECT DstRect(0, 0, GameConfig().InternalWidth, 768);
880 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/blackpoint.tga"), true, GameBlackTransp);
881 	}
882 
883 	// черное затемнение, если нужно
884 	if (NeedOffGame) {
885 		GameBlackTransp = 2.4f * (vw_GetTimeThread(0) - LastGameOnOffUpdateTime);
886 		if (GameBlackTransp >= 1.0f) {
887 			GameBlackTransp = 1.0f;
888 			NeedOffGame = false;
889 
890 			// выходим из игры
891 			RealExitGame();
892 			cCommand::GetInstance().Set(GameExitCommand);
893 		}
894 
895 		sRECT SrcRect(0, 0, 2, 2);
896 		sRECT DstRect(0, 0, GameConfig().InternalWidth, 768);
897 		vw_Draw2D(DstRect, SrcRect, GetPreloadedTextureAsset("menu/blackpoint.tga"), true, GameBlackTransp);
898 	}
899 
900 
901 
902 	cStopwatch::GetInstance().Update();
903 	cStopwatch::GetInstance().Draw();
904 
905 
906 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
907 	// завершение 2д части
908 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
909 	vw_End2DMode();
910 }
911 
912 } // astromenace namespace
913 } // viewizard namespace
914