/************************************************************************************ AstroMenace Hardcore 3D space scroll-shooter with spaceship upgrade possibilities. Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard AstroMenace is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. AstroMenace is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AstroMenace. If not, see . Website: https://viewizard.com/ Project: https://github.com/viewizard/astromenace E-mail: viewizard@viewizard.com *************************************************************************************/ // TODO translate comments #include "../core/core.h" #include "../config/config.h" #include "../platform/platform.h" #include "../ui/font.h" #include "../ui/game/text.h" #include "../assets/audio.h" #include "../assets/texture.h" #include "../game/camera.h" #include "../object3d/explosion/explosion.h" #include "../object3d/space_object/space_object.h" #include "../object3d/ground_object/ground_object.h" #include "../object3d/space_ship/space_ship.h" #include "../object3d/projectile/projectile.h" #include "../game.h" // FIXME "game.h" should be replaced by individual headers // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17) namespace viewizard { namespace astromenace { // режим неубиваемости... отладка bool UndeadDebugMode = false; // состояние управления, нужно, чтобы определять что менялось int LastMouseX = -1; int LastMouseY = -1; // для восстановления положения курсора в меню, точнее при выходе из него int LastMouseXR = 0; int LastMouseYR = 0; // в показателях от -1.0f до 1.0f // назад-вперед float MoveFB = 0.0f; // лево-право float MoveLR = 0.0f; // текущая энергия корабля float CurrentPlayerShipEnergy; // для управления в аркадном режиме маневровыми двигателями bool PlayerFighterLeftEng = false; bool PlayerFighterRightEng = false; std::weak_ptr Shild1{}; std::weak_ptr Shild2{}; float ShildRadius; float ShildEnergyStatus; float ShildStartHitStatus; // голос с ворнингом, если столкнулись с несбиваемой частью unsigned int VoiceWarningCollisionDetected{0}; // Номер, для проигрывания голосового сообщения об обнаружении ракеты unsigned int VoiceMissileDetected{0}; bool VoiceMissileDetectedStatus{false}; // номер, для проигрывания голосового сообщения о проблемах в орудии // Номер, для проигрывания голосового сообщения об отсутствии снарядов в боекомплекте unsigned int VoiceWeaponMalfunction{0}; // для звука - мало жизни unsigned int SoundLowLife{0}; // тут храним какая часть взорвалась на корабле игрока int PlayerDeadObjectPieceNum; // симулятивный режим sVECTOR3D CurrentMovementVel(0.0f, 0.0f, 0.0f); // работа с морганием вывода extern float CurrentAlert2; extern float CurrentAlert3; // для переменного типа стрельбы int PrimaryGroupCurrentFireWeaponNum = 1; float PrimaryGroupCurrentFireWeaponDelay = 0.0f; int SecondaryGroupCurrentFireWeaponNum = 1; float SecondaryGroupCurrentFireWeaponDelay = 0.0f; //------------------------------------------------------------------------------------ // Получаем максимально возможное значение энергии для устройства реактора-батареи //------------------------------------------------------------------------------------ float GetShipMaxEnergy(int Num) { switch (Num) { case 0: return 0.0f; // аккамулятор case 1: return 100.0f; // ядерный case 2: return 200.0f; // плазменный case 3: return 400.0f; // антиматерия case 4: return 800.0f; default: std::cerr << __func__ << "(): " << "wrong Num.\n"; break; } return -1.0f; } //------------------------------------------------------------------------------------ // Получаем перезарядку энергии, в секунду //------------------------------------------------------------------------------------ float GetShipRechargeEnergy(int Num) { switch (Num) { case 0: return 0.0f; // аккамулятор case 1: return 20.0f; // ядерный case 2: return 50.0f; // плазменный case 3: return 130.0f; // антиматерия case 4: return 250.0f; default: std::cerr << __func__ << "(): " << "wrong Num.\n"; break; } return -1.0f; } //------------------------------------------------------------------------------------ // Получаем расход энергии в секунду для доп систем (GameAdvancedProtectionSystem) //------------------------------------------------------------------------------------ float GetShipProtectionSystemEnergyUse(int Num) { switch (Num) { // нано роботы case 1: return 10.0f; // спец защитный слой case 2: return 0.0f; // щит case 3: return 50.0f; // отражатель case 4: return 100.0f; default: std::cerr << __func__ << "(): " << "wrong Num.\n"; break; } return 0.0f; } //------------------------------------------------------------------------------------ // Получаем расход энергии в секунду для двигателей GameEngineSystem //------------------------------------------------------------------------------------ float GetShipEngineSystemEnergyUse(int Num) { switch (Num) { // обычные двигатели case 1: return 5.0f; // фатоновые case 2: return 10.0f; // плазменные case 3: return 30.0f; // на антиматерии case 4: return 60.0f; default: std::cerr << __func__ << "(): " << "wrong Num.\n"; break; } return 0.0f; } //------------------------------------------------------------------------------------ // Инициализация корабля игрока //------------------------------------------------------------------------------------ void InitGamePlayerShip() { // создаем корабль игрока по настройкам в профайле VoiceMissileDetected = 0; VoiceMissileDetectedStatus = false; VoiceWeaponMalfunction = 0; SoundLowLife = 0; int TMPGameEnemyArmorPenalty = GameEnemyArmorPenalty; GameEnemyArmorPenalty = 1; // если не создано, здесь будет ноль скорее всего if (GameConfig().Profile[CurrentProfile].ShipHull == 0) std::cerr << __func__ << "(): " << "Error, Pilot Profile not created.\n"; PlayerFighter = CreateEarthSpaceFighter(GameConfig().Profile[CurrentProfile].ShipHull); auto sharedPlayerFighter = PlayerFighter.lock(); if (!sharedPlayerFighter) return; sharedPlayerFighter->ShipShake.emplace_back(sVECTOR3D{0.0f, 0.0f, 1.0f}, 0, 0.035f, [] () {return vw_fRand0() * 0.1f;}); sharedPlayerFighter->ObjectStatus = eObjectStatus::Player; sharedPlayerFighter->ArmorInitialStatus *= GameConfig().Profile[CurrentProfile].ShipHullUpgrade; sharedPlayerFighter->ArmorCurrentStatus = GameConfig().Profile[CurrentProfile].ArmorStatus; sharedPlayerFighter->ShowStatus = false; // создаем оружие for (unsigned i=0; iWeaponSlots.size(); i++) { if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { if (SetEarthSpaceFighterWeapon(PlayerFighter, i+1, GameConfig().Profile[CurrentProfile].Weapon[i])) { if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) sharedWeapon->Ammo = GameConfig().Profile[CurrentProfile].WeaponAmmo[i]; sharedPlayerFighter->WeaponSlots[i].YAngle = -GameConfig().Profile[CurrentProfile].WeaponSlotYAngle[i]; } } } // создаем системы (визуальные) SetEarthSpaceFighterEngine(PlayerFighter, GameEngineSystem); SetEarthSpaceFighterArmor(PlayerFighter, GameConfig().Profile[CurrentProfile].ShipHullUpgrade - 1); GameEnemyArmorPenalty = TMPGameEnemyArmorPenalty; float Width2 = sharedPlayerFighter->Width/2.0f; float Length2 = sharedPlayerFighter->Length/2.0f; ShildRadius = vw_sqrtf(Width2*Width2+Length2*Length2); ShildEnergyStatus = 0.0f; ShildStartHitStatus = 0.0f; if (GameConfig().Profile[CurrentProfile].AdvancedProtectionSystem == 3) { Shild1 = vw_CreateParticleSystem(); if (auto sharedShild1 = Shild1.lock()) { sharedShild1->ColorStart.r = 0.20f; sharedShild1->ColorStart.g = 0.50f; sharedShild1->ColorStart.b = 0.10f; sharedShild1->ColorEnd.r = 0.20f; sharedShild1->ColorEnd.g = 0.50f; sharedShild1->ColorEnd.b = 0.10f; sharedShild1->AlphaStart = 1.00f; sharedShild1->AlphaEnd = 0.00f; sharedShild1->SizeStart = 0.60f; sharedShild1->SizeVar = 0.10f; sharedShild1->SizeEnd = 0.10f; sharedShild1->Speed = 0.00f; sharedShild1->SpeedOnCreation = -1.00f; sharedShild1->Theta = 360.00f; sharedShild1->Life = 1.00f; sharedShild1->ParticlesPerSec = (int)(40 * ShildRadius); sharedShild1->CreationType = eParticleCreationType::Sphere; sharedShild1->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius}; sharedShild1->DeadZone = ShildRadius - 0.05f; sharedShild1->AlphaShowHide = true; sharedShild1->IsMagnet = true; sharedShild1->MagnetFactor = -3.0f; sharedShild1->Texture = GetPreloadedTextureAsset("gfx/flare1.tga"); sharedShild1->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f}; sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); } ShildStartHitStatus = 100.0f; ShildEnergyStatus = 1.0f; } if (GameConfig().Profile[CurrentProfile].AdvancedProtectionSystem == 4) { Shild1 = vw_CreateParticleSystem(); if (auto sharedShild1 = Shild1.lock()) { sharedShild1->ColorStart.r = 0.50f; sharedShild1->ColorStart.g = 0.50f; sharedShild1->ColorStart.b = 1.00f; sharedShild1->ColorEnd.r = 0.50f; sharedShild1->ColorEnd.g = 0.50f; sharedShild1->ColorEnd.b = 1.00f; sharedShild1->AlphaStart = 0.50f; sharedShild1->AlphaEnd = 0.00f; sharedShild1->SizeStart = 0.40f; sharedShild1->SizeVar = 0.10f; sharedShild1->SizeEnd = 0.20f; sharedShild1->Speed = 0.00f; sharedShild1->SpeedOnCreation = -1.00f; sharedShild1->Theta = 360.00f; sharedShild1->Life = 1.00f; sharedShild1->ParticlesPerSec = (int)(40 * ShildRadius); sharedShild1->CreationType = eParticleCreationType::Sphere; sharedShild1->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius}; sharedShild1->DeadZone = ShildRadius - 0.05f; sharedShild1->IsMagnet = true; sharedShild1->AlphaShowHide = true; sharedShild1->MagnetFactor = 2.5f; sharedShild1->Texture = GetPreloadedTextureAsset("gfx/flare1.tga"); sharedShild1->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f}; sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); } Shild2 = vw_CreateParticleSystem(); if (auto sharedShild2 = Shild2.lock()) { sharedShild2->ColorStart.r = 0.50f; sharedShild2->ColorStart.g = 0.50f; sharedShild2->ColorStart.b = 1.00f; sharedShild2->ColorEnd.r = 0.50f; sharedShild2->ColorEnd.g = 0.50f; sharedShild2->ColorEnd.b = 1.00f; sharedShild2->AlphaStart = 0.70f; sharedShild2->AlphaEnd = 0.10f; sharedShild2->SizeStart = 0.50f; sharedShild2->SizeVar = 0.10f; sharedShild2->SizeEnd = 0.30f; sharedShild2->Speed = 0.00f; sharedShild2->SpeedOnCreation = -1.00f; sharedShild2->Theta = 360.00f; sharedShild2->Life = 1.00f; sharedShild2->ParticlesPerSec = (int)(5 * ShildRadius); sharedShild2->CreationType = eParticleCreationType::Sphere; sharedShild2->CreationSize = sVECTOR3D{ShildRadius, 0.05f * ShildRadius, ShildRadius}; sharedShild2->DeadZone = ShildRadius - 0.05f; sharedShild2->IsMagnet = true; sharedShild2->MagnetFactor = 20.0f; sharedShild2->Texture = GetPreloadedTextureAsset("gfx/flare1.tga"); sharedShild2->Direction = sVECTOR3D{0.0f, 0.0f, -1.0f}; } ShildStartHitStatus = 150.0f; ShildEnergyStatus = 1.0f; } // предварительная установка, полностью заряженное устройство CurrentPlayerShipEnergy = GetShipMaxEnergy(GamePowerSystem); // предварительно иним состояния управления LastMouseX = -1; LastMouseY = -1; MoveFB = 0.0f; MoveLR = 0.0f; CurrentMovementVel = sVECTOR3D{0.0f, 0.0f, 0.0f}; // сброс стрельбы... PrimaryGroupCurrentFireWeaponNum = 1; PrimaryGroupCurrentFireWeaponDelay = 0.0f; SecondaryGroupCurrentFireWeaponNum = 1; SecondaryGroupCurrentFireWeaponDelay = 0.0f; } //------------------------------------------------------------------------------------ // Основная процедура обработки состояния корабля игрока //------------------------------------------------------------------------------------ void GamePlayerShip() { auto sharedPlayerFighter = PlayerFighter.lock(); if (!sharedPlayerFighter) return; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // проверяем, корабль живой еще, или сбили и нужно его удалить... //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (sharedPlayerFighter->ArmorCurrentStatus <= 0.0f) { // редкий случай if (UndeadDebugMode) { sharedPlayerFighter->ArmorCurrentStatus = sharedPlayerFighter->ArmorInitialStatus; } else { // делаем взрыв // + 10.0f движение камеры CreateSpaceExplosion(*sharedPlayerFighter, 31, sharedPlayerFighter->Location, sharedPlayerFighter->Speed+10.0f, PlayerDeadObjectPieceNum); // включаем музыку и отображение "миссия провалена" PlayMusicTheme(eMusicTheme::FAILED, 2000, 2000); // удаляем и уходим отсюда ReleaseSpaceShip(PlayerFighter); SetupMissionFailedText(20.0f); return; } } // голос выводим только в игре! в меню включается пауза // и если не закончился уровень if (GameContentTransp < 0.99f && !GameMissionCompleteStatus) { int WarningMessagesCount = 0; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Вывод голосового предупреждения, если навелась ракета //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bool CheckStatus{false}; ForEachProjectile([&] (const cProjectile &Projectile) { if (auto sharedTarget = Projectile.Target.lock()) { if ((sharedTarget.get() == sharedPlayerFighter.get()) && // homing missile targeted on this ship, but not homing mine ((Projectile.Num < 26) || (Projectile.Num > 29))) CheckStatus = true; } }); if (CheckStatus) { // проверяем, действительно еще играем (играем только 1 раз!) if (!vw_IsSoundAvailable(VoiceMissileDetected) && !VoiceMissileDetectedStatus) { VoiceMissileDetected = PlayVoicePhrase(eVoicePhrase::MissileDetected, 1.0f); VoiceMissileDetectedStatus = true; } // визуальный вывод - выводим постоянно vw_SetFontSize(24); int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Missile Detected"))) / 2; vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, CurrentAlert3, vw_GetTextUTF32("Missile Detected")); ResetFontSize(); WarningMessagesCount++; } else { if (CurrentAlert3 == 1.0f) { // сделали полный цикл , предыдущее значение счетчика было минимальное VoiceMissileDetectedStatus = false; } else if (VoiceMissileDetectedStatus) { // визуальный вывод - выводим постоянно vw_SetFontSize(24); int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Missile Detected"))) / 2; vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::orange}, CurrentAlert3, vw_GetTextUTF32("Missile Detected")); ResetFontSize(); WarningMessagesCount++; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Вывод голосового предупреждения если возможно столкновение //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bool CollisionDetected = false; ForEachSpaceObject([&CollisionDetected, &sharedPlayerFighter] (const cSpaceObject &tmpSpace, eSpaceCycle &Command) { // test with "immortal" big asteroids if ((tmpSpace.ObjectType == eObjectType::BigAsteroid) && (vw_SphereSphereCollision(sharedPlayerFighter->Radius, sharedPlayerFighter->Location, tmpSpace.Radius, tmpSpace.Location, tmpSpace.PrevLocation)) && (vw_SphereAABBCollision(tmpSpace.AABB, tmpSpace.Location, sharedPlayerFighter->Radius, sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation))) { CollisionDetected = true; Command = eSpaceCycle::Break; } }); ForEachGroundObject([&CollisionDetected, &sharedPlayerFighter] (const cGroundObject &tmpGround, eGroundCycle &Command) { // test with "immortal" civilian buildings if ((tmpGround.ObjectType == eObjectType::CivilianBuilding) && (vw_SphereSphereCollision(sharedPlayerFighter->Radius, sharedPlayerFighter->Location, tmpGround.Radius, tmpGround.Location, tmpGround.PrevLocation)) && (vw_SphereAABBCollision(tmpGround.AABB, tmpGround.Location, sharedPlayerFighter->Radius, sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation)) && (vw_SphereOBBCollision(tmpGround.OBB.Box, tmpGround.OBB.Location, tmpGround.Location, tmpGround.CurrentRotationMat, sharedPlayerFighter->Radius, sharedPlayerFighter->Location, sharedPlayerFighter->PrevLocation))) { CollisionDetected = true; Command = eGroundCycle::Break; } }); if (CollisionDetected) { // голос, ворнинг, можем столкнуться с объектом // проверяем, действительно еще играем if (!vw_IsSoundAvailable(VoiceWarningCollisionDetected)) VoiceWarningCollisionDetected = PlayVoicePhrase(eVoicePhrase::Warning, 1.0f); // визуальный вывод - выводим постоянно vw_SetFontSize(24); int TmpFontSize = (GameConfig().InternalWidth - vw_TextWidthUTF32(vw_GetTextUTF32("Collision Course Detected"))) / 2; vw_DrawTextUTF32(TmpFontSize, 720 - 40*WarningMessagesCount, 0, 0, 1.0f, sRGBCOLOR{eRGBCOLOR::red}, CurrentAlert3, vw_GetTextUTF32("Collision Course Detected")); ResetFontSize(); WarningMessagesCount++; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Вывод голосового предупреждения, если в оружие нет пуль //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (!sharedPlayerFighter->WeaponSlots.empty()) { // если вообще есть оружие for (const auto &tmpWeaponSlot : sharedPlayerFighter->WeaponSlots) { // если нажали стрелять, а патронов нет в одном из орудий if (auto sharedWeapon = tmpWeaponSlot.Weapon.lock()) { if (tmpWeaponSlot.SetFire && (sharedWeapon->Ammo <= 0) && !vw_IsSoundAvailable(VoiceWeaponMalfunction)) // проверяем, действительно еще играем VoiceWeaponMalfunction = PlayVoicePhrase(eVoicePhrase::WeaponMalfunction, 1.0f); } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Звуковое оповещение, если жизни менее 10% //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // если меньше 10% нужно бить тревогу if ((sharedPlayerFighter->ArmorCurrentStatus < sharedPlayerFighter->ArmorInitialStatus / 10.0f) && !vw_IsSoundAvailable(SoundLowLife)) // если не играем, запускаем звук сирены SoundLowLife = PlayMenuSFX(eMenuSFX::WarningLowLife, 1.0f); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // управление кораблем - движение //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (GameContentTransp < 1.0f) { // если не в меню нажимают // получаем данные, для обоих типов управления // уже получили данные, нужно игнорировать остальные источникик bool NeedSkip = false; // mouse + joystick (since we emulate mouse movements) if (GameConfig().MouseControl) { SDL_GetMouseState(&LastMouseXR, &LastMouseYR); int X, Y; vw_GetMousePos(X, Y); if (LastMouseX == -1 && LastMouseY == -1) { LastMouseX = X; LastMouseY = Y; } else { if (X != LastMouseX || Y != LastMouseY) { // 0.9+0.1 = 1.0 - минимум, всегда 1.0 должен быть! float Koef = 0.9f + GameConfig().ControlSensivity / 10.0f; // при любом реальном разрешении у нас x и y меняются с учетом AspectRatio float AWw2 = GameConfig().InternalWidth / 2.0f; float AHw2 = GameConfig().InternalHeight / 2.0f; MoveFB += (-(Y-LastMouseY)/AHw2)*Koef; MoveLR += ( (X-LastMouseX)/AWw2)*Koef; NeedSkip = true; } LastMouseX = X; LastMouseY = Y; } } // клавиатура if (!NeedSkip) { if (vw_GetKeyStatus(GameConfig().KeyBoardDown)) MoveFB -= 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta; if (vw_GetKeyStatus(GameConfig().KeyBoardUp)) MoveFB += 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta; if (vw_GetKeyStatus(GameConfig().KeyBoardLeft)) MoveLR -= 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta; if (vw_GetKeyStatus(GameConfig().KeyBoardRight)) MoveLR += 2.0f * (GameConfig().ControlSensivity / 10.0f) * sharedPlayerFighter->TimeDelta; } // дополнительная проверка, т.к. можем выйти выйти за пределы if (MoveFB < -1.0f) MoveFB = -1.0f; if (MoveFB > 1.0f) MoveFB = 1.0f; if (MoveLR < -1.0f) MoveLR = -1.0f; if (MoveLR > 1.0f) MoveLR = 1.0f; // находим конечную точку перемещения sVECTOR3D PlayerFighterEndLocation; if (GameConfig().InternalWidth == 1024) PlayerFighterEndLocation = sVECTOR3D{-(73.15f-sharedPlayerFighter->Width/2.0f+MoveFB*(20.05f-sharedPlayerFighter->Length/6.0f))*MoveLR, 0.0f, (46.0f-sharedPlayerFighter->Length/2.0f)*MoveFB}; else PlayerFighterEndLocation = sVECTOR3D{-(70.0f-sharedPlayerFighter->Width/2.0f+MoveFB*(23.2f-sharedPlayerFighter->Length/6.0f))*MoveLR, 0.0f, (46.0f-sharedPlayerFighter->Length/2.0f)*MoveFB}; PlayerFighterEndLocation += GetCameraCoveredDistance(); // если есть двигатель if (GameEngineSystem != 0) { // в зависимости от типа управления выполняем действия if (GameConfig().Profile[CurrentProfile].SpaceShipControlMode == 1) { // аркадный режим // запускаем маневровые двигатели, если тянем корабль в сторону if ((int)sharedPlayerFighter->Location.x > (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = true; PlayerFighterRightEng = false; } if ((int)sharedPlayerFighter->Location.x < (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = false; PlayerFighterRightEng = true; } // если не двигаем, останавливаем маневровые двигатели if ((int)sharedPlayerFighter->Location.x == (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = false; PlayerFighterRightEng = false; } // находим расстояние sVECTOR3D PlayerFighterNewDirection = PlayerFighterEndLocation - sharedPlayerFighter->Location; float EndLocationDistance = PlayerFighterNewDirection.Length(); // находим направление движения PlayerFighterNewDirection.Normalize(); float SimMoveSpeed = EndLocationDistance; if (SimMoveSpeed > 30.0f) SimMoveSpeed = 30.0f; SimMoveSpeed = SimMoveSpeed*4.0f*sharedPlayerFighter->TimeDelta; // получаем текущее движение CurrentMovementVel = PlayerFighterNewDirection^SimMoveSpeed; // проверка float MaxSpeed = CurrentMovementVel.Length(); CurrentMovementVel.Normalize(); if (MaxSpeed > 30.0f) MaxSpeed = 30.0f; CurrentMovementVel = CurrentMovementVel^MaxSpeed; } else { // симулятивный режим // запускаем маневровые двигатели, если тянем корабль в сторону if ((int)sharedPlayerFighter->Location.x > (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = true; PlayerFighterRightEng = false; } if ((int)sharedPlayerFighter->Location.x < (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = false; PlayerFighterRightEng = true; } // если не двигаем, останавливаем маневровые двигатели if ((int)sharedPlayerFighter->Location.x == (int)PlayerFighterEndLocation.x) { PlayerFighterLeftEng = false; PlayerFighterRightEng = false; } // находим расстояние sVECTOR3D PlayerFighterNewDirection = PlayerFighterEndLocation - sharedPlayerFighter->Location; float EndLocationDistance = PlayerFighterNewDirection.Length(); // находим направление движения PlayerFighterNewDirection.Normalize(); float SimMoveSpeed = EndLocationDistance; if (SimMoveSpeed > sharedPlayerFighter->MaxSpeed) SimMoveSpeed = sharedPlayerFighter->MaxSpeed; SimMoveSpeed = SimMoveSpeed*(sharedPlayerFighter->MaxAcceler/14.0f)*sharedPlayerFighter->TimeDelta; // получаем текущее движение CurrentMovementVel = PlayerFighterNewDirection^SimMoveSpeed; // проверка float MaxSpeed = CurrentMovementVel.Length(); CurrentMovementVel.Normalize(); if (MaxSpeed > sharedPlayerFighter->MaxSpeed) MaxSpeed = sharedPlayerFighter->MaxSpeed; CurrentMovementVel = CurrentMovementVel^MaxSpeed; } } // переносим корабль sVECTOR3D CurrentVel = sharedPlayerFighter->Location + CurrentMovementVel; CurrentVel.y = 0.0f; sharedPlayerFighter->SetLocationArcadePlayer(CurrentVel); // если стандартный аспект рейшен, надо перемещать камеру в дополнении к перемещению корабля if (GameConfig().InternalWidth == 1024) { float DeviationSize = 14.55f; if (sharedPlayerFighter->Location.x < 0.0f) { float Diff = sharedPlayerFighter->Location.x / 3.5f; if (Diff < -DeviationSize) Diff = -DeviationSize; sVECTOR3D TMPCameraLocation; vw_GetCameraLocation(&TMPCameraLocation); TMPCameraLocation.x = Diff; vw_SetCameraLocation(TMPCameraLocation); } else { float Diff = sharedPlayerFighter->Location.x / 3.5f; if (Diff > DeviationSize) Diff = DeviationSize; sVECTOR3D TMPCameraLocation; vw_GetCameraLocation(&TMPCameraLocation); TMPCameraLocation.x = Diff; vw_SetCameraLocation(TMPCameraLocation); } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // управление кораблем - стрельба //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (GameContentTransp < 0.5f) // если не в меню нажимают if (!sharedPlayerFighter->WeaponSlots.empty()) { // если вообще есть оружие int PrimCount = 0; float PrimTime = 0.0f; int SecCount = 0; float SecTime = 0.0f; PrimaryGroupCurrentFireWeaponDelay -= sharedPlayerFighter->TimeDelta; SecondaryGroupCurrentFireWeaponDelay -= sharedPlayerFighter->TimeDelta; // находим кол-во оружия в группах for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) { if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { // если это оружие установлено if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 1 || GameConfig().Profile[CurrentProfile].WeaponControl[i] == 3) { if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) { PrimCount++; PrimTime += sharedWeapon->NextFireTime; } } if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 2 || GameConfig().Profile[CurrentProfile].WeaponControl[i] == 3) { if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) { SecCount++; SecTime += sharedWeapon->NextFireTime; } } } } int PrimNum = 0; int SecNum = 0; for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) { if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { // если это оружие установлено sharedPlayerFighter->WeaponSlots[i].SetFire = false; // получаем данные, в какую группу относится bool primary_fire = false; bool secondary_fire = false; if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 1 || GameConfig().Profile[CurrentProfile].WeaponControl[i] ==3) primary_fire = true; if (GameConfig().Profile[CurrentProfile].WeaponControl[i] == 2 || GameConfig().Profile[CurrentProfile].WeaponControl[i] ==3) secondary_fire = true; // мышка if (GameConfig().MouseControl) { // primary fire if (primary_fire) if (vw_GetMouseButtonStatus(GameConfig().MousePrimary)) { if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { PrimNum++; if (PrimaryGroupCurrentFireWeaponNum == PrimNum && PrimaryGroupCurrentFireWeaponDelay <= 0.0f) { PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; PrimaryGroupCurrentFireWeaponNum++; if (PrimaryGroupCurrentFireWeaponNum > PrimCount) PrimaryGroupCurrentFireWeaponNum = 1; } } } // secondary fire if (secondary_fire) if (vw_GetMouseButtonStatus(GameConfig().MouseSecondary)) { if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { SecNum++; if (SecondaryGroupCurrentFireWeaponNum == SecNum && SecondaryGroupCurrentFireWeaponDelay <= 0.0f) { SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; SecondaryGroupCurrentFireWeaponNum++; if (SecondaryGroupCurrentFireWeaponNum > SecCount) SecondaryGroupCurrentFireWeaponNum = 1; } } } // альтернативное управление if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 2) if (vw_GetMouseButtonStatus(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i])) sharedPlayerFighter->WeaponSlots[i].SetFire = true; } // джойстик if (isJoystickAvailable()) { // primary fire if (primary_fire) if (GetJoystickButton(GameConfig().JoystickPrimary)) { if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { PrimNum++; if (PrimaryGroupCurrentFireWeaponNum == PrimNum && PrimaryGroupCurrentFireWeaponDelay <= 0.0f) { PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; PrimaryGroupCurrentFireWeaponNum++; if (PrimaryGroupCurrentFireWeaponNum > PrimCount) PrimaryGroupCurrentFireWeaponNum = 1; } } } // secondary fire if (secondary_fire) if (GetJoystickButton(GameConfig().JoystickSecondary)) { if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { SecNum++; if (SecondaryGroupCurrentFireWeaponNum == SecNum && SecondaryGroupCurrentFireWeaponDelay <= 0.0f) { SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; SecondaryGroupCurrentFireWeaponNum++; if (SecondaryGroupCurrentFireWeaponNum > SecCount) SecondaryGroupCurrentFireWeaponNum = 1; } } } // альтернативное управление if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 3) if (GetJoystickButton(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i])) sharedPlayerFighter->WeaponSlots[i].SetFire = true; } // клавиатура // primary fire if (primary_fire) if (vw_GetKeyStatus(GameConfig().KeyBoardPrimary)) { if (GameConfig().Profile[CurrentProfile].PrimaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { PrimNum++; if (PrimaryGroupCurrentFireWeaponNum == PrimNum && PrimaryGroupCurrentFireWeaponDelay <= 0.0f) { PrimaryGroupCurrentFireWeaponDelay = PrimTime / (PrimCount * PrimCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; PrimaryGroupCurrentFireWeaponNum++; if (PrimaryGroupCurrentFireWeaponNum > PrimCount) PrimaryGroupCurrentFireWeaponNum = 1; } } } // secondary fire if (secondary_fire) if (vw_GetKeyStatus(GameConfig().KeyBoardSecondary)) { if (GameConfig().Profile[CurrentProfile].SecondaryWeaponFireMode == 1) { sharedPlayerFighter->WeaponSlots[i].SetFire = true; } else { SecNum++; if (SecondaryGroupCurrentFireWeaponNum == SecNum && SecondaryGroupCurrentFireWeaponDelay <= 0.0f) { SecondaryGroupCurrentFireWeaponDelay = SecTime / (SecCount * SecCount); sharedPlayerFighter->WeaponSlots[i].SetFire = true; SecondaryGroupCurrentFireWeaponNum++; if (SecondaryGroupCurrentFireWeaponNum > SecCount) SecondaryGroupCurrentFireWeaponNum = 1; } } } // альтернативное управление if (GameConfig().Profile[CurrentProfile].WeaponAltControl[i] == 1) if (vw_GetKeyStatus(GameConfig().Profile[CurrentProfile].WeaponAltControlData[i])) sharedPlayerFighter->WeaponSlots[i].SetFire = true; } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // управление и работа внутренних систем корабля //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // сделать: // учитывать, как работает двигатель... стоим или летим... // если не аркадный режим... if (GameSpaceShipControlMode != 1) { if (CurrentPlayerShipEnergy < GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta) { sharedPlayerFighter->MaxSpeed = 0.0f; sharedPlayerFighter->MaxAcceler = 0.0f; sharedPlayerFighter->MaxSpeedRotate = 0.0f; // глушим двигатели for (auto &tmpEngine : sharedPlayerFighter->Engines) { if (auto sharedEngine = tmpEngine.lock()) sharedEngine->IsSuppressed = true; } if (!sharedPlayerFighter->EnginesLeft.empty()) { for (auto &tmpEngineLeft : sharedPlayerFighter->EnginesLeft) { if (auto sharedEngineLeft = tmpEngineLeft.lock()) sharedEngineLeft->IsSuppressed = true; } } if (!sharedPlayerFighter->EnginesRight.empty()) { for (auto &tmpEngineRight : sharedPlayerFighter->EnginesRight) { if (auto sharedEngineRight = tmpEngineRight.lock()) sharedEngineRight->IsSuppressed = true; } } } else { sharedPlayerFighter->MaxSpeed = GetEnginePower(GameEngineSystem); sharedPlayerFighter->MaxAcceler = GetEngineAcceleration(GameEngineSystem); sharedPlayerFighter->MaxSpeedRotate = GetEngineRotatePower(GameEngineSystem); // запускаем прорисовку for (auto &tmpEngine : sharedPlayerFighter->Engines) { if (auto sharedEngine = tmpEngine.lock()) sharedEngine->IsSuppressed = false; } if (!sharedPlayerFighter->EnginesLeft.empty()) { for (auto &tmpEngineLeft : sharedPlayerFighter->EnginesLeft) { if (auto sharedEngineLeft = tmpEngineLeft.lock()) sharedEngineLeft->IsSuppressed = false; } } if (!sharedPlayerFighter->EnginesRight.empty()) { for (auto &tmpEngineRight : sharedPlayerFighter->EnginesRight) { if (auto sharedEngineRight = tmpEngineRight.lock()) sharedEngineRight->IsSuppressed = false; } } CurrentPlayerShipEnergy -= GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // энергия для перезарядки и выстрела... //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // сейчас получаем всю энергию для перезарядки и выстрела // потом лучше будет переделать на постепенный отбор энергии for (unsigned i = 0; i < sharedPlayerFighter->WeaponSlots.size(); i++) { if (GameConfig().Profile[CurrentProfile].Weapon[i] != 0) { if (auto sharedWeapon = sharedPlayerFighter->WeaponSlots[i].Weapon.lock()) { if (sharedWeapon->CurrentEnergyAccumulated < sharedWeapon->EnergyUse) { // если энергии не достаточно для зарядки орудия if (CurrentPlayerShipEnergy < sharedWeapon->EnergyUse) { // останавливаем перезарядку оружия sharedWeapon->LastFireTime += sharedPlayerFighter->TimeDelta; if (auto sharedFire = sharedWeapon->Fire.lock()) sharedFire->IsSuppressed = true; } else { // если энергии достаточно, все нормально берем ее и перезаряжаем оружие sharedWeapon->CurrentEnergyAccumulated = sharedWeapon->EnergyUse; CurrentPlayerShipEnergy -= sharedWeapon->EnergyUse; } } } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // питание других (защитных) систем //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (CurrentPlayerShipEnergy >= GetShipEngineSystemEnergyUse(GameEngineSystem)*sharedPlayerFighter->TimeDelta) { switch (GameAdvancedProtectionSystem) { // нано роботы case 1: // восстанавливаем на 0.5% в секунду if (sharedPlayerFighter->ArmorCurrentStatus < sharedPlayerFighter->ArmorInitialStatus) { CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta; sharedPlayerFighter->ArmorCurrentStatus += (sharedPlayerFighter->ArmorInitialStatus / 200.0f) * sharedPlayerFighter->TimeDelta; if (sharedPlayerFighter->ArmorCurrentStatus > sharedPlayerFighter->ArmorInitialStatus) sharedPlayerFighter->ArmorCurrentStatus = sharedPlayerFighter->ArmorInitialStatus; } break; // спец защитный слой case 2: break; // ничего не делаем // щит case 3: // восстанавливаем полностью за 4 секунды if (ShildEnergyStatus < 1.0f) { CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta; ShildEnergyStatus += 0.02f * sharedPlayerFighter->TimeDelta; if (ShildEnergyStatus > 1.0f) ShildEnergyStatus = 1.0f; } break; // отражатель case 4: // восстанавливаем полностью за 2 секунды if (ShildEnergyStatus < 1.0f) { CurrentPlayerShipEnergy -= GetShipProtectionSystemEnergyUse(GameAdvancedProtectionSystem) * sharedPlayerFighter->TimeDelta; ShildEnergyStatus += 0.03f * sharedPlayerFighter->TimeDelta; if (ShildEnergyStatus > 1.0f) ShildEnergyStatus = 1.0f; } break; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // управление визуализацией щитов-дефлекторов //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if (auto sharedShild1 = Shild1.lock()) { sharedShild1->MoveSystem(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); sharedShild1->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); sharedShild1->RotateSystemAndParticlesByAngle(sharedPlayerFighter->Rotation); sharedShild1->ParticlesPerSec = (int)(40 * ShildEnergyStatus * ShildRadius); } if (auto sharedShild2 = Shild2.lock()) { sharedShild2->MoveSystem(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); sharedShild2->SetStartLocation(sharedPlayerFighter->Location + sharedPlayerFighter->OBB.Location); sharedShild2->RotateSystemAndParticlesByAngle(sharedPlayerFighter->Rotation); sharedShild2->ParticlesPerSec = (int)(5 * ShildEnergyStatus * ShildRadius); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // если реактор - можем генерировать энергию, если баттарея - нет //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CurrentPlayerShipEnergy += GetShipRechargeEnergy(GamePowerSystem)*sharedPlayerFighter->TimeDelta; if (CurrentPlayerShipEnergy > GetShipMaxEnergy(GamePowerSystem)) CurrentPlayerShipEnergy = GetShipMaxEnergy(GamePowerSystem); } } // astromenace namespace } // viewizard namespace