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 #include "../core/core.h"
31 #include "../config/config.h"
32 #include "../platform/platform.h"
33 #include "../ui/font.h"
34 #include "../ui/game/text.h"
35 #include "../assets/audio.h"
36 #include "../assets/texture.h"
37 #include "../game/camera.h"
38 #include "../object3d/explosion/explosion.h"
39 #include "../object3d/space_object/space_object.h"
40 #include "../object3d/ground_object/ground_object.h"
41 #include "../object3d/space_ship/space_ship.h"
42 #include "../object3d/projectile/projectile.h"
43 #include "../game.h" // FIXME "game.h" should be replaced by individual headers
44 
45 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
46 namespace viewizard {
47 namespace astromenace {
48 
49 // режим неубиваемости... отладка
50 bool UndeadDebugMode = false;
51 
52 // состояние управления, нужно, чтобы определять что менялось
53 int LastMouseX = -1;
54 int LastMouseY = -1;
55 // для восстановления положения курсора в меню, точнее при выходе из него
56 int LastMouseXR = 0;
57 int LastMouseYR = 0;
58 
59 // в показателях от -1.0f до 1.0f
60 // назад-вперед
61 float MoveFB = 0.0f;
62 // лево-право
63 float MoveLR = 0.0f;
64 
65 // текущая энергия корабля
66 float CurrentPlayerShipEnergy;
67 
68 // для управления в аркадном режиме маневровыми двигателями
69 bool PlayerFighterLeftEng = false;
70 bool PlayerFighterRightEng = false;
71 
72 std::weak_ptr<cParticleSystem> Shild1{};
73 std::weak_ptr<cParticleSystem> Shild2{};
74 float ShildRadius;
75 float ShildEnergyStatus;
76 float ShildStartHitStatus;
77 
78 // голос с ворнингом, если столкнулись с несбиваемой частью
79 unsigned int VoiceWarningCollisionDetected{0};
80 
81 // Номер, для проигрывания голосового сообщения об обнаружении ракеты
82 unsigned int VoiceMissileDetected{0};
83 bool VoiceMissileDetectedStatus{false};
84 // номер, для проигрывания голосового сообщения о проблемах в орудии
85 // Номер, для проигрывания голосового сообщения об отсутствии снарядов в боекомплекте
86 unsigned int VoiceWeaponMalfunction{0};
87 // для звука - мало жизни
88 unsigned int SoundLowLife{0};
89 
90 
91 // тут храним какая часть взорвалась на корабле игрока
92 int PlayerDeadObjectPieceNum;
93 
94 
95 // симулятивный режим
96 sVECTOR3D CurrentMovementVel(0.0f, 0.0f, 0.0f);
97 
98 // работа с морганием вывода
99 extern float CurrentAlert2;
100 extern float CurrentAlert3;
101 
102 // для переменного типа стрельбы
103 int PrimaryGroupCurrentFireWeaponNum = 1;
104 float PrimaryGroupCurrentFireWeaponDelay = 0.0f;
105 int SecondaryGroupCurrentFireWeaponNum = 1;
106 float SecondaryGroupCurrentFireWeaponDelay = 0.0f;
107 
108 
109 
110 
111 //------------------------------------------------------------------------------------
112 // Получаем максимально возможное значение энергии для устройства реактора-батареи
113 //------------------------------------------------------------------------------------
GetShipMaxEnergy(int Num)114 float GetShipMaxEnergy(int Num)
115 {
116 	switch (Num) {
117 	case 0:
118 		return 0.0f;
119 	// аккамулятор
120 	case 1:
121 		return 100.0f;
122 	// ядерный
123 	case 2:
124 		return 200.0f;
125 	// плазменный
126 	case 3:
127 		return 400.0f;
128 	// антиматерия
129 	case 4:
130 		return 800.0f;
131 
132 	default:
133 		std::cerr << __func__ << "(): " << "wrong Num.\n";
134 		break;
135 	}
136 
137 	return -1.0f;
138 }
139 //------------------------------------------------------------------------------------
140 // Получаем перезарядку энергии, в секунду
141 //------------------------------------------------------------------------------------
GetShipRechargeEnergy(int Num)142 float GetShipRechargeEnergy(int Num)
143 {
144 	switch (Num) {
145 	case 0:
146 		return 0.0f;
147 	// аккамулятор
148 	case 1:
149 		return 20.0f;
150 	// ядерный
151 	case 2:
152 		return 50.0f;
153 	// плазменный
154 	case 3:
155 		return 130.0f;
156 	// антиматерия
157 	case 4:
158 		return 250.0f;
159 
160 	default:
161 		std::cerr << __func__ << "(): " << "wrong Num.\n";
162 		break;
163 	}
164 
165 	return -1.0f;
166 }
167 //------------------------------------------------------------------------------------
168 // Получаем расход энергии в секунду для доп систем (GameAdvancedProtectionSystem)
169 //------------------------------------------------------------------------------------
GetShipProtectionSystemEnergyUse(int Num)170 float GetShipProtectionSystemEnergyUse(int Num)
171 {
172 	switch (Num) {
173 	// нано роботы
174 	case 1:
175 		return 10.0f;
176 	// спец защитный слой
177 	case 2:
178 		return 0.0f;
179 	// щит
180 	case 3:
181 		return 50.0f;
182 	// отражатель
183 	case 4:
184 		return 100.0f;
185 
186 	default:
187 		std::cerr << __func__ << "(): " << "wrong Num.\n";
188 		break;
189 	}
190 
191 	return 0.0f;
192 }
193 //------------------------------------------------------------------------------------
194 // Получаем расход энергии в секунду для двигателей GameEngineSystem
195 //------------------------------------------------------------------------------------
GetShipEngineSystemEnergyUse(int Num)196 float GetShipEngineSystemEnergyUse(int Num)
197 {
198 	switch (Num) {
199 	// обычные двигатели
200 	case 1:
201 		return 5.0f;
202 	// фатоновые
203 	case 2:
204 		return 10.0f;
205 	// плазменные
206 	case 3:
207 		return 30.0f;
208 	// на антиматерии
209 	case 4:
210 		return 60.0f;
211 
212 	default:
213 		std::cerr << __func__ << "(): " << "wrong Num.\n";
214 		break;
215 	}
216 
217 	return 0.0f;
218 }
219 
220 
221 
222 
223 
224 
225 //------------------------------------------------------------------------------------
226 // Инициализация корабля игрока
227 //------------------------------------------------------------------------------------
InitGamePlayerShip()228 void InitGamePlayerShip()
229 {
230 	// создаем корабль игрока по настройкам в профайле
231 	VoiceMissileDetected = 0;
232 	VoiceMissileDetectedStatus = false;
233 	VoiceWeaponMalfunction = 0;
234 	SoundLowLife = 0;
235 
236 	int TMPGameEnemyArmorPenalty = GameEnemyArmorPenalty;
237 	GameEnemyArmorPenalty = 1;
238 
239 	// если не создано, здесь будет ноль скорее всего
240 	if (GameConfig().Profile[CurrentProfile].ShipHull == 0)
241 		std::cerr << __func__ << "(): " << "Error, Pilot Profile not created.\n";
242 
243 	PlayerFighter = CreateEarthSpaceFighter(GameConfig().Profile[CurrentProfile].ShipHull);
244 	auto sharedPlayerFighter = PlayerFighter.lock();
245 	if (!sharedPlayerFighter)
246 		return;
247 
248 	sharedPlayerFighter->ShipShake.emplace_back(sVECTOR3D{0.0f, 0.0f, 1.0f},
249 						    0,
250 						    0.035f,
251 						    [] () {return vw_fRand0() * 0.1f;});
252 
253 	sharedPlayerFighter->ObjectStatus = eObjectStatus::Player;
254 	sharedPlayerFighter->ArmorInitialStatus *= GameConfig().Profile[CurrentProfile].ShipHullUpgrade;
255 	sharedPlayerFighter->ArmorCurrentStatus = GameConfig().Profile[CurrentProfile].ArmorStatus;
256 	sharedPlayerFighter->ShowStatus = false;
257 
258 	// создаем оружие
259 	for (unsigned i=0; i<sharedPlayerFighter->WeaponSlots.size(); i++) {
260 		if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) {
261 			if (SetEarthSpaceFighterWeapon(PlayerFighter, i+1, GameConfig().Profile[CurrentProfile].Weapon[i])) {
262 				if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock())
263 					sharedWeapon->Ammo = GameConfig().Profile[CurrentProfile].WeaponAmmo[i];
264 				sharedPlayerFighter->WeaponSlots[i].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[i];
265 			}
266 		}
267 	}
268 
269 	// создаем системы (визуальные)
270 	SetEarthSpaceFighterEngine(PlayerFighter, GameEngineSystem);
271 	SetEarthSpaceFighterArmor(PlayerFighter, GameConfig().Profile[CurrentProfile].ShipHullUpgrade - 1);
272 
273 	GameEnemyArmorPenalty = TMPGameEnemyArmorPenalty;
274 
275 
276 
277 	float Width2 = sharedPlayerFighter->Width/2.0f;
278 	float Length2 = sharedPlayerFighter->Length/2.0f;
279 	ShildRadius = vw_sqrtf(Width2*Width2+Length2*Length2);
280 	ShildEnergyStatus = 0.0f;
281 	ShildStartHitStatus = 0.0f;
282 
283 
284 
285 	if (GameConfig().Profile[CurrentProfile].AdvancedProtectionSystem == 3) {
286 		Shild1 = vw_CreateParticleSystem();
287 		if (auto sharedShild1 = Shild1.lock()) {
288 			sharedShild1->ColorStart.r = 0.20f;
289 			sharedShild1->ColorStart.g = 0.50f;
290 			sharedShild1->ColorStart.b = 0.10f;
291 			sharedShild1->ColorEnd.r = 0.20f;
292 			sharedShild1->ColorEnd.g = 0.50f;
293 			sharedShild1->ColorEnd.b = 0.10f;
294 			sharedShild1->AlphaStart = 1.00f;
295 			sharedShild1->AlphaEnd = 0.00f;
296 			sharedShild1->SizeStart = 0.60f;
297 			sharedShild1->SizeVar = 0.10f;
298 			sharedShild1->SizeEnd = 0.10f;
299 			sharedShild1->Speed = 0.00f;
300 			sharedShild1->SpeedOnCreation = -1.00f;
301 			sharedShild1->Theta = 360.00f;
302 			sharedShild1->Life = 1.00f;
303 			sharedShild1->ParticlesPerSec = (int)(40 * ShildRadius);
304 			sharedShild1->CreationType = eParticleCreationType::Sphere;
305 			sharedShild1->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius};
306 			sharedShild1->DeadZone = ShildRadius - 0.05f;
307 			sharedShild1->AlphaShowHide = true;
308 			sharedShild1->IsMagnet = true;
309 			sharedShild1->MagnetFactor = -3.0f;
310 			sharedShild1->Texture = GetPreloadedTextureAsset("gfx/flare1.tga");
311 			sharedShild1->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f};
312 			sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
313 		}
314 
315 		ShildStartHitStatus = 100.0f;
316 		ShildEnergyStatus = 1.0f;
317 	}
318 	if (GameConfig().Profile[CurrentProfile].AdvancedProtectionSystem == 4) {
319 		Shild1 = vw_CreateParticleSystem();
320 		if (auto sharedShild1 = Shild1.lock()) {
321 			sharedShild1->ColorStart.r = 0.50f;
322 			sharedShild1->ColorStart.g = 0.50f;
323 			sharedShild1->ColorStart.b = 1.00f;
324 			sharedShild1->ColorEnd.r = 0.50f;
325 			sharedShild1->ColorEnd.g = 0.50f;
326 			sharedShild1->ColorEnd.b = 1.00f;
327 			sharedShild1->AlphaStart = 0.50f;
328 			sharedShild1->AlphaEnd = 0.00f;
329 			sharedShild1->SizeStart = 0.40f;
330 			sharedShild1->SizeVar = 0.10f;
331 			sharedShild1->SizeEnd = 0.20f;
332 			sharedShild1->Speed = 0.00f;
333 			sharedShild1->SpeedOnCreation = -1.00f;
334 			sharedShild1->Theta = 360.00f;
335 			sharedShild1->Life = 1.00f;
336 			sharedShild1->ParticlesPerSec = (int)(40 * ShildRadius);
337 			sharedShild1->CreationType = eParticleCreationType::Sphere;
338 			sharedShild1->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius};
339 			sharedShild1->DeadZone = ShildRadius - 0.05f;
340 			sharedShild1->IsMagnet = true;
341 			sharedShild1->AlphaShowHide = true;
342 			sharedShild1->MagnetFactor = 2.5f;
343 			sharedShild1->Texture = GetPreloadedTextureAsset("gfx/flare1.tga");
344 			sharedShild1->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f};
345 			sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
346 		}
347 
348 		Shild2 = vw_CreateParticleSystem();
349 		if (auto sharedShild2 = Shild2.lock()) {
350 			sharedShild2->ColorStart.r = 0.50f;
351 			sharedShild2->ColorStart.g = 0.50f;
352 			sharedShild2->ColorStart.b = 1.00f;
353 			sharedShild2->ColorEnd.r = 0.50f;
354 			sharedShild2->ColorEnd.g = 0.50f;
355 			sharedShild2->ColorEnd.b = 1.00f;
356 			sharedShild2->AlphaStart = 0.70f;
357 			sharedShild2->AlphaEnd = 0.10f;
358 			sharedShild2->SizeStart = 0.50f;
359 			sharedShild2->SizeVar = 0.10f;
360 			sharedShild2->SizeEnd = 0.30f;
361 			sharedShild2->Speed = 0.00f;
362 			sharedShild2->SpeedOnCreation = -1.00f;
363 			sharedShild2->Theta = 360.00f;
364 			sharedShild2->Life = 1.00f;
365 			sharedShild2->ParticlesPerSec = (int)(5 * ShildRadius);
366 			sharedShild2->CreationType = eParticleCreationType::Sphere;
367 			sharedShild2->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius};
368 			sharedShild2->DeadZone = ShildRadius - 0.05f;
369 			sharedShild2->IsMagnet = true;
370 			sharedShild2->MagnetFactor = 20.0f;
371 			sharedShild2->Texture = GetPreloadedTextureAsset("gfx/flare1.tga");
372 			sharedShild2->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f};
373 		}
374 
375 		ShildStartHitStatus = 150.0f;
376 		ShildEnergyStatus = 1.0f;
377 	}
378 
379 
380 
381 	// предварительная установка, полностью заряженное устройство
382 	CurrentPlayerShipEnergy = GetShipMaxEnergy(GamePowerSystem);
383 
384 	// предварительно иним состояния управления
385 	LastMouseX = -1;
386 	LastMouseY = -1;
387 	MoveFB = 0.0f;
388 	MoveLR = 0.0f;
389 	CurrentMovementVel = sVECTOR3D{0.0f, 0.0f, 0.0f};
390 
391 	// сброс стрельбы...
392 	PrimaryGroupCurrentFireWeaponNum = 1;
393 	PrimaryGroupCurrentFireWeaponDelay = 0.0f;
394 	SecondaryGroupCurrentFireWeaponNum = 1;
395 	SecondaryGroupCurrentFireWeaponDelay = 0.0f;
396 }
397 
398 
399 
400 
401 
402 
403 
404 
405 //------------------------------------------------------------------------------------
406 // Основная процедура обработки состояния корабля игрока
407 //------------------------------------------------------------------------------------
GamePlayerShip()408 void GamePlayerShip()
409 {
410 	auto sharedPlayerFighter = PlayerFighter.lock();
411 	if (!sharedPlayerFighter)
412 		return;
413 
414 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415 	// проверяем, корабль живой еще, или сбили и нужно его удалить...
416 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
417 	if (sharedPlayerFighter->ArmorCurrentStatus <= 0.0f) {
418 		// редкий случай
419 		if (UndeadDebugMode) {
420 			sharedPlayerFighter->ArmorCurrentStatus = sharedPlayerFighter->ArmorInitialStatus;
421 		} else {
422 			// делаем взрыв
423 			// + 10.0f движение камеры
424 			CreateSpaceExplosion(*sharedPlayerFighter, 31, sharedPlayerFighter->Location, sharedPlayerFighter->Speed+10.0f, PlayerDeadObjectPieceNum);
425 
426 			// включаем музыку и отображение "миссия провалена"
427 			PlayMusicTheme(eMusicTheme::FAILED, 2000, 2000);
428 
429 			// удаляем и уходим отсюда
430 			ReleaseSpaceShip(PlayerFighter);
431 
432 			SetupMissionFailedText(20.0f);
433 
434 			return;
435 		}
436 	}
437 
438 
439 
440 	// голос выводим только в игре! в меню включается пауза
441 	// и если не закончился уровень
442 	if (GameContentTransp < 0.99f && !GameMissionCompleteStatus) {
443 		int WarningMessagesCount = 0;
444 
445 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
446 		// Вывод голосового предупреждения, если навелась ракета
447 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
448 		bool CheckStatus{false};
449 		ForEachProjectile([&] (const cProjectile &Projectile) {
450 			if (auto sharedTarget = Projectile.Target.lock()) {
451 				if ((sharedTarget.get() == sharedPlayerFighter.get()) &&
452 				    // homing missile targeted on this ship, but not homing mine
453 				    ((Projectile.Num < 26) || (Projectile.Num > 29)))
454 					CheckStatus = true;
455 			}
456 		});
457 
458 		if (CheckStatus) {
459 			// проверяем, действительно еще играем (играем только 1 раз!)
460 			if (!vw_IsSoundAvailable(VoiceMissileDetected) &&
461 			    !VoiceMissileDetectedStatus) {
462 				VoiceMissileDetected = PlayVoicePhrase(eVoicePhrase::MissileDetected, 1.0f);
463 				VoiceMissileDetectedStatus = true;
464 			}
465 
466 			// визуальный вывод - выводим постоянно
467 			vw_SetFontSize(24);
468 			int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Missile Detected"))) / 2;
469 			vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, CurrentAlert3, vw_GetTextUTF32("Missile Detected"));
470 			ResetFontSize();
471 			WarningMessagesCount++;
472 		} else {
473 			if (CurrentAlert3 == 1.0f) { // сделали полный цикл , предыдущее значение счетчика было минимальное
474 				VoiceMissileDetectedStatus = false;
475 			} else if (VoiceMissileDetectedStatus) {
476 				// визуальный вывод - выводим постоянно
477 				vw_SetFontSize(24);
478 				int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Missile Detected"))) / 2;
479 				vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, CurrentAlert3, vw_GetTextUTF32("Missile Detected"));
480 				ResetFontSize();
481 				WarningMessagesCount++;
482 			}
483 		}
484 
485 
486 
487 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
488 		// Вывод голосового предупреждения если возможно столкновение
489 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
490 		bool CollisionDetected = false;
491 		ForEachSpaceObject([&CollisionDetected, &sharedPlayerFighter] (const cSpaceObject &tmpSpace, eSpaceCycle &Command) {
492 			// test with "immortal" big asteroids
493 			if ((tmpSpace.ObjectType == eObjectType::BigAsteroid) &&
494 			    (vw_SphereSphereCollision(sharedPlayerFighter->Radius, sharedPlayerFighter->Location,
495 						      tmpSpace.Radius, tmpSpace.Location, tmpSpace.PrevLocation)) &&
496 			    (vw_SphereAABBCollision(tmpSpace.AABB, tmpSpace.Location, sharedPlayerFighter->Radius,
497 						    sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation))) {
498 				CollisionDetected = true;
499 				Command = eSpaceCycle::Break;
500 			}
501 		});
502 		ForEachGroundObject([&CollisionDetected, &sharedPlayerFighter] (const cGroundObject &tmpGround, eGroundCycle &Command) {
503 			// test with "immortal" civilian buildings
504 			if ((tmpGround.ObjectType == eObjectType::CivilianBuilding) &&
505 			    (vw_SphereSphereCollision(sharedPlayerFighter->Radius, sharedPlayerFighter->Location,
506 						      tmpGround.Radius, tmpGround.Location, tmpGround.PrevLocation)) &&
507 			    (vw_SphereAABBCollision(tmpGround.AABB, tmpGround.Location, sharedPlayerFighter->Radius,
508 						    sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation)) &&
509 			    (vw_SphereOBBCollision(tmpGround.OBB.Box, tmpGround.OBB.Location, tmpGround.Location,
510 						   tmpGround.CurrentRotationMat, sharedPlayerFighter->Radius,
511 						   sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation))) {
512 				CollisionDetected = true;
513 				Command = eGroundCycle::Break;
514 			}
515 		});
516 		if (CollisionDetected) {
517 			// голос, ворнинг, можем столкнуться с объектом
518 			// проверяем, действительно еще играем
519 			if (!vw_IsSoundAvailable(VoiceWarningCollisionDetected))
520 				VoiceWarningCollisionDetected = PlayVoicePhrase(eVoicePhrase::Warning, 1.0f);
521 
522 			// визуальный вывод - выводим постоянно
523 			vw_SetFontSize(24);
524 			int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Collision Course Detected"))) / 2;
525 			vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::red}, CurrentAlert3, vw_GetTextUTF32("Collision Course Detected"));
526 			ResetFontSize();
527 			WarningMessagesCount++;
528 		}
529 
530 
531 
532 
533 
534 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
535 		// Вывод голосового предупреждения, если в оружие нет пуль
536 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
537 		if (!sharedPlayerFighter->WeaponSlots.empty()) { // если вообще есть оружие
538 			for (const auto &tmpWeaponSlot : sharedPlayerFighter->WeaponSlots) {
539 				// если нажали стрелять, а патронов нет в одном из орудий
540 				if (auto sharedWeapon = tmpWeaponSlot.Weapon.lock()) {
541 					if (tmpWeaponSlot.SetFire &&
542 					    (sharedWeapon->Ammo <= 0) &&
543 					    !vw_IsSoundAvailable(VoiceWeaponMalfunction)) // проверяем, действительно еще играем
544 						VoiceWeaponMalfunction = PlayVoicePhrase(eVoicePhrase::WeaponMalfunction, 1.0f);
545 				}
546 			}
547 		}
548 
549 
550 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
551 		// Звуковое оповещение, если жизни менее 10%
552 		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
553 		// если меньше 10% нужно бить тревогу
554 		if ((sharedPlayerFighter->ArmorCurrentStatus < sharedPlayerFighter->ArmorInitialStatus / 10.0f) &&
555 		    !vw_IsSoundAvailable(SoundLowLife)) // если не играем, запускаем звук сирены
556 			SoundLowLife = PlayMenuSFX(eMenuSFX::WarningLowLife, 1.0f);
557 	}
558 
559 
560 
561 
562 
563 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
564 	// управление кораблем - движение
565 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
566 	if (GameContentTransp < 1.0f) { // если не в меню нажимают
567 		// получаем данные, для обоих типов управления
568 		// уже получили данные, нужно игнорировать остальные источникик
569 		bool NeedSkip = false;
570 
571 		// mouse + joystick (since we emulate mouse movements)
572 		if (GameConfig().MouseControl) {
573 			SDL_GetMouseState(&LastMouseXR, &LastMouseYR);
574 
575 			int X, Y;
576 			vw_GetMousePos(X, Y);
577 			if (LastMouseX == -1 && LastMouseY == -1) {
578 				LastMouseX = X;
579 				LastMouseY = Y;
580 			} else {
581 				if (X != LastMouseX || Y != LastMouseY) {
582 					// 0.9+0.1 = 1.0 - минимум, всегда 1.0 должен быть!
583 					float Koef = 0.9f + GameConfig().ControlSensivity / 10.0f;
584 
585 					// при любом реальном разрешении у нас x и y меняются с учетом AspectRatio
586 					float AWw2 = GameConfig().InternalWidth / 2.0f;
587 					float AHw2 = GameConfig().InternalHeight / 2.0f;
588 
589 					MoveFB += (-(Y-LastMouseY)/AHw2)*Koef;
590 					MoveLR += ( (X-LastMouseX)/AWw2)*Koef;
591 
592 					NeedSkip = true;
593 				}
594 
595 				LastMouseX = X;
596 				LastMouseY = Y;
597 			}
598 
599 		}
600 
601 		// клавиатура
602 		if (!NeedSkip) {
603 			if (vw_GetKeyStatus(GameConfig().KeyBoardDown))
604 				MoveFB -= 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta;
605 			if (vw_GetKeyStatus(GameConfig().KeyBoardUp))
606 				MoveFB += 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta;
607 			if (vw_GetKeyStatus(GameConfig().KeyBoardLeft))
608 				MoveLR -= 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta;
609 			if (vw_GetKeyStatus(GameConfig().KeyBoardRight))
610 				MoveLR += 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta;
611 		}
612 
613 
614 
615 		// дополнительная проверка, т.к. можем выйти выйти за пределы
616 		if (MoveFB < -1.0f) MoveFB = -1.0f;
617 		if (MoveFB > 1.0f) MoveFB = 1.0f;
618 		if (MoveLR < -1.0f) MoveLR = -1.0f;
619 		if (MoveLR > 1.0f) MoveLR = 1.0f;
620 
621 
622 
623 
624 
625 
626 		// находим конечную точку перемещения
627 		sVECTOR3D PlayerFighterEndLocation;
628 		if (GameConfig().InternalWidth == 1024)
629 			PlayerFighterEndLocation = sVECTOR3D{-(73.15f-sharedPlayerFighter->Width/2.0f+MoveFB*(20.05f-sharedPlayerFighter->Length/6.0f))*MoveLR,
630 							     0.0f,
631 							     (46.0f-sharedPlayerFighter->Length/2.0f)*MoveFB};
632 		else
633 			PlayerFighterEndLocation = sVECTOR3D{-(70.0f-sharedPlayerFighter->Width/2.0f+MoveFB*(23.2f-sharedPlayerFighter->Length/6.0f))*MoveLR,
634 							     0.0f,
635 							     (46.0f-sharedPlayerFighter->Length/2.0f)*MoveFB};
636 
637 		PlayerFighterEndLocation += GetCameraCoveredDistance();
638 
639 		// если есть двигатель
640 		if (GameEngineSystem != 0) {
641 			// в зависимости от типа управления выполняем действия
642 			if (GameConfig().Profile[CurrentProfile].SpaceShipControlMode == 1) {
643 				// аркадный режим
644 
645 				// запускаем маневровые двигатели, если тянем корабль в сторону
646 				if ((int)sharedPlayerFighter->Location.x > (int)PlayerFighterEndLocation.x) {
647 					PlayerFighterLeftEng = true;
648 					PlayerFighterRightEng = false;
649 				}
650 				if ((int)sharedPlayerFighter->Location.x < (int)PlayerFighterEndLocation.x) {
651 					PlayerFighterLeftEng = false;
652 					PlayerFighterRightEng = true;
653 				}
654 				// если не двигаем, останавливаем маневровые двигатели
655 				if ((int)sharedPlayerFighter->Location.x == (int)PlayerFighterEndLocation.x) {
656 					PlayerFighterLeftEng = false;
657 					PlayerFighterRightEng = false;
658 				}
659 
660 
661 				// находим расстояние
662 				sVECTOR3D PlayerFighterNewDirection = PlayerFighterEndLocation - sharedPlayerFighter->Location;
663 				float EndLocationDistance = PlayerFighterNewDirection.Length();
664 
665 				// находим направление движения
666 				PlayerFighterNewDirection.Normalize();
667 
668 				float SimMoveSpeed = EndLocationDistance;
669 
670 				if (SimMoveSpeed > 30.0f) SimMoveSpeed = 30.0f;
671 
672 				SimMoveSpeed = SimMoveSpeed*4.0f*sharedPlayerFighter->TimeDelta;
673 
674 
675 				// получаем текущее движение
676 				CurrentMovementVel = PlayerFighterNewDirection^SimMoveSpeed;
677 
678 				// проверка
679 				float MaxSpeed = CurrentMovementVel.Length();
680 				CurrentMovementVel.Normalize();
681 				if (MaxSpeed > 30.0f) MaxSpeed = 30.0f;
682 
683 				CurrentMovementVel = CurrentMovementVel^MaxSpeed;
684 
685 			} else {
686 				// симулятивный режим
687 
688 
689 				// запускаем маневровые двигатели, если тянем корабль в сторону
690 				if ((int)sharedPlayerFighter->Location.x > (int)PlayerFighterEndLocation.x) {
691 					PlayerFighterLeftEng = true;
692 					PlayerFighterRightEng = false;
693 				}
694 				if ((int)sharedPlayerFighter->Location.x < (int)PlayerFighterEndLocation.x) {
695 					PlayerFighterLeftEng = false;
696 					PlayerFighterRightEng = true;
697 				}
698 				// если не двигаем, останавливаем маневровые двигатели
699 				if ((int)sharedPlayerFighter->Location.x == (int)PlayerFighterEndLocation.x) {
700 					PlayerFighterLeftEng = false;
701 					PlayerFighterRightEng = false;
702 				}
703 
704 
705 				// находим расстояние
706 				sVECTOR3D PlayerFighterNewDirection = PlayerFighterEndLocation - sharedPlayerFighter->Location;
707 				float EndLocationDistance = PlayerFighterNewDirection.Length();
708 
709 				// находим направление движения
710 				PlayerFighterNewDirection.Normalize();
711 
712 				float SimMoveSpeed = EndLocationDistance;
713 
714 				if (SimMoveSpeed > sharedPlayerFighter->MaxSpeed)
715 					SimMoveSpeed = sharedPlayerFighter->MaxSpeed;
716 
717 				SimMoveSpeed = SimMoveSpeed*(sharedPlayerFighter->MaxAcceler/14.0f)*sharedPlayerFighter->TimeDelta;
718 
719 
720 				// получаем текущее движение
721 				CurrentMovementVel = PlayerFighterNewDirection^SimMoveSpeed;
722 
723 				// проверка
724 				float MaxSpeed = CurrentMovementVel.Length();
725 				CurrentMovementVel.Normalize();
726 				if (MaxSpeed > sharedPlayerFighter->MaxSpeed)
727 					MaxSpeed = sharedPlayerFighter->MaxSpeed;
728 
729 				CurrentMovementVel = CurrentMovementVel^MaxSpeed;
730 
731 			}
732 		}
733 
734 		// переносим корабль
735 		sVECTOR3D CurrentVel = sharedPlayerFighter->Location + CurrentMovementVel;
736 		CurrentVel.y = 0.0f;
737 		sharedPlayerFighter->SetLocationArcadePlayer(CurrentVel);
738 
739 
740 
741 		// если стандартный аспект рейшен, надо перемещать камеру в дополнении к перемещению корабля
742 		if (GameConfig().InternalWidth == 1024) {
743 			float DeviationSize = 14.55f;
744 
745 			if (sharedPlayerFighter->Location.x < 0.0f) {
746 				float Diff = sharedPlayerFighter->Location.x / 3.5f;
747 				if (Diff < -DeviationSize)
748 					Diff = -DeviationSize;
749 
750 				sVECTOR3D TMPCameraLocation;
751 				vw_GetCameraLocation(&TMPCameraLocation);
752 				TMPCameraLocation.x = Diff;
753 				vw_SetCameraLocation(TMPCameraLocation);
754 			} else {
755 				float Diff = sharedPlayerFighter->Location.x / 3.5f;
756 				if (Diff > DeviationSize)
757 					Diff = DeviationSize;
758 
759 				sVECTOR3D TMPCameraLocation;
760 				vw_GetCameraLocation(&TMPCameraLocation);
761 				TMPCameraLocation.x = Diff;
762 				vw_SetCameraLocation(TMPCameraLocation);
763 			}
764 		}
765 
766 	}
767 
768 
769 
770 
771 
772 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
773 	// управление кораблем - стрельба
774 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
775 	if (GameContentTransp < 0.5f) // если не в меню нажимают
776 		if (!sharedPlayerFighter->WeaponSlots.empty()) { // если вообще есть оружие
777 
778 			int PrimCount = 0;
779 			float PrimTime = 0.0f;
780 			int SecCount = 0;
781 			float SecTime = 0.0f;
782 
783 			PrimaryGroupCurrentFireWeaponDelay -= sharedPlayerFighter->TimeDelta;
784 			SecondaryGroupCurrentFireWeaponDelay -= sharedPlayerFighter->TimeDelta;
785 
786 			// находим кол-во оружия в группах
787 			for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) {
788 				if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { // если это оружие установлено
789 
790 					if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 1 ||
791 					    GameConfig().Profile[CurrentProfile].WeaponControl[i] == 3) {
792 						if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) {
793 							PrimCount++;
794 							PrimTime += sharedWeapon->NextFireTime;
795 						}
796 					}
797 
798 					if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 2 ||
799 					    GameConfig().Profile[CurrentProfile].WeaponControl[i] == 3) {
800 						if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) {
801 							SecCount++;
802 							SecTime += sharedWeapon->NextFireTime;
803 						}
804 					}
805 				}
806 			}
807 
808 
809 			int PrimNum = 0;
810 			int SecNum = 0;
811 
812 			for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) {
813 				if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { // если это оружие установлено
814 
815 					sharedPlayerFighter->WeaponSlots[i].SetFire = false;
816 
817 					// получаем данные, в какую группу относится
818 					bool primary_fire = false;
819 					bool secondary_fire = false;
820 					if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 1 ||
821 					    GameConfig().Profile[CurrentProfile].WeaponControl[i] ==3)
822 						primary_fire = true;
823 					if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 2 ||
824 					    GameConfig().Profile[CurrentProfile].WeaponControl[i] ==3)
825 						secondary_fire = true;
826 
827 					// мышка
828 					if (GameConfig().MouseControl) {
829 						// primary fire
830 						if (primary_fire)
831 							if (vw_GetMouseButtonStatus(GameConfig().MousePrimary)) {
832 								if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) {
833 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
834 								} else {
835 									PrimNum++;
836 									if (PrimaryGroupCurrentFireWeaponNum == PrimNum &&
837 									    PrimaryGroupCurrentFireWeaponDelay <= 0.0f) {
838 										PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount);
839 										sharedPlayerFighter->WeaponSlots[i].SetFire = true;
840 										PrimaryGroupCurrentFireWeaponNum++;
841 										if (PrimaryGroupCurrentFireWeaponNum > PrimCount)
842 											PrimaryGroupCurrentFireWeaponNum = 1;
843 									}
844 								}
845 							}
846 
847 						// secondary fire
848 						if (secondary_fire)
849 							if (vw_GetMouseButtonStatus(GameConfig().MouseSecondary)) {
850 								if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) {
851 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
852 								} else {
853 									SecNum++;
854 									if (SecondaryGroupCurrentFireWeaponNum == SecNum &&
855 									    SecondaryGroupCurrentFireWeaponDelay <= 0.0f) {
856 										SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount);
857 										sharedPlayerFighter->WeaponSlots[i].SetFire = true;
858 										SecondaryGroupCurrentFireWeaponNum++;
859 										if (SecondaryGroupCurrentFireWeaponNum > SecCount)
860 											SecondaryGroupCurrentFireWeaponNum = 1;
861 									}
862 								}
863 							}
864 
865 						// альтернативное управление
866 						if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 2)
867 							if (vw_GetMouseButtonStatus(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i]))
868 								sharedPlayerFighter->WeaponSlots[i].SetFire = true;
869 					}
870 
871 
872 					// джойстик
873 					if (isJoystickAvailable()) {
874 						// primary fire
875 						if (primary_fire)
876 							if (GetJoystickButton(GameConfig().JoystickPrimary)) {
877 								if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) {
878 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
879 								} else {
880 									PrimNum++;
881 									if (PrimaryGroupCurrentFireWeaponNum == PrimNum &&
882 									    PrimaryGroupCurrentFireWeaponDelay <= 0.0f) {
883 										PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount);
884 										sharedPlayerFighter->WeaponSlots[i].SetFire = true;
885 										PrimaryGroupCurrentFireWeaponNum++;
886 										if (PrimaryGroupCurrentFireWeaponNum > PrimCount)
887 											PrimaryGroupCurrentFireWeaponNum = 1;
888 									}
889 								}
890 							}
891 
892 						// secondary fire
893 						if (secondary_fire)
894 							if (GetJoystickButton(GameConfig().JoystickSecondary)) {
895 								if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) {
896 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
897 								} else {
898 									SecNum++;
899 									if (SecondaryGroupCurrentFireWeaponNum == SecNum &&
900 									    SecondaryGroupCurrentFireWeaponDelay <= 0.0f) {
901 										SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount);
902 										sharedPlayerFighter->WeaponSlots[i].SetFire = true;
903 										SecondaryGroupCurrentFireWeaponNum++;
904 										if (SecondaryGroupCurrentFireWeaponNum > SecCount)
905 											SecondaryGroupCurrentFireWeaponNum = 1;
906 									}
907 								}
908 							}
909 
910 						// альтернативное управление
911 						if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 3)
912 							if (GetJoystickButton(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i]))
913 								sharedPlayerFighter->WeaponSlots[i].SetFire = true;
914 					}
915 
916 					// клавиатура
917 
918 					// primary fire
919 					if (primary_fire)
920 						if (vw_GetKeyStatus(GameConfig().KeyBoardPrimary)) {
921 							if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) {
922 								sharedPlayerFighter->WeaponSlots[i].SetFire = true;
923 							} else {
924 								PrimNum++;
925 								if (PrimaryGroupCurrentFireWeaponNum == PrimNum &&
926 								    PrimaryGroupCurrentFireWeaponDelay <= 0.0f) {
927 									PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount);
928 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
929 									PrimaryGroupCurrentFireWeaponNum++;
930 									if (PrimaryGroupCurrentFireWeaponNum > PrimCount)
931 										PrimaryGroupCurrentFireWeaponNum = 1;
932 								}
933 							}
934 						}
935 
936 					// secondary fire
937 					if (secondary_fire)
938 						if (vw_GetKeyStatus(GameConfig().KeyBoardSecondary)) {
939 							if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) {
940 								sharedPlayerFighter->WeaponSlots[i].SetFire = true;
941 							} else {
942 								SecNum++;
943 								if (SecondaryGroupCurrentFireWeaponNum == SecNum &&
944 								    SecondaryGroupCurrentFireWeaponDelay <= 0.0f) {
945 									SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount);
946 									sharedPlayerFighter->WeaponSlots[i].SetFire = true;
947 									SecondaryGroupCurrentFireWeaponNum++;
948 									if (SecondaryGroupCurrentFireWeaponNum > SecCount)
949 										SecondaryGroupCurrentFireWeaponNum = 1;
950 								}
951 							}
952 						}
953 
954 					// альтернативное управление
955 					if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 1)
956 						if (vw_GetKeyStatus(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i]))
957 							sharedPlayerFighter->WeaponSlots[i].SetFire = true;
958 
959 				}
960 			}
961 		}
962 
963 
964 
965 
966 
967 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
968 	// управление и работа внутренних систем корабля
969 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
970 	// сделать:
971 	// учитывать, как работает двигатель... стоим или летим...
972 	// если не аркадный режим...
973 	if (GameSpaceShipControlMode != 1) {
974 		if (CurrentPlayerShipEnergy < GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta) {
975 			sharedPlayerFighter->MaxSpeed = 0.0f;
976 			sharedPlayerFighter->MaxAcceler = 0.0f;
977 			sharedPlayerFighter->MaxSpeedRotate = 0.0f;
978 			// глушим двигатели
979 			for (auto &tmpEngine : sharedPlayerFighter->Engines) {
980 				if (auto sharedEngine = tmpEngine.lock())
981 					sharedEngine->IsSuppressed = true;
982 			}
983 			if (!sharedPlayerFighter->EnginesLeft.empty()) {
984 				for (auto &tmpEngineLeft : sharedPlayerFighter->EnginesLeft) {
985 					if (auto sharedEngineLeft = tmpEngineLeft.lock())
986 						sharedEngineLeft->IsSuppressed = true;
987 				}
988 			}
989 			if (!sharedPlayerFighter->EnginesRight.empty()) {
990 				for (auto &tmpEngineRight : sharedPlayerFighter->EnginesRight) {
991 					if (auto sharedEngineRight = tmpEngineRight.lock())
992 						sharedEngineRight->IsSuppressed = true;
993 				}
994 			}
995 		} else {
996 			sharedPlayerFighter->MaxSpeed = GetEnginePower(GameEngineSystem);
997 			sharedPlayerFighter->MaxAcceler = GetEngineAcceleration(GameEngineSystem);
998 			sharedPlayerFighter->MaxSpeedRotate = GetEngineRotatePower(GameEngineSystem);
999 			// запускаем прорисовку
1000 			for (auto &tmpEngine : sharedPlayerFighter->Engines) {
1001 				if (auto sharedEngine = tmpEngine.lock())
1002 					sharedEngine->IsSuppressed = false;
1003 			}
1004 			if (!sharedPlayerFighter->EnginesLeft.empty()) {
1005 				for (auto &tmpEngineLeft : sharedPlayerFighter->EnginesLeft) {
1006 					if (auto sharedEngineLeft = tmpEngineLeft.lock())
1007 						sharedEngineLeft->IsSuppressed = false;
1008 				}
1009 			}
1010 			if (!sharedPlayerFighter->EnginesRight.empty()) {
1011 				for (auto &tmpEngineRight : sharedPlayerFighter->EnginesRight) {
1012 					if (auto sharedEngineRight = tmpEngineRight.lock())
1013 						sharedEngineRight->IsSuppressed = false;
1014 				}
1015 			}
1016 			CurrentPlayerShipEnergy -= GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta;
1017 		}
1018 	}
1019 
1020 
1021 
1022 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1023 	// энергия для перезарядки и выстрела...
1024 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1025 	// сейчас получаем всю энергию для перезарядки и выстрела
1026 	// потом лучше будет переделать на постепенный отбор энергии
1027 	for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) {
1028 		if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) {
1029 			if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) {
1030 				if (sharedWeapon->CurrentEnergyAccumulated < sharedWeapon->EnergyUse) {
1031 					// если энергии не достаточно для зарядки орудия
1032 					if (CurrentPlayerShipEnergy < sharedWeapon->EnergyUse) {
1033 						// останавливаем перезарядку оружия
1034 						sharedWeapon->LastFireTime += sharedPlayerFighter->TimeDelta;
1035 						if (auto sharedFire = sharedWeapon->Fire.lock())
1036 							sharedFire->IsSuppressed = true;
1037 					} else {
1038 						// если энергии достаточно, все нормально берем ее и перезаряжаем оружие
1039 						sharedWeapon->CurrentEnergyAccumulated = sharedWeapon->EnergyUse;
1040 						CurrentPlayerShipEnergy -= sharedWeapon->EnergyUse;
1041 					}
1042 				}
1043 			}
1044 		}
1045 	}
1046 
1047 
1048 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1049 	// питание других (защитных) систем
1050 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1051 	if (CurrentPlayerShipEnergy >= GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta) {
1052 
1053 		switch (GameAdvancedProtectionSystem) {
1054 		// нано роботы
1055 		case 1:
1056 			// восстанавливаем на 0.5% в секунду
1057 			if (sharedPlayerFighter->ArmorCurrentStatus < sharedPlayerFighter->ArmorInitialStatus) {
1058 				CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta;
1059 				sharedPlayerFighter->ArmorCurrentStatus += (sharedPlayerFighter->ArmorInitialStatus / 200.0f) * sharedPlayerFighter->TimeDelta;
1060 				if (sharedPlayerFighter->ArmorCurrentStatus > sharedPlayerFighter->ArmorInitialStatus)
1061 					sharedPlayerFighter->ArmorCurrentStatus = sharedPlayerFighter->ArmorInitialStatus;
1062 			}
1063 			break;
1064 		// спец защитный слой
1065 		case 2:
1066 			break; // ничего не делаем
1067 		// щит
1068 		case 3:
1069 			// восстанавливаем полностью за 4 секунды
1070 			if (ShildEnergyStatus < 1.0f) {
1071 				CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta;
1072 				ShildEnergyStatus += 0.02f * sharedPlayerFighter->TimeDelta;
1073 				if (ShildEnergyStatus > 1.0f)
1074 					ShildEnergyStatus = 1.0f;
1075 			}
1076 			break;
1077 		// отражатель
1078 		case 4:
1079 			// восстанавливаем полностью за 2 секунды
1080 			if (ShildEnergyStatus < 1.0f) {
1081 				CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta;
1082 				ShildEnergyStatus += 0.03f * sharedPlayerFighter->TimeDelta;
1083 				if (ShildEnergyStatus > 1.0f)
1084 					ShildEnergyStatus = 1.0f;
1085 			}
1086 			break;
1087 		}
1088 	}
1089 
1090 
1091 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1092 	// управление визуализацией щитов-дефлекторов
1093 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1094 	if (auto sharedShild1 = Shild1.lock()) {
1095 		sharedShild1->MoveSystem(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
1096 		sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
1097 		sharedShild1->RotateSystemAndParticlesByAngle(sharedPlayerFighter->Rotation);
1098 		sharedShild1->ParticlesPerSec = (int)(40 * ShildEnergyStatus * ShildRadius);
1099 	}
1100 	if (auto sharedShild2 = Shild2.lock()) {
1101 		sharedShild2->MoveSystem(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
1102 		sharedShild2->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location);
1103 		sharedShild2->RotateSystemAndParticlesByAngle(sharedPlayerFighter->Rotation);
1104 		sharedShild2->ParticlesPerSec = (int)(5 * ShildEnergyStatus * ShildRadius);
1105 	}
1106 
1107 
1108 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1109 	// если реактор - можем генерировать энергию, если баттарея - нет
1110 	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1111 	CurrentPlayerShipEnergy += GetShipRechargeEnergy(GamePowerSystem)*sharedPlayerFighter->TimeDelta;
1112 	if (CurrentPlayerShipEnergy > GetShipMaxEnergy(GamePowerSystem)) CurrentPlayerShipEnergy = GetShipMaxEnergy(GamePowerSystem);
1113 
1114 }
1115 
1116 } // astromenace namespace
1117 } // viewizard namespace
1118