/////////////////////////////////////////////////////// //*-------------------------------------------------*// //| Part of Project One (https://www.maus-games.at) |// //*-------------------------------------------------*// //| Copyright (c) 2010 Martin Mauersics |// //| Released under the zlib License |// //*-------------------------------------------------*// /////////////////////////////////////////////////////// #include "main.h" // **************************************************************** // setup the Calor mission void cCalorMission::__SetupOwn() { // ################################################################ // STAGE_MAIN({TAKE_ALWAYS}) { if(HAS_FLAG(g_pGame->GetStatus(), GAME_STATUS_QUICK)) { STAGE_FINISH_NOW } else { STAGE_FINISH_AFTER(MISSION_WAIT_INTRO) } }); // ################################################################ // start STAGE_MAIN({TAKE_ALWAYS}) { g_pEnvironment->ChangeBackground(cSnowBackground::ID, ENVIRONMENT_MIX_CURTAIN, 1.0f, coreVector2(1.0f,0.0f)); g_pEnvironment->SetTargetDirectionNow(ENVIRONMENT_DEFAULT_DIRECTION); g_pEnvironment->SetTargetSideNow (ENVIRONMENT_DEFAULT_SIDE); g_pEnvironment->SetTargetSpeedNow (6.0f); g_pGame->StartIntro(); STAGE_FINISH_NOW }); // ################################################################ // change background appearance STAGE_MAIN({TAKE_ALWAYS, 0u, 1u}) { cSnowBackground* pBackground = d_cast<cSnowBackground*>(g_pEnvironment->GetBackground()); pBackground->GetOutdoor()->LerpHeightNow(-0.5f, -34.0f); pBackground->SetGroundDensity(0u, 1.0f); pBackground->SetGroundDensity(1u, 0.0f); pBackground->SetGroundDensity(2u, 0.0f); pBackground->SetAirDensity (1u, 1.0f); STAGE_FINISH_NOW }); // ################################################################ // show mission name STAGE_MAIN({TAKE_ALWAYS, 0u}) { if(HAS_FLAG(g_pGame->GetStatus(), GAME_STATUS_NAMELESS)) { STAGE_FINISH_NOW } else { if(STAGE_BEGINNING) { g_pGame->GetInterface()->ShowMission(this); } STAGE_FINISH_AFTER(MISSION_WAIT_PLAY) } }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 0u}) { STAGE_FINISH_PLAY }); // ################################################################ // evade being attacked // - 2. group shows single-enemy split-up, extended in 3. group // - first groups are positioned a bit from center, to give player a safe space to navigate among unpredictable enemy-evasion movements // - evasion needs visual feedback, especially on multi-jumper // - directly evading enemies should evade diagonal, so evasion is valid from any attack-direction // - don't let enemies shoot while still at original position, otherwise will merge bullets especially on 3. group // - multi-jumper should not start at the cross from last position of path-enemy // - multi-jumper should not move together again after splitting up // - next groups need to spawn (source, target) where player would not attack from/into the previous group // - the clock group has the same angular speed to make it easier to control // - (old: in star group, everyone has their own path, only goes with an odd number of enemies) // - enemies move smooth, flakes move flat // TASK: collect all moving flakes // TASK: intercept every move by the jumping enemies // ACHIEVEMENT: destroy all enemies while your partner needs repair // TODO 1: hardmode: enemies return after some time // TODO 1: hardmode: enemy splits up, the original location attacks, the other keeps moving // TODO 1: implement own task-objects instead of re-using hails, also adjust count again (CALOR_HAILS) (name: flake?) STAGE_MAIN({TAKE_ALWAYS, 0u}) { constexpr coreUintW iNumData = 16u; constexpr coreUintW iMaxTrail = 12u; STAGE_ADD_PATH(pPath1) { pPath1->Reserve(21u); pPath1->AddNode(coreVector2( 0.0f, 0.0f), coreVector2( 1.0f, 1.0f).Normalized()); pPath1->AddNode(coreVector2( 0.4f, 0.4f), coreVector2( 0.0f, 1.0f)); pPath1->AddNode(coreVector2( 0.1f, 0.8f), coreVector2(-1.0f, 0.0f)); pPath1->AddNode(coreVector2(-0.3f, 0.4f), coreVector2( 0.0f,-1.0f)); pPath1->AddNode(coreVector2(-0.6f, 0.1f), coreVector2(-1.0f, 0.0f)); pPath1->AddNode(coreVector2(-0.9f, 0.4f), coreVector2( 0.0f, 1.0f)); pPath1->AddNode(coreVector2(-0.6f, 0.7f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2(-0.3f, 0.4f), coreVector2( 0.0f,-1.0f)); pPath1->AddNode(coreVector2(-0.7f,-0.6f), coreVector2( 0.0f,-1.0f)); pPath1->AddNode(coreVector2(-0.5f,-0.8f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.0f,-0.5f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.5f,-0.8f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.7f,-0.3f), coreVector2( 0.0f, 1.0f)); pPath1->AddNode(coreVector2( 0.4f, 0.0f), coreVector2(-1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.1f,-0.3f), coreVector2( 0.0f,-1.0f)); pPath1->AddNode(coreVector2( 0.4f,-0.6f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.7f,-0.3f), coreVector2( 0.0f, 1.0f)); pPath1->AddNode(coreVector2( 0.2f, 0.5f), coreVector2( 0.0f, 1.0f)); pPath1->AddNode(coreVector2( 0.5f, 0.8f), coreVector2( 1.0f, 0.0f)); pPath1->AddNode(coreVector2( 0.8f, 0.5f), coreVector2( 0.0f,-1.0f)); pPath1->AddNode(coreVector2( 0.0f, 0.0f), coreVector2(-1.0f,-1.0f).Normalized()); pPath1->Refine(); pPath1->Refine(); // double }); STAGE_ADD_PATH(pPath2) { pPath2->Reserve(3u); pPath2->AddNode(coreVector2(-0.8f,-1.3f), coreVector2(0.0f, 1.0f)); pPath2->AddNode(coreVector2( 0.0f, 0.8f), coreVector2(1.0f, 0.0f)); pPath2->AddNode(coreVector2( 0.8f,-1.3f), coreVector2(0.0f,-1.0f)); pPath2->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cStarEnemy, 81u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.7f); pEnemy->Configure(4, COLOR_SHIP_GREY); pEnemy->AddStatus(ENEMY_STATUS_GHOST); }); }); STAGE_GET_START(iNumData * 3u + 8u) STAGE_GET_FLOAT_ARRAY(afRecover, iNumData) STAGE_GET_FLOAT_ARRAY(afRecoverOld, iNumData) STAGE_GET_UINT_ARRAY (aiRecoverHit, iNumData) STAGE_GET_FLOAT (fShootTime) STAGE_GET_UINT (iTrail) STAGE_GET_UINT (iTrailOld) STAGE_GET_UINT (iHelperState) STAGE_GET_UINT (iIntercept) STAGE_GET_UINT (iUnrepaired) STAGE_GET_UINT (iHailState) STAGE_GET_UINT (iHailEffect) STAGE_GET_END ASSERT(pSquad1->GetNumEnemiesAlive() <= iNumData) if(STAGE_CLEARED) { if(STAGE_SUB( 1u)) STAGE_RESURRECT(pSquad1, 0u, 7u) else if(STAGE_SUB( 2u)) STAGE_RESURRECT(pSquad1, 8u, 15u) else if(STAGE_SUB( 3u)) STAGE_RESURRECT(pSquad1, 16u, 31u) else if(STAGE_SUB( 4u)) STAGE_RESURRECT(pSquad1, 32u, 43u) else if(STAGE_SUB( 5u)) STAGE_RESURRECT(pSquad1, 44u, 55u) else if(STAGE_SUB( 7u)) STAGE_RESURRECT(pSquad1, 56u, 64u) // skip 6 else if(STAGE_SUB( 8u)) STAGE_RESURRECT(pSquad1, 65u, 70u) else if(STAGE_SUB( 9u)) STAGE_RESURRECT(pSquad1, 71u, 76u) else if(STAGE_SUB(10u)) STAGE_RESURRECT(pSquad1, 77u, 80u) else { if(!iUnrepaired) STAGE_BADGE(3u, BADGE_ACHIEVEMENT, g_pGame->FindPlayerDual(0u)->GetPosition()) } if(g_pGame->IsTask()) { switch(m_iStageSub) { case 5u: this->EnableHail(0u); break; case 7u: this->EnableHail(1u); break; case 8u: this->EnableHail(2u); break; case 9u: this->EnableHail(3u); this->EnableHail(4u); break; } } std::memset(afRecover, 0, sizeof(coreFloat) * iNumData); std::memset(afRecoverOld, 0, sizeof(coreFloat) * iNumData); std::memset(aiRecoverHit, 0, sizeof(coreUint32) * iNumData); fShootTime = 0.0f; } const coreFloat fOldShootTime = fShootTime; if(std::any_of(afRecover, afRecover + iNumData, [](const coreFloat A) {return (A != 0.0f);})) { fShootTime += 1.0f * TIME; } STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { STAGE_LIFETIME(pEnemy, 1.0f, 0.0f) coreBool bActive = false; coreFloat& fRecover = STAGE_SINK_FLOAT(afRecover [i % iNumData]); coreUint32& iRecoverHit = STAGE_SINK_UINT (aiRecoverHit[i % iNumData]); coreVector2 vStart, vLocation, vEvade; if(i < 44u) { switch(i) { default: UNREACHABLE case 0u: vStart = coreVector2(-1.3f, 0.2f); vLocation = coreVector2(-0.4f, 0.3f) + coreVector2( 0.1f, 0.1f); vEvade = coreVector2( 0.2f, 0.2f); break; case 1u: vStart = coreVector2(-1.3f, 0.2f); vLocation = coreVector2(-0.4f, 0.3f) + coreVector2( 0.1f,-0.1f); vEvade = coreVector2( 0.2f,-0.2f); break; case 2u: vStart = coreVector2(-1.3f, 0.2f); vLocation = coreVector2(-0.4f, 0.3f) + coreVector2(-0.1f, 0.1f); vEvade = coreVector2(-0.2f, 0.2f); break; case 3u: vStart = coreVector2(-1.3f, 0.2f); vLocation = coreVector2(-0.4f, 0.3f) + coreVector2(-0.1f,-0.1f); vEvade = coreVector2(-0.2f,-0.2f); break; case 4u: vStart = coreVector2( 1.3f, 0.2f); vLocation = coreVector2( 0.4f, 0.3f) + coreVector2( 0.1f, 0.1f); vEvade = coreVector2( 0.2f, 0.2f); break; case 5u: vStart = coreVector2( 1.3f, 0.2f); vLocation = coreVector2( 0.4f, 0.3f) + coreVector2( 0.1f,-0.1f); vEvade = coreVector2( 0.2f,-0.2f); break; case 6u: vStart = coreVector2( 1.3f, 0.2f); vLocation = coreVector2( 0.4f, 0.3f) + coreVector2(-0.1f, 0.1f); vEvade = coreVector2(-0.2f, 0.2f); break; case 7u: vStart = coreVector2( 1.3f, 0.2f); vLocation = coreVector2( 0.4f, 0.3f) + coreVector2(-0.1f,-0.1f); vEvade = coreVector2(-0.2f,-0.2f); break; case 8u: vStart = coreVector2(-1.3f,-0.8f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2(-0.3f,-0.3f); vEvade = coreVector2(-0.2f, 0.2f); break; case 9u: vStart = coreVector2(-1.3f,-0.8f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2(-0.3f,-0.3f); vEvade = coreVector2( 0.2f,-0.2f); break; case 10u: vStart = coreVector2(-1.3f, 1.3f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2(-0.3f, 0.3f); vEvade = coreVector2(-0.2f,-0.2f); break; case 11u: vStart = coreVector2(-1.3f, 1.3f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2(-0.3f, 0.3f); vEvade = coreVector2( 0.2f, 0.2f); break; case 12u: vStart = coreVector2( 1.3f,-0.8f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2( 0.3f,-0.3f); vEvade = coreVector2(-0.2f,-0.2f); break; case 13u: vStart = coreVector2( 1.3f,-0.8f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2( 0.3f,-0.3f); vEvade = coreVector2( 0.2f, 0.2f); break; case 14u: vStart = coreVector2( 1.3f, 1.3f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2( 0.3f, 0.3f); vEvade = coreVector2(-0.2f, 0.2f); break; case 15u: vStart = coreVector2( 1.3f, 1.3f); vLocation = coreVector2( 0.0f, 0.3f) + coreVector2( 0.3f, 0.3f); vEvade = coreVector2( 0.2f,-0.2f); break; case 16u: vStart = coreVector2(-1.9f, 0.0f); vLocation = coreVector2(-0.9f, 0.0f); vEvade = coreVector2( 0.3f, 0.0f); break; case 17u: vStart = coreVector2(-1.9f, 0.0f); vLocation = coreVector2(-0.9f, 0.0f); vEvade = coreVector2( 0.6f, 0.0f); break; case 18u: vStart = coreVector2(-1.9f, 0.0f); vLocation = coreVector2(-0.9f, 0.0f); vEvade = coreVector2( 0.9f, 0.0f); break; case 19u: vStart = coreVector2(-1.9f, 0.0f); vLocation = coreVector2(-0.9f, 0.0f); vEvade = coreVector2( 1.2f, 0.0f); break; case 20u: vStart = coreVector2( 1.3f, 0.3f); vLocation = coreVector2( 0.3f, 0.3f); vEvade = coreVector2(-0.3f, 0.0f); break; case 21u: vStart = coreVector2( 1.3f, 0.3f); vLocation = coreVector2( 0.3f, 0.3f); vEvade = coreVector2(-0.6f, 0.0f); break; case 22u: vStart = coreVector2( 1.3f, 0.3f); vLocation = coreVector2( 0.3f, 0.3f); vEvade = coreVector2(-0.9f, 0.0f); break; case 23u: vStart = coreVector2( 1.3f, 0.3f); vLocation = coreVector2( 0.3f, 0.3f); vEvade = coreVector2(-1.2f, 0.0f); break; case 24u: vStart = coreVector2(-1.3f, 0.6f); vLocation = coreVector2(-0.3f, 0.6f); vEvade = coreVector2( 0.3f, 0.0f); break; case 25u: vStart = coreVector2(-1.3f, 0.6f); vLocation = coreVector2(-0.3f, 0.6f); vEvade = coreVector2( 0.6f, 0.0f); break; case 26u: vStart = coreVector2(-1.3f, 0.6f); vLocation = coreVector2(-0.3f, 0.6f); vEvade = coreVector2( 0.9f, 0.0f); break; case 27u: vStart = coreVector2(-1.3f, 0.6f); vLocation = coreVector2(-0.3f, 0.6f); vEvade = coreVector2( 1.2f, 0.0f); break; case 28u: vStart = coreVector2( 1.9f, 0.9f); vLocation = coreVector2( 0.9f, 0.9f); vEvade = coreVector2(-0.3f, 0.0f); break; case 29u: vStart = coreVector2( 1.9f, 0.9f); vLocation = coreVector2( 0.9f, 0.9f); vEvade = coreVector2(-0.6f, 0.0f); break; case 30u: vStart = coreVector2( 1.9f, 0.9f); vLocation = coreVector2( 0.9f, 0.9f); vEvade = coreVector2(-0.9f, 0.0f); break; case 31u: vStart = coreVector2( 1.9f, 0.9f); vLocation = coreVector2( 0.9f, 0.9f); vEvade = coreVector2(-1.2f, 0.0f); break; case 32u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.3f, 0.3f); break; case 33u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.4f, 0.0f); break; case 34u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.3f,-0.3f); break; case 35u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.3f, 0.3f); break; case 36u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.4f, 0.0f); break; case 37u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.3f,-0.3f); break; case 38u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.8f, 0.3f); break; case 39u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.9f, 0.0f); break; case 40u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2(-0.8f,-0.3f); break; case 41u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.8f, 0.3f); break; case 42u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.9f, 0.0f); break; case 43u: vStart = coreVector2( 0.0f, 1.3f); vLocation = coreVector2( 0.0f, 0.6f); vEvade = coreVector2( 0.8f,-0.3f); break; } } else if(i < 77u) { coreFloat fFactor = 0.0f; if(i < 56u) { const coreFloat fSide = (i < 50u) ? -1.0f : 1.0f; const coreFloat fHeight = (I_TO_F((i - 44u) % 6u) - 1.5f) * 0.2f; const coreVector2 vFrom = coreVector2(((i % 2u) ? 0.85f : 0.95f) * fSide, fHeight); const coreVector2 vTo = coreVector2(((i % 2u) ? -0.95f : -0.85f) * fSide, fHeight); fFactor = MIN1(fRecover * RCP((vFrom - vTo).Length())); vStart = coreVector2(1.3f * fSide, fHeight); vLocation = LERP(vFrom, vTo, fFactor); } else if(i < 65u) { const coreVector2 vDir = coreVector2::Direction(I_TO_F(i - 56u) * (1.0f/9.0f) * (2.0f*PI)); fFactor = MIN1(fRecover * 0.15f); vStart = vDir * (1.3f * SQRT2); vLocation = MapToAxis(vDir * LERP(0.3f, 0.6f, fFactor), coreVector2::Direction(LERP(0.0f*PI, 4.0f*PI, fFactor))); if((i == 56u) && !pEnemy->HasStatus(ENEMY_STATUS_DEAD)) g_pEnvironment->SetTargetDirection(coreVector2::Direction(LERP(0.0f*PI, 1.0f*PI, fFactor)), 1.0f); } else if(i < 71u) { const coreFloat fHeight = (I_TO_F(i - 65u) + 0.5f) * 0.2f; fFactor = MIN1(fRecover * 0.2f); vStart = coreVector2(1.3f,1.3f); vLocation = MapToAxis(coreVector2(0.0f, fHeight), coreVector2::Direction(LERP(-1.25f*PI, -4.0f*PI, fFactor))); if((i == 65u) && !pEnemy->HasStatus(ENEMY_STATUS_DEAD)) g_pEnvironment->SetTargetDirection(coreVector2::Direction(LERP(1.0f*PI, 2.0f*PI, fFactor)), 1.0f); } else { fFactor = CLAMP01((fRecover - I_TO_F(i - 71u) * 0.2f) * RCP(pPath1->GetTotalDistance())); vStart = coreVector2(-1.3f,0.0f); vLocation = pPath1->CalcPositionLerp(fFactor); if((i == 71u) && !pEnemy->HasStatus(ENEMY_STATUS_DEAD)) g_pEnvironment->SetTargetDirection(coreVector2::Direction(LERP(0.0f*PI, 2.0f*PI, fFactor)), 1.0f); } if(fFactor >= 1.0f) bActive = true; vEvade = coreVector2(0.0f,0.0f); } else { if(iTrailOld == 0u) vLocation = coreVector2(-0.5f,0.9f); else if(iTrailOld >= iMaxTrail) vLocation = coreVector2( 0.0f,0.0f); else { if((i == 78u) && (iTrailOld >= 6u)) vLocation = coreVector2::Direction(I_TO_F(iTrailOld) * (3.0f/7.0f) * (-2.0f*PI) + 1.5f * 0.3f) * 0.9f; else if((i == 80u) && (iTrailOld >= 6u)) vLocation = coreVector2::Direction(I_TO_F(iTrailOld) * (3.0f/7.0f) * (-2.0f*PI) - 1.5f * 0.3f) * 0.9f; else if((i == 79u || i == 80u) && (iTrailOld >= 3u)) vLocation = coreVector2::Direction(I_TO_F(iTrailOld) * (3.0f/7.0f) * (-2.0f*PI) + 0.5f * 0.3f) * 0.9f; else vLocation = coreVector2::Direction(I_TO_F(iTrailOld) * (3.0f/7.0f) * (-2.0f*PI) - 0.5f * 0.3f) * 0.9f; } vStart = coreVector2(-0.5f,1.3f); vEvade = coreVector2( 0.0f,0.0f); } if(STAGE_TAKEOFF) { pEnemy->SetPosition(coreVector3(vStart * FOREGROUND_AREA, 0.0f)); } iRecoverHit = 0u; if(STAGE_LIFETIME_AFTER(0.5f)) { g_pGame->GetBulletManagerPlayer()->ForEachBullet([&](const cBullet* pBullet) { const coreVector2 vDiff1 = pEnemy->GetPosition().xy() - (pBullet->GetPosition().xy() + pBullet->GetFlyMove()); const coreVector2 vDiff2 = pEnemy->GetPosition().xy() - (pBullet->GetPosition().xy()); const coreVector2 vDiff3 = pEnemy->GetPosition().xy() - (pBullet->GetPosition().xy() - pBullet->GetFlyMove()); if((vDiff1.LengthSq() < POW2(10.0f)) || (vDiff2.LengthSq() < POW2(10.0f)) || (vDiff3.LengthSq() < POW2(10.0f))) { cPlayer* pPlayer = d_cast<cPlayer*>(pBullet->GetOwner()); ADD_BIT(iRecoverHit, g_pGame->GetPlayerIndex(pPlayer)) if(pEnemy->HasStatus(ENEMY_STATUS_GHOST)) pPlayer->GetScoreTable()->RefreshCooldown(); } }); } const coreFloat fDistSq = (pEnemy->GetPosition().xy() - vLocation * FOREGROUND_AREA).LengthSq(); if(i < 44u) { bActive = (fDistSq > (vEvade * FOREGROUND_AREA).LengthSq() - POW2(10.0f)); if(iRecoverHit) { fRecover = 1.0f; } if(fRecover && (fDistSq > POW2((m_iStageSub >= 3u) ? 20.0f : 4.0f))) { if(STAGE_TICK_EXTERN(fShootTime, fOldShootTime, 1.7f, 0.0f) && (!g_pGame->IsEasy() || ((s_iTick % 3u) < 1u))) { const coreVector2 vPos = pEnemy->GetPosition().xy(); const coreVector2 vDir = pEnemy->AimAtPlayerDual(s_iTick % 2u).Normalized(); g_pGame->GetBulletManagerEnemy()->AddBullet<cWaveBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } } } else if(i < 77u) { if(fRecover) { if(STAGE_TICK_EXTERN(fShootTime, fOldShootTime, 1.7f, 0.0f) && (!g_pGame->IsEasy() || ((s_iTick % 3u) < 1u))) { const coreVector2 vPos = pEnemy->GetPosition().xy(); const coreVector2 vDir = pEnemy->AimAtPlayerDual(s_iTick % 2u).Normalized(); g_pGame->GetBulletManagerEnemy()->AddBullet<cWaveBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } } } else { bActive = (iTrail >= iMaxTrail) && (fDistSq < POW2(2.0f)); if(iRecoverHit && !bActive && (fDistSq < POW2(2.0f)) && (iTrail == iTrailOld)) { iTrail += 1u; } } if(STAGE_LIFETIME_AFTER(0.5f) && bActive) { if(pEnemy->HasStatus(ENEMY_STATUS_GHOST)) { pEnemy->SetBaseColor(COLOR_SHIP_ORANGE); pEnemy->RemoveStatus(ENEMY_STATUS_GHOST_BULLET); } } else { if(!pEnemy->HasStatus(ENEMY_STATUS_GHOST)) { pEnemy->SetBaseColor(COLOR_SHIP_GREY); pEnemy->AddStatus (ENEMY_STATUS_GHOST); } } const coreVector2 vTarget = fRecover ? (vLocation + vEvade) : vLocation; pEnemy->DefaultMoveSmooth(vTarget, 120.0f, 12.0f); if(g_pGame->IsTask() && (i >= 77u)) { if(iTrail && !HAS_BIT(iIntercept, iTrail) && (fDistSq >= POW2(10.0f))) { STAGE_FOREACH_PLAYER(pPlayer, j) { const coreVector2 vDiff = pEnemy->GetPosition().xy() - pPlayer->GetPosition().xy(); if(vDiff.LengthSq() < POW2(9.0f)) { ADD_BIT(iIntercept, iTrail) const coreUint32 iCount = coreMath::PopCount(iIntercept); if(iCount >= iMaxTrail) { STAGE_BADGE(1u, BADGE_NORMAL, pEnemy->GetPosition()) } else { g_pGame->GetCombatText()->DrawCountdown(coreMath::PopCount(iIntercept), iMaxTrail, pEnemy->GetPosition()); g_pSpecialEffects->PlaySound(pEnemy->GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iCount, iMaxTrail), SOUND_ITEM_01); } g_pSpecialEffects->CreateSplashColor(pEnemy->GetPosition(), SPECIAL_SPLASH_TINY, COLOR_ENERGY_YELLOW); } }); } } if(pEnemy->ReachedDeath()) { if(!g_pGame->GetRepairEnemy()->GetPlayer()) { iUnrepaired += 1u; STAGE_FAILTROPHY } } }); if((m_iStageSub >= 5u) && (m_iStageSub < 10u)) { coreUintW iSplit, iOffset; if(m_iStageSub == 5u) { iSplit = 2u; iOffset = 2u; } else { iSplit = 1u; iOffset = 0u; } const coreUintW iStep = iNumData / iSplit; for(coreUintW i = 0u; i < iSplit; ++i) { const coreUintW iIndex = i * iStep + iOffset; coreFloat fMax = 0.0f; for(coreUintW j = 0u; j < iStep; ++j) fMax = MAX(fMax, afRecover[(iIndex + j) % iNumData]); coreUint32 iAll = 0u; for(coreUintW j = 0u; j < iStep; ++j) iAll |= aiRecoverHit[(iIndex + j) % iNumData]; fMax += 1.0f * TIME * I_TO_F(coreMath::PopCount(iAll)) * RCP(I_TO_F(g_pGame->GetNumPlayers())); for(coreUintW j = 0u; j < iStep; ++j) afRecover[(iIndex + j) % iNumData] = fMax; } } STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { STAGE_LIFETIME(pEnemy, 1.0f, 0.0f) coreFloat& fRecover = STAGE_SINK_FLOAT(afRecover [i % iNumData]); coreFloat& fRecoverOld = STAGE_SINK_FLOAT(afRecoverOld[i % iNumData]); if(InBetween(0.0f, fRecoverOld, fRecover)) { g_pSpecialEffects->CreateSplashColor(pEnemy->GetPosition(), 10.0f, 3u, COLOR_ENERGY_GREEN); } if(iTrailOld != iTrail) { const coreVector2 vPos = pEnemy->GetPosition().xy(); for(coreUintW j = 40u; j--; ) { if(((j + (i - 77u) * 5u) % 20u) < (g_pGame->IsEasy() ? 18u : 17u)) continue; const coreVector2 vDir = coreVector2::Direction(DEG_TO_RAD(I_TO_F(j) * 4.5f - 2.25f)); g_pGame->GetBulletManagerEnemy()->AddBullet<cWaveBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pGame->GetBulletManagerEnemy()->AddBullet<cWaveBullet>(5, 1.1f, pEnemy, vPos, -vDir)->ChangeSize(1.7f); } g_pSpecialEffects->CreateSplashColor(coreVector3(vPos, 0.0f), 25.0f, 5u, COLOR_ENERGY_GREEN); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } fRecoverOld = fRecover; }); iTrailOld = iTrail; if(g_pGame->IsTask()) { for(coreUintW i = 0u; i < CALOR_HAILS; ++i) { coreObject3D* pHail = this->GetHail(i); if(!pHail->IsEnabled(CORE_OBJECT_ENABLE_MOVE)) continue; coreFloat fTime; switch(i) { default: UNREACHABLE case 0u: fTime = STEP(0.0f, 2.0f, afRecover[50u % iNumData]); break; case 1u: fTime = STEP(2.0f, 4.0f, afRecover[56u % iNumData]); break; case 2u: fTime = STEP(2.0f, 4.0f, afRecover[65u % iNumData]); break; case 3u: fTime = STEP(1.0f, 5.0f, afRecover[71u % iNumData]); break; case 4u: fTime = STEP(7.0f, 11.0f, afRecover[71u % iNumData]); break; } coreVector2 vPos; switch(i) { default: UNREACHABLE case 0u: vPos = LERP(coreVector2(-1.2f,-0.8f), coreVector2( 1.2f,-0.8f), fTime); break; case 1u: vPos = LERP(coreVector2( 0.5f, 1.2f), coreVector2(-0.5f,-1.2f), fTime); break; case 2u: vPos = coreVector2::Direction(LERP(LERP(-1.25f*PI, -4.0f*PI, 2.0f/5.0f), LERP(-1.25f*PI, -4.0f*PI, 4.0f/5.0f) + (2.0f*PI), fTime)) * 0.5f; break; case 3u: vPos = pPath2->CalcPositionLerp(fTime).Rotated90(); break; case 4u: vPos = pPath2->CalcPositionLerp(fTime).Rotated90().InvertedX(); break; } if(fTime >= 1.0f) this->DisableHail(i, true); else if(fTime <= 0.0f) vPos = HIDDEN_POS; pHail->SetPosition(coreVector3(vPos * FOREGROUND_AREA, 0.0f)); if(fTime && !HAS_BIT(iHailEffect, i)) { ADD_BIT(iHailEffect, i) g_pSpecialEffects->CreateSplashColor(pHail->GetPosition(), SPECIAL_SPLASH_TINY, COLOR_ENERGY_YELLOW); } STAGE_FOREACH_PLAYER(pPlayer, j) { const coreVector2 vDiff = pHail->GetPosition().xy() - pPlayer->GetPosition().xy(); if(vDiff.LengthSq() < POW2(6.0f)) { this->DisableHail(i, true); if(++iHailState >= 5u) { STAGE_BADGE(0u, BADGE_EASY, pHail->GetPosition()) } else { g_pGame->GetCombatText()->DrawProgress(iHailState, 5u, pHail->GetPosition()); g_pSpecialEffects->PlaySound(pHail->GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iHailState, 5u), SOUND_ITEM_02); } } }); } } cHelper* pHelper = g_pGame->GetHelper(ELEMENT_ORANGE); if(m_iStageSub == 4u) { const coreUintW iIndex = 32u; if(!iHelperState && afRecover[iIndex % iNumData]) { iHelperState = 1u; pHelper->Resurrect(false); pHelper->SetPosition(pSquad1->GetEnemy(iIndex)->GetPosition()); } } if(!pHelper->HasStatus(HELPER_STATUS_DEAD)) { const coreVector2 vTarget = coreVector2(0.0f,1.3f) * FOREGROUND_AREA; if(pHelper->DefaultMoveSmooth(vTarget, 120.0f, 12.0f)) { pHelper->Kill(false); } } STAGE_WAVE(0u, "6-1", {50.0f, 75.0f, 100.0f, 125.0f, 250.0f}) // EINUNDDREISSIG }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cStarEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cWaveBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 0u}) { g_pGame->KillHelpers(); for(coreUintW i = 0u; i < CALOR_HAILS; ++i) this->DisableHail(i, false); STAGE_FINISH_NOW }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 1u}) { STAGE_FINISH_PLAY }); // ################################################################ // snowstorm makes the windscreen dirty // - player will avoid snow after explosion, it will drive players away // - player can fly above the first group, to assume a more aggressive position // - enemy creating snow while moving makes him nearly immune, even with offset or timed creation, it also does not fit to the rest and adds no pressure // - low health, enemies are protected by snow // - 1-4: show bullet interaction // - 5-6: show slow down // - 7: ramp up, tease invert // - 8: invert // - creating bullets from walls or snow did not feel good, it was just back-and-forth for the player // - creating snow without action (drops, avalanche, etc.) felt disconnected and random // - hiding enemies (completely) in the snow does not fit to the rest of the mechanic // - enemy at the bottom shows invert-blast, shows that not every enemy attacks, and adds a pause // - after some time, snow gets overwhelming and you cannot see changes anymore, so the invert-mechanic was able to help on that // - enemies protect each other with the snow they create // TASK: destroy all enemies under the snow // TASK: get stuck in snow // TASK EXTRA: collect all snowing objects // ACHIEVEMENT: clean the whole screen before destroying the last enemy // TODO 1: hardmode: player is slipping // TODO 1: hardmode: snow can freeze player // TODO 1: hardmode: snow is created all the time (snowing, avalanche) // TODO 1: adjust (and fix!) snow for all weapons [RP] STAGE_MAIN({TAKE_ALWAYS, 1u}) { STAGE_ADD_PATH(pPath1) { pPath1->Reserve(2u); pPath1->AddNode(coreVector2(0.0f, 1.3f), coreVector2(0.0f,-1.0f)); pPath1->AddNode(coreVector2(0.0f,-1.3f), coreVector2(0.0f,-1.0f)); pPath1->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cFreezerEnemy, 35u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.1f); pEnemy->Configure(10, COLOR_SHIP_YELLOW); }); }); STAGE_ADD_SQUAD(pSquad2, cFreezerEnemy, 3u) { STAGE_FOREACH_ENEMY_ALL(pSquad2, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.1f); pEnemy->Configure(10, COLOR_SHIP_GREY); pEnemy->AddStatus(ENEMY_STATUS_BOTTOM | ENEMY_STATUS_GHOST_PLAYER | ENEMY_STATUS_WORTHLESS); }); const auto nInitFunc = [&](const coreUintW iIndex, const coreVector2 vPos, const coreVector2 vDir) { pSquad2->GetEnemy(iIndex)->SetPosition (coreVector3(vPos * FOREGROUND_AREA, 0.0f)); pSquad2->GetEnemy(iIndex)->SetDirection(coreVector3(vDir, 0.0f)); }; nInitFunc(0u, coreVector2( 1.0f,-3.0f) * 0.25f, coreVector2(-1.0f, 0.0f)); nInitFunc(1u, coreVector2( 3.0f, 1.0f) * 0.25f, coreVector2( 0.0f,-1.0f)); nInitFunc(2u, coreVector2(-3.0f, 1.0f) * 0.25f, coreVector2( 1.0f, 0.0f)); }); STAGE_GET_START(4u + CALOR_HAILS) STAGE_GET_UINT (iExplosions) STAGE_GET_UINT (iHiddenCount) STAGE_GET_UINT (iHailCreate) STAGE_GET_UINT (iHailState) STAGE_GET_FLOAT_ARRAY(afHailTime, CALOR_HAILS) STAGE_GET_END if(pSquad1->IsFinished()) { if(STAGE_SUB( 1u)) STAGE_RESURRECT(pSquad1, 0u, 0u) else if(STAGE_SUB( 2u)) STAGE_RESURRECT(pSquad1, 1u, 1u) else if(STAGE_SUB( 3u)) STAGE_RESURRECT(pSquad1, 2u, 2u) else if(STAGE_SUB( 4u)) STAGE_RESURRECT(pSquad1, 3u, 3u) else if(STAGE_SUB( 5u)) STAGE_RESURRECT(pSquad1, 4u, 5u) else if(STAGE_SUB( 6u)) STAGE_RESURRECT(pSquad1, 6u, 7u) else if(STAGE_SUB( 7u)) STAGE_RESURRECT(pSquad1, 8u, 10u) else if(STAGE_SUB( 8u)) STAGE_RESURRECT(pSquad1, 11u, 13u) else if(STAGE_SUB( 9u)) STAGE_RESURRECT(pSquad1, 34u, 34u) // # else if(STAGE_SUB(10u)) STAGE_RESURRECT(pSquad1, 14u, 14u) else if(STAGE_SUB(11u)) STAGE_RESURRECT(pSquad1, 15u, 15u) else if(STAGE_SUB(12u)) STAGE_RESURRECT(pSquad1, 16u, 17u) else if(STAGE_SUB(13u)) STAGE_RESURRECT(pSquad1, 18u, 18u) else if(STAGE_SUB(14u)) STAGE_RESURRECT(pSquad1, 19u, 19u) else if(STAGE_SUB(15u)) STAGE_RESURRECT(pSquad1, 20u, 21u) else if(STAGE_SUB(16u)) STAGE_RESURRECT(pSquad1, 22u, 25u) else if(STAGE_SUB(17u)) STAGE_RESURRECT(pSquad1, 26u, 33u) else if(STAGE_SUB(18u)) { this->DisableSnow(true); if(!m_Snow.AnyData()) STAGE_BADGE(3u, BADGE_ACHIEVEMENT, g_pGame->FindPlayerDual(0u)->GetPosition()) // snow not yet inverted } if(m_iStageSub == 16u) { g_pGame->GetBulletManagerEnemy()->ClearBullets(true); } if(g_pGame->IsTask()) { if(m_iStageSub == 8u) { STAGE_RESURRECT(pSquad2, 0u, 2u) } } if(g_pGame->IsTaskExtra()) { if((m_iStageSub == 3u) || (m_iStageSub == 6u) || (m_iStageSub == 11u) || (m_iStageSub == 14u) || (m_iStageSub == 17u)) { if(++iHailCreate >= CALOR_HAILS) iHailCreate = 0u; this->EnableHail(iHailCreate); this->GetHail(iHailCreate)->SetPosition(coreVector3(0.0f,1.3f,0.0f) * FOREGROUND_AREA3); } } } cHelper* pHelper = g_pGame->GetHelper(ELEMENT_BLUE); if((m_iStageSub == 17u) && STAGE_BEGINNING2) { pHelper->Resurrect(false); pHelper->SetPosition(coreVector3(0.0f,1.3f,0.0f) * FOREGROUND_AREA3); } if(STAGE_BEGINNING) { this->EnableSnow(); } const auto nLineFunc = [this](const coreVector2 vPosition, const coreFloat fSize, const coreBool bHorizontal, const eSnowType eType) { m_Snow.DrawLine(vPosition, fSize, bHorizontal, eType); if(eType) { g_pGame->GetBulletManagerPlayer()->ForEachBullet([&](cBullet* OUTPUT pBullet) { const coreVector2 vDiff = pBullet->GetPosition().xy() - vPosition; if(ABS(vDiff.arr(bHorizontal)) < fSize) pBullet->Deactivate(true); }); } }; const auto nPointFunc = [this](const coreVector2 vPosition, const coreFloat fSize, const eSnowType eType) { m_Snow.DrawPoint(vPosition, fSize, eType); if(eType) { g_pGame->GetBulletManagerPlayer()->ForEachBullet([&](cBullet* OUTPUT pBullet) { const coreVector2 vDiff = pBullet->GetPosition().xy() - vPosition; if(vDiff.LengthSq() < POW2(fSize)) pBullet->Deactivate(true); }); } }; const auto nAllFunc = [this](const eSnowType eType) { m_Snow.DrawAll(eType); if(eType != SNOW_TYPE_REMOVE) g_pGame->GetBulletManagerPlayer()->ClearBullets(true); if(eType == SNOW_TYPE_ADD) g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_SMALL, 250u); }; const coreFloat fHailSpeed = g_pGame->IsEasy() ? 0.4f : 0.7f; if((m_iStageSub >= 16u) && (m_iStageSub < 18u)) { if((STAGE_SUBTIME_AFTER(0.5f) || (m_iStageSub >= 17u)) && STAGE_TICK_FREE(12.0f * fHailSpeed, 0.0f)) { const coreVector2 vPos = coreVector2((I_TO_F((s_iTick * 3u) % 11u) - 5.0f) / 5.0f * -1.0f, 1.2f) * FOREGROUND_AREA; const coreVector2 vDir = coreVector2(0.0f,-1.0f); const coreInt32 iDamage = s_iTick + 1; g_pGame->GetBulletManagerEnemy()->AddBullet<cOrbBullet>(iDamage, 1.1f * fHailSpeed, pSquad1->GetEnemy(0u), vPos, vDir)->ChangeSize(1.7f); } g_pGame->GetBulletManagerEnemy()->ForEachBullet([&](cBullet* OUTPUT pBullet) { const coreFloat fOffset = SIN(pBullet->GetFlyTime() * fHailSpeed + I_TO_F(pBullet->GetDamage()) * GA); const coreVector2 vDir = coreVector2(fOffset, -1.5f).Normalized(); pBullet->SetFlyDir(vDir); }); } if(!pHelper->HasStatus(HELPER_STATUS_DEAD)) { const coreFloat fOffset = COS(pHelper->GetLifeTime() * fHailSpeed * 2.0f); const coreVector2 vDir = coreVector2(fOffset, -1.5f).Normalized(); const coreVector2 vPos = pHelper->GetPosition().xy() + vDir * (1.1f * fHailSpeed * BULLET_SPEED_FACTOR * TIME); if(vPos.y < -FOREGROUND_AREA.y * 1.3f) pHelper->Kill(false); pHelper->SetPosition(coreVector3(vPos, 0.0f)); } if(g_pGame->IsTask()) { STAGE_FOREACH_PLAYER(pPlayer, i) { if(m_afSnowStuck[i] >= 1.0f) { STAGE_BADGE(1u, BADGE_NORMAL, pPlayer->GetPosition()) } }); } if(g_pGame->IsTaskExtra()) { for(coreUintW i = 0u; i < CALOR_HAILS; ++i) { coreObject3D* pHail = this->GetHail(i); if(!pHail->IsEnabled(CORE_OBJECT_ENABLE_MOVE)) continue; afHailTime[i] += 1.0f * TIME; const coreFloat fOffset = SIN(afHailTime[i] * fHailSpeed + I_TO_F(i + 2u) * -GA); const coreVector2 vDir = coreVector2(fOffset, -1.5f).Normalized(); const coreVector2 vPos = pHail->GetPosition().xy() + vDir * (1.1f * fHailSpeed * BULLET_SPEED_FACTOR * TIME); if(vPos.y < -FOREGROUND_AREA.y * 1.3f) this->DisableHail(i, false); pHail->SetPosition(coreVector3(vPos, 0.0f)); STAGE_FOREACH_PLAYER(pPlayer, j) { const coreVector2 vDiff = pHail->GetPosition().xy() - pPlayer->GetPosition().xy(); if(vDiff.LengthSq() < POW2(5.0f)) { this->DisableHail(i, true); if(++iHailState >= 5u) { STAGE_BADGE(2u, BADGE_HARD, pHail->GetPosition()) } else { g_pGame->GetCombatText()->DrawProgress(iHailState, 5u, pHail->GetPosition()); g_pSpecialEffects->PlaySound(pHail->GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iHailState, 5u), SOUND_ITEM_01); } } }); } } STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { if(i < 14u) { STAGE_LIFETIME(pEnemy, 1.0f, 0.5f * ((i < 4u) ? 0.0f : ((i < 8u) ? I_TO_F((i - 4u) % 2u) : I_TO_F((i - 8u) % 3u))) + ((i >= 11u && i < 14u) ? 4.0f : 0.0f)) const coreFloat fValue = 0.5f + 0.5f * SIN(MAX(fLifeTime, 0.0f) + 0.5f*PI); if(i < 4u) { pEnemy->SetPosition(coreVector3(((i < 2u) ? 0.6f : 0.2f) * ((i % 2u) ? 1.0f : -1.0f), LERP(-0.8f, 1.3f, fValue), 0.0f) * FOREGROUND_AREA3); if(pEnemy->ReachedDeath()) { nLineFunc(pEnemy->GetPosition().xy(), 5.0f, true, SNOW_TYPE_ADD); } } else if(i < 8u) { pEnemy->SetPosition(coreVector3(LERP(-0.8f, 1.3f, fValue) * ((i < 6u) ? 1.0f : -1.0f), ((i - 4u) % 2u) ? 0.4f : 0.8f, 0.0f) * FOREGROUND_AREA3); if(pEnemy->ReachedDeath()) { nLineFunc(pEnemy->GetPosition().xy(), 5.0f, false, SNOW_TYPE_ADD); } } else { pEnemy->SetPosition(coreVector3((I_TO_F((i - 8u) % 3u) - 1.0f) * ((i < 11u) ? 0.6f : -0.6f), LERP(0.0f, 1.3f, fValue), 0.0f) * FOREGROUND_AREA3); if(pEnemy->ReachedDeath()) { if(++iExplosions == 3u) { nAllFunc(SNOW_TYPE_ADD); cSnowBackground* pBackground = d_cast<cSnowBackground*>(g_pEnvironment->GetBackground()); pBackground->GetOutdoor()->LerpHeightNow(1.0f, 0.0f); pBackground->SetGroundDensity(0u, 0.0f, true); pBackground->SetGroundDensity(1u, 1.0f, true); pBackground->SetGroundDensity(2u, 1.0f, true); pBackground->SetAirDensity (1u, 0.0f, true); g_pEnvironment->SetTargetDirectionNow(coreVector2(0.0f,-1.0f)); } else { nPointFunc(pEnemy->GetPosition().xy(), 15.0f, SNOW_TYPE_REMOVE); } } } pEnemy->DefaultRotate(fLifeTime * 3.0f); if(STAGE_TICK_LIFETIME(60.0f, 0.0f) && ((s_iTick % ((i < 8u) ? 16u : 8u)) < (g_pGame->IsEasy() ? 2u : 4u))) { const coreVector2 vPos = pEnemy->GetPosition ().xy(); const coreVector2 vDir = pEnemy->GetDirection().xy(); g_pGame->GetBulletManagerEnemy()->AddBullet<cOrbBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pSpecialEffects->CreateSplashColor(coreVector3(vPos, 0.0f), 10.0f, 1u, COLOR_ENERGY_BLUE); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } } else if(i < 22u) { STAGE_LIFETIME(pEnemy, (i < 18u) ? 0.7f : 1.4f, (i < 18u) ? ((i == 17u) ? 0.5f : 0.1f) : 0.0f) if(i < 18u) { const coreFloat X = 0.9f * SIN(fLifeTime + I_TO_F(((i - 14u) * 2u + 1u) % 5u) * (0.5f*PI)); const coreFloat Y = FmodRange(1.3f - MAX0(fLifeTime), -1.3f, 1.3f); pEnemy->SetPosition(coreVector3(X, Y, 0.0f) * FOREGROUND_AREA3); } else { const coreFloat fSide = (i < 20u) ? -1.0f : 1.0f; const coreVector2 vDir = coreVector2::Direction((fLifeTime - I_TO_F((i - 18u) % 2u) * (1.0f*PI)) * fSide); const coreFloat fLen = LERPB(1.3f, 0.6f, MIN1(fLifeTime * 0.1f)) * FOREGROUND_AREA.x; pEnemy->SetPosition(coreVector3(vDir * fLen, 0.0f)); } if(pEnemy->ReachedDeath()) { nPointFunc(pEnemy->GetPosition().xy(), 30.0f, SNOW_TYPE_INVERT); } pEnemy->DefaultRotate(fLifeTime * 3.0f); if(STAGE_TICK_LIFETIME(60.0f / fLifeSpeed, 0.0f) && ((s_iTick % 8u) < (g_pGame->IsEasy() ? 2u : 4u))) { const coreVector2 vPos = pEnemy->GetPosition ().xy(); const coreVector2 vDir = pEnemy->GetDirection().xy(); g_pGame->GetBulletManagerEnemy()->AddBullet<cOrbBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pSpecialEffects->CreateSplashColor(coreVector3(vPos, 0.0f), 10.0f, 1u, COLOR_ENERGY_BLUE); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } } else if(i < 35u) { STAGE_LIFETIME(pEnemy, (i < 34u) ? 0.8f : 0.5f, (i < 34u) ? (3.5f + 0.325f * I_TO_F((i - 22u) % 8u)) : 0.0f) STAGE_REPEAT(pPath1->GetTotalDistance()) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((i < 26u) ? ((I_TO_F(i - 22u) - 1.5f) * 0.6f) : ((i < 34u) ? ((I_TO_F((((i - 26u) % 4u) * 2u + 1u) % 5u) - 1.5f) * 0.6f) : -0.8f), 0.0f); pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); if(i == 22u) {} else if(i == 23u) pEnemy->InvertY (); else if(i == 24u) {} else if(i == 25u) pEnemy->InvertY (); else if(i < 34u) pEnemy->Rotate90 (); else if(i < 35u) pEnemy->Rotate270(); if(pEnemy->ReachedDeath()) { if(i < 26u) { nLineFunc(pEnemy->GetPosition().xy(), 5.0f, true, SNOW_TYPE_INVERT); nLineFunc(pEnemy->GetPosition().xy(), 5.0f, false, SNOW_TYPE_INVERT); } else if(i < 34u) { nAllFunc(SNOW_TYPE_INVERT); } else { nPointFunc(pEnemy->GetPosition().xy(), 30.0f, SNOW_TYPE_INVERT); } } pEnemy->DefaultRotate(fLifeTimeBase * 3.0f); } }); STAGE_FOREACH_ENEMY(pSquad2, pEnemy, i) { STAGE_LIFETIME(pEnemy, 0.5f, 0.0f) pEnemy->DefaultMoveForward(pEnemy->GetDirection().xy(), 15.0f); ASSERT(m_Snow.IsActive()) const coreBool bHitOld = m_Snow.TestCollision(pEnemy->GetOldPos()); const coreBool bHitNew = m_Snow.TestCollision(pEnemy->GetPosition().xy()); pEnemy->SetAlpha(bHitNew ? 0.2f : 1.0f); if(bHitOld != bHitNew) { g_pSpecialEffects->CreateSplashDot(pEnemy->GetPosition(), SPECIAL_SPLASH_TINY, COLOR_ENERGY_WHITE); } if(pEnemy->ReachedDeath()) { if(++iHiddenCount >= pSquad2->GetNumEnemies()) { STAGE_BADGE(0u, BADGE_EASY, pEnemy->GetPosition()) } else { g_pSpecialEffects->PlaySound(pEnemy->GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iHiddenCount, pSquad2->GetNumEnemies()), SOUND_ITEM_02); } } if(!g_pForeground->IsVisiblePoint(pEnemy->GetPosition().xy(), 1.3f)) { pEnemy->Kill(false); } }); if((m_iStageSub == 9u) && STAGE_BEGINNING2) g_pEnvironment->SetTargetDirectionLerp(coreVector2(0.0f,1.0f), 30.0f); STAGE_WAVE(1u, "6-2", {55.0f, 80.0f, 110.0f, 135.0f, 270.0f}) // ZWEIUNDDREISSIG }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cFreezerEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cOrbBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 1u}) { g_pGame->KillHelpers(); this->DisableSnow(false); for(coreUintW i = 0u; i < CALOR_HAILS; ++i) this->DisableHail(i, false); STAGE_FINISH_NOW }); // ################################################################ // change background appearance STAGE_MAIN({TAKE_ALWAYS, 2u}) { cSnowBackground* pBackground = d_cast<cSnowBackground*>(g_pEnvironment->GetBackground()); pBackground->GetOutdoor()->LerpHeightNow(1.0f, 0.0f); pBackground->SetGroundDensity(0u, 0.0f); pBackground->SetGroundDensity(1u, 1.0f); pBackground->SetGroundDensity(2u, 1.0f); pBackground->SetAirDensity (1u, 0.0f); STAGE_FINISH_NOW }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 2u}) { STAGE_FINISH_PLAY }); // ################################################################ // everything moves against current direction // - no infinity for opponents, difficult to understand, some could be hiding // - all enemies in the dungeon are optional, you could miss one and backtracking is impossible, portals are boring, pay attention to points! // - (old: in dungeon start-tunnel, opponents should not be behind a straight line, otherwise they will simply be shot when turning (though I think tat's good to show the dungeon movement)) // - all enemies in the dungeon have their own row, colum, and are not identical between rooms // - enemy arrangement should make the player turn, distinguish between 90 (easy) and 180 (hard) // - first enemy group has to make the player turn while bullets cannot yet do any damage to him, but are already clearly visible // - traps on doors lure the player there, and because they are different you know where you got in and out // - enemy groups in the first two phases must have a good flow (with attention to player rotation), be careful that the next opponents are not shot straight away // - last enemy of the second phase must guide the player to the dungeon entrance (already rotating) // - start-tunnel in dungeon prepares the player // - there should be no enemies outside the dungeon (except a special one), otherwise you can easily miss them // - bullets in dungeon are created in order to have consistent straight line overlap everywhere (bottom to top, left to right), corners are ignored and random though // - corners in dungeon have double-bullets, but they are important for collision handling // - collision handling in the dungeon is extremely unstable, and can fail with slightly different parameters, should be fixed or not reused (possible issues: player collision handling, bullet collision range (player needs to touch cluster), global move, bullet test order, cGame callback being called later, ...) // - background needs to move slower, to improve visual distinction with the moving bullets (could also be during a sandstorm, rainstorm or other visual distortion to completely remove the background movement) // - fullscreen arrow/cone (to show the current move direction) distracts too much from chests and the target arrow // TASK: collect hidden treasure-boxes in dungeons (open up when flying over it) // TASK: destroy secret enemy at the back of the initial room // ACHIEVEMENT: create a prison with a maximum size of 4x4 bullets // TODO 1: hardmode: attacking enemies // TODO 1: hardmode: moving enemies in dungeon // TODO 1: hardmode: additional attacking pseudo-enemy, made of bullet?), or infinity object, even in dungeon (though similar to P1 object) // TODO 3: late update: make sure corners in dungeon are visually consistent (depth) STAGE_MAIN({TAKE_ALWAYS, 2u}) { STAGE_ADD_PATH(pPath1) { pPath1->Reserve(2u); pPath1->AddNode(coreVector2(0.0f,1.3f), coreVector2(0.0f,-1.0f)); pPath1->AddStop(coreVector2(0.0f,0.9f), coreVector2(0.0f,-1.0f)); pPath1->Refine(); }); STAGE_ADD_PATH(pPath2) { pPath2->Reserve(2u); pPath2->AddNode(coreVector2(0.0f, 1.3f), coreVector2(0.0f,-1.0f)); pPath2->AddNode(coreVector2(0.0f,-1.3f), coreVector2(0.0f,-1.0f)); pPath2->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cMinerEnemy, 64u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.3f); pEnemy->Configure(4, COLOR_SHIP_PURPLE); }); }); STAGE_GET_START(17u) STAGE_GET_VEC2 (vGlobalOffset) STAGE_GET_VEC2 (vGlobalMove) STAGE_GET_VEC2 (vGlobalDir, vGlobalDir = coreVector2(0.0f,1.0f)) STAGE_GET_UINT (iBulletField) STAGE_GET_UINT (iTrapIndex) STAGE_GET_UINT (iSecretIndex) STAGE_GET_UINT (iCreationDelay) STAGE_GET_UINT (iCreationEnemy, iCreationEnemy = 44u) STAGE_GET_UINT (iCreationChest) STAGE_GET_UINT (iChestCount) STAGE_GET_UINT (iReset) STAGE_GET_UINT (iHitState) STAGE_GET_UINT (iHitStateOld) STAGE_GET_FLOAT(fSpeedFix) STAGE_GET_END if(STAGE_CLEARED) { if(STAGE_SUB(1u)) STAGE_RESURRECT(pSquad1, 0u, 1u) else if(STAGE_SUB(2u)) STAGE_RESURRECT(pSquad1, 2u, 6u) else if(STAGE_SUB(3u)) STAGE_RESURRECT(pSquad1, 7u, 11u) else if(STAGE_SUB(4u)) STAGE_RESURRECT(pSquad1, 12u, 16u) else if(STAGE_SUB(5u)) STAGE_RESURRECT(pSquad1, 17u, 26u) else if(STAGE_SUB(6u)) STAGE_RESURRECT(pSquad1, 27u, 34u) else if(STAGE_SUB(7u)) STAGE_RESURRECT(pSquad1, 35u, 42u) else if(STAGE_SUB(8u)) STAGE_RESURRECT(pSquad1, 43u, 43u) else if(STAGE_SUB(9u)) STAGE_RESURRECT(pSquad1, 44u, 63u) if((m_iStageSub == 6u) || (m_iStageSub == 9u)) { vGlobalOffset = coreVector2(0.0f,0.0f); g_pGame->GetBulletManagerEnemy()->ClearBulletsTyped<cQuadBullet>(true); g_pSpecialEffects->PlaySound(SPECIAL_RELATIVE, 1.0f, 1.0f, SOUND_ENEMY_EXPLOSION_06); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_SMALL, 250u); } if(m_iStageSub == 9u) { this->ResetCollPlayerBullet(); } } cHelper* pHelperBlue = g_pGame->GetHelper(ELEMENT_BLUE); cHelper* pHelperCyan = g_pGame->GetHelper(ELEMENT_CYAN); cEnemy* pDummy = pSquad1->GetEnemy(0u); constexpr coreFloat fScale = 0.1f; const auto nPositionFunc = [&](const coreInt32 x, const coreInt32 y, const coreVector2 vShift = coreVector2(0.0f,0.0f)) { return (coreVector2(I_TO_F(x), I_TO_F(y) + 2.5f) + vShift) * (FOREGROUND_AREA * fScale * 6.0f) + vGlobalOffset; }; const auto nAddBulletFunc = [&](const coreInt32 iDamage, const coreVector2 vPosition, const coreVector2 vDirection, const coreFloat fScale = 1.3f) { return g_pGame->GetBulletManagerEnemy()->AddBullet<cQuadBullet>(iDamage, 0.0f, pDummy, vPosition, vDirection)->ChangeSize(fScale)->ChangeCollisionModifier(coreVector3(1.0f,1.0f,1.0f) * 1.0f)->AddStatus(BULLET_STATUS_IMMORTAL); }; const auto nAddBulletWallFunc = [&](const coreInt32 iDamage, const coreVector2 vPosition, const coreVector2 vDirection, const coreFloat fScale = 1.3f) { return g_pGame->GetBulletManagerEnemy()->AddBullet<cQuadBullet>(iDamage, 0.0f, pDummy, vPosition, vDirection)->ChangeSize(fScale)->ChangeCollisionModifier(coreVector3(1.0f,1.0f,1.0f) * 1.5f)->AddStatus(BULLET_STATUS_IMMORTAL | BULLET_STATUS_GHOST); }; if((m_iStageSub == 9u) && (iCreationDelay < 6u)) { const auto nCreateBlockFunc = [&](const coreInt32 x, const coreInt32 y, const coreUint8 iShape = 0u) { const coreVector2 vBase = nPositionFunc(x, y); if(iShape) { // 0 // 3 1 // 2 for(coreUintW i = 0u; i < 7u; ++i) { const coreVector2 vPos = coreVector2(I_TO_F(i) - 3.0f, 3.0f) * (FOREGROUND_AREA * fScale); const coreVector2 vDir = coreVector2(0.0f,1.0f); if(HAS_BIT(iShape, 0u)) nAddBulletWallFunc(6, vPos + vBase, vDir); if(HAS_BIT(iShape, 2u)) nAddBulletWallFunc(6, vPos.InvertedY() + vBase, vDir); if(HAS_BIT(iShape, 3u)) nAddBulletWallFunc(7, -vPos .Rotated90() + vBase, vDir); if(HAS_BIT(iShape, 1u)) nAddBulletWallFunc(7, -vPos.InvertedY().Rotated90() + vBase, vDir); } } else { const coreVector2 vPos = coreVector2(0.5f,0.5f) * 0.11f * FOREGROUND_AREA; const coreVector2 vDir = coreVector2(0.0f,1.0f); nAddBulletFunc(5, vPos + vBase, vDir); nAddBulletFunc(5, -vPos + vBase, vDir); nAddBulletFunc(5, vPos.Rotated90() + vBase, vDir); nAddBulletFunc(5, -vPos.Rotated90() + vBase, vDir); } }; const auto nCreateEnemyFunc = [&](const coreInt32 x, const coreInt32 y) { const coreVector2 vBase = nPositionFunc(x, y); pSquad1->GetEnemy(iCreationEnemy++)->SetPosition(coreVector3(vBase, 0.0f)); }; const auto nCreateChestFunc = [&](const coreInt32 x, const coreInt32 y) { if(g_pGame->IsTask()) { const coreVector2 vBase = nPositionFunc(x, y); this->EnableChest(iCreationChest); this->GetChest(iCreationChest++)->SetPosition(coreVector3(vBase, 0.0f)); } }; switch(iCreationDelay++) { default: UNREACHABLE case 0u: nCreateBlockFunc(-2, -4, BIT(3u) | BIT(2u)); nCreateBlockFunc(-1, -4, BIT(2u)); nCreateBlockFunc( 0, -4, BIT(2u)); nCreateBlockFunc( 1, -4, BIT(2u)); nCreateBlockFunc( 2, -4, BIT(2u) | BIT(1u)); nCreateBlockFunc(-2, -3, BIT(3u)); nCreateBlockFunc( 2, -3, BIT(1u)); nCreateBlockFunc(-2, -2, BIT(3u)); nCreateBlockFunc( 2, -2, BIT(1u)); nCreateBlockFunc(-2, -1, BIT(3u) | BIT(0u)); nCreateBlockFunc(-1, -1, BIT(0u)); nCreateBlockFunc( 1, -1, BIT(0u)); nCreateBlockFunc( 2, -1, BIT(1u) | BIT(0u)); if(g_pGame->IsTask()) { iSecretIndex = iCreationEnemy; ASSERT(iSecretIndex) nCreateEnemyFunc( 0, -5); } break; case 1u: nCreateBlockFunc( 0, 0, BIT(3u) | BIT(1u)); nCreateBlockFunc( 0, 1, BIT(3u) | BIT(0u)); nCreateBlockFunc( 1, 1, BIT(2u) | BIT(0u)); nCreateBlockFunc( 2, 1, BIT(2u) | BIT(1u)); nCreateBlockFunc( 2, 2, BIT(3u) | BIT(1u)); nCreateBlockFunc( 0, 3, BIT(3u) | BIT(2u)); nCreateBlockFunc( 1, 3, BIT(2u) | BIT(0u)); nCreateBlockFunc( 2, 3, BIT(1u) | BIT(0u)); nCreateBlockFunc( 0, 4, BIT(3u) | BIT(1u)); nCreateEnemyFunc( 1, 1); nCreateEnemyFunc( 2, 2); nCreateEnemyFunc( 1, 3); break; case 2u: nCreateBlockFunc(-2, 5, BIT(3u) | BIT(2u) | BIT(0u)); nCreateBlockFunc(-1, 5, BIT(2u) | BIT(1u)); nCreateBlockFunc( 1, 5, BIT(2u)); nCreateBlockFunc( 2, 5, BIT(2u) | BIT(1u)); nCreateBlockFunc(-2, 6, BIT(3u)); nCreateBlockFunc( 0, 6, BIT(0u)); nCreateBlockFunc( 1, 6, BIT(1u)); nCreateBlockFunc( 2, 6, BIT(1u)); nCreateBlockFunc(-2, 7, BIT(3u) | BIT(1u)); nCreateBlockFunc( 1, 7, BIT(0u)); nCreateBlockFunc( 2, 7, BIT(1u)); nCreateBlockFunc(-2, 8, BIT(3u)); nCreateBlockFunc(-1, 8, BIT(0u)); nCreateBlockFunc( 2, 8, BIT(1u) | BIT(0u)); nCreateBlockFunc(-2, 9, BIT(3u) | BIT(0u)); nCreateBlockFunc(-1, 9, BIT(0u)); nCreateBlockFunc( 0, 9, BIT(1u)); nCreateBlockFunc( 1, 9, BIT(0u)); nCreateBlockFunc( 2, 9, BIT(1u) | BIT(0u)); nCreateEnemyFunc(-2, 5); nCreateEnemyFunc( 2, 6); nCreateEnemyFunc( 0, 7); nCreateEnemyFunc(-1, 8); nCreateEnemyFunc( 1, 9); nCreateChestFunc(-2, 9); nCreateChestFunc( 1, 6); break; case 3u: nCreateBlockFunc( 0, 10, BIT(3u) | BIT(1u)); nCreateBlockFunc( 0, 11, BIT(3u) | BIT(1u)); nCreateBlockFunc(-2, 12, BIT(3u) | BIT(2u)); nCreateBlockFunc(-1, 12, BIT(2u) | BIT(0u)); nCreateBlockFunc( 0, 12, BIT(0u)); nCreateBlockFunc( 1, 12, BIT(2u) | BIT(1u) | BIT(0u)); nCreateBlockFunc( 2, 12, BIT(2u) | BIT(1u)); nCreateBlockFunc(-2, 13, BIT(3u) | BIT(0u)); nCreateBlockFunc( 0, 13, BIT(0u)); nCreateBlockFunc( 1, 13, BIT(0u)); nCreateBlockFunc( 2, 13, BIT(1u)); nCreateBlockFunc(-2, 14, BIT(3u) | BIT(1u)); nCreateBlockFunc(-1, 14, BIT(1u)); nCreateBlockFunc( 1, 14, BIT(1u)); nCreateBlockFunc( 2, 14, BIT(1u)); nCreateBlockFunc(-2, 15, BIT(3u)); nCreateBlockFunc(-1, 15, BIT(0u)); nCreateBlockFunc( 0, 15, BIT(1u) | BIT(0u)); nCreateBlockFunc( 1, 15, BIT(0u)); nCreateBlockFunc( 2, 15, BIT(1u)); nCreateBlockFunc(-2, 16, BIT(3u) | BIT(0u)); nCreateBlockFunc(-1, 16, BIT(0u)); nCreateBlockFunc( 1, 16, BIT(0u)); nCreateBlockFunc( 2, 16, BIT(1u) | BIT(0u)); nCreateEnemyFunc( 2, 12); nCreateEnemyFunc( 0, 13); nCreateEnemyFunc(-2, 14); nCreateEnemyFunc( 1, 15); nCreateEnemyFunc(-1, 16); nCreateChestFunc( 0, 14); nCreateChestFunc( 1, 12); break; case 4u: nCreateBlockFunc( 0, 17, BIT(3u) | BIT(1u)); nCreateBlockFunc( 0, 18, BIT(3u) | BIT(1u)); nCreateBlockFunc(-2, 19, BIT(3u) | BIT(2u)); nCreateBlockFunc(-1, 19, BIT(2u) | BIT(0u)); nCreateBlockFunc( 0, 19, BIT(0u)); nCreateBlockFunc( 1, 19, BIT(2u) | BIT(0u)); nCreateBlockFunc( 2, 19, BIT(2u) | BIT(1u)); nCreateBlockFunc(-2, 20, BIT(3u) | BIT(1u)); nCreateBlockFunc( 1, 20, BIT(1u)); nCreateBlockFunc( 2, 20, BIT(1u)); nCreateBlockFunc(-2, 21, BIT(3u) | BIT(1u)); nCreateBlockFunc(-1, 21, BIT(1u)); nCreateBlockFunc( 0, 21, BIT(1u) | BIT(0u)); nCreateBlockFunc( 1, 21, BIT(1u)); nCreateBlockFunc( 2, 21, BIT(1u)); nCreateBlockFunc(-2, 22, BIT(3u) | BIT(1u)); nCreateBlockFunc(-1, 22, BIT(0u)); nCreateBlockFunc( 1, 22, BIT(1u) | BIT(0u)); nCreateBlockFunc( 2, 22, BIT(1u)); nCreateBlockFunc(-2, 23, BIT(3u) | BIT(0u)); nCreateBlockFunc(-1, 23, BIT(0u)); nCreateBlockFunc( 0, 23, BIT(0u)); nCreateBlockFunc( 1, 23, BIT(0u)); nCreateBlockFunc( 2, 23, BIT(1u) | BIT(0u)); nCreateEnemyFunc(-1, 19); nCreateEnemyFunc( 1, 20); nCreateEnemyFunc( 2, 21); nCreateEnemyFunc( 0, 22); nCreateEnemyFunc(-2, 23); nCreateChestFunc(-1, 20); nCreateChestFunc( 1, 23); break; case 5u: iTrapIndex = g_pGame->GetBulletManagerEnemy()->GetNumBullets(); for(coreUintW i = 0u; i < 34u; ++i) { const coreVector2 vPos = HIDDEN_POS; const coreVector2 vDir = coreVector2(0.0f,1.0f); nAddBulletFunc(4, vPos, vDir, 1.6f); } nCreateEnemyFunc(1000, 1000); pHelperBlue->AddStatus(HELPER_STATUS_HIDDEN); pHelperBlue->Resurrect(false); pHelperBlue->SetPosition(coreVector3(nPositionFunc(0, 21), 0.0f)); pHelperCyan->Resurrect(false); pHelperCyan->SetPosition(coreVector3(nPositionFunc(3, 12), 0.0f)); this->EnableAim(pHelperBlue); break; } } const coreVector2 vBaseDir = g_pGame->FindPlayerDual(0u)->GetDirection().xy(); coreBool bSameBase = true; STAGE_FOREACH_PLAYER(pPlayer, i) { if(!SameDirection(vBaseDir, pPlayer->GetDirection().xy())) bSameBase = false; if(m_iStageSub < 10u) pPlayer->ShowArrow(0u); }); if(bSameBase) vGlobalDir = vBaseDir; const coreFloat fGlobalSpeed = g_pGame->IsEasy() ? 20.0f : 30.0f; vGlobalMove = iReset ? -vGlobalOffset : (vGlobalDir * (-fGlobalSpeed * TIME * (1.0f - (g_pGame->IsVersion(12u) ? fSpeedFix : MIN1(fSpeedFix))))); fSpeedFix = MAX0(fSpeedFix - 1.0f * TIME); vGlobalOffset += vGlobalMove; STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { STAGE_LIFETIME(pEnemy, 0.7f, (i < 2u) ? (0.5f + 0.2f * I_TO_F(i % 2u)) : ((i < 17u) ? (0.2f * I_TO_F((i - 2u) % 5u)) : 0.0f)) if(i < 44u) { const coreSpline2* pPath = (i < 2u || i >= 27u) ? pPath1 : pPath2; if(pPath == pPath2) STAGE_REPEAT(pPath->GetTotalDistance()) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((i < 2u) ? ((i % 2u) ? -0.9f : -0.7f) : ((i < 17u) ? -0.8f : ((i < 27u) ? ((I_TO_F(i - 17u) - 4.5f) * 0.22f) : ((i < 43u) ? ((I_TO_F((i - 27u) % 4u) - 1.5f) * 0.3f) : 0.0f))), 0.0f); pEnemy->DefaultMovePath(pPath, vFactor, vOffset * vFactor, fLifeTime); if(i < 2u) pEnemy->Rotate270(); else if(i < 7u) pEnemy->Rotate90 (); else if(i < 12u) pEnemy->Rotate180(); else if(i < 17u) {} else if(i < 27u) pEnemy->Rotate180(); else if(i < 31u) pEnemy->Rotate180(); else if(i < 35u) {} else if(i < 39u) pEnemy->Rotate270(); else if(i < 43u) pEnemy->Rotate90 (); else if(i < 44u) {} } else { const coreVector2 vNewPos = pEnemy->GetPosition().xy() + vGlobalMove; pEnemy->SetPosition(coreVector3(vNewPos, 0.0f)); } if(i >= 43u) pEnemy->DefaultRotate(m_fStageSubTime * (1.4f*PI)); if(iSecretIndex && (i == iSecretIndex) && pEnemy->ReachedDeath()) { STAGE_BADGE(1u, BADGE_NORMAL, pEnemy->GetPosition()) } }); if(m_iStageSub < 6u) { constexpr coreUintW iBullets = 18u; STATIC_ASSERT(iBullets <= sizeof(iBulletField)*8u) for(coreUintW i = 0u; i < iBullets; ++i) { if(HAS_BIT(iBulletField, i)) continue; if(g_pGame->IsEasy() && (i % 2u)) continue; const coreVector2 vBulletPos = vGlobalOffset + (coreVector2(I_TO_F(i % 6u), I_TO_F(i / 6u) * 2.0f + I_TO_F(i % 2u)) / 6.0f - 0.5f) * (FOREGROUND_AREA * 2.8f); if((ABS(vBulletPos.x) > FOREGROUND_AREA.x * 1.4f) || (ABS(vBulletPos.y) > FOREGROUND_AREA.y * 1.4f)) { ADD_BIT(iBulletField, i) const coreVector2 vPos = coreVector2(0.5f,0.5f) * 0.11f * FOREGROUND_AREA; const coreVector2 vDir = coreVector2(0.0f,1.0f); nAddBulletFunc(5, vPos + vBulletPos, vDir); nAddBulletFunc(5, -vPos + vBulletPos, vDir); nAddBulletFunc(5, vPos.Rotated90() + vBulletPos, vDir); nAddBulletFunc(5, -vPos.Rotated90() + vBulletPos, vDir); } } } else if(m_iStageSub < 9u) { if(!g_pGame->GetBulletManagerEnemy()->GetNumBulletsEst()) { for(coreUintW j = 0u; j < 4u; ++j) { for(coreUintW i = 0u; i < 66u; ++i) { const coreVector2 vPos = MapStepRotated90(coreVector2((I_TO_F(i) - 33.0f) * 0.11f, 1.21f) * FOREGROUND_AREA, j); const coreVector2 vDir = coreVector2(0.0f,1.0f); nAddBulletFunc(j + 1, vPos, vDir); } } } coreFloat afRange[4] = {1000.0f}; // +y, -x, -y, +x const coreVector2 vBorder = (vGlobalOffset - vGlobalMove).Processed(FmodRange, 0.0f, FOREGROUND_AREA.x * 0.11f); g_pGame->GetBulletManagerEnemy()->ForEachBulletTyped<cQuadBullet>([&](cQuadBullet* OUTPUT pBullet) { const coreUintW iDamage = pBullet->GetDamage(); ASSERT((iDamage >= 1u) && (iDamage <= 4u)) const coreVector2 vDir = UnpackDirection((iDamage - 1u) * 2u); coreVector2 vNewPos = pBullet->GetPosition().xy(); if((vNewPos.x < -FOREGROUND_AREA.x * 1.21f) && (vDir.x < 0.0f)) vNewPos.x = -FOREGROUND_AREA.x * 1.32f + vBorder.x; // # else if((vNewPos.x > FOREGROUND_AREA.x * 1.21f) && (vDir.x > 0.0f)) vNewPos.x = FOREGROUND_AREA.x * 1.21f + vBorder.x; if((vNewPos.y < -FOREGROUND_AREA.y * 1.21f) && (vDir.y < 0.0f)) vNewPos.y = -FOREGROUND_AREA.y * 1.32f + vBorder.y; // # else if((vNewPos.y > FOREGROUND_AREA.y * 1.21f) && (vDir.y > 0.0f)) vNewPos.y = FOREGROUND_AREA.y * 1.21f + vBorder.y; pBullet->SetPosition(coreVector3(vNewPos, 0.0f)); afRange[iDamage - 1u] = pBullet->GetPosition().arr(iDamage % 2u); }); if((afRange[3] - afRange[1] < 15.0f) && (afRange[0] - afRange[2] < 15.0f)) { STAGE_BADGE(3u, BADGE_ACHIEVEMENT, g_pGame->FindPlayerDual(0u)->GetPosition()) } STAGE_COLL_PLAYER_BULLET(pPlayer, pBullet, vIntersection, bFirstHit, COLL_REF(vGlobalOffset)) { vGlobalOffset = coreVector2(0.0f,0.0f); g_pGame->GetBulletManagerEnemy()->ClearBulletsTyped<cQuadBullet>(true); g_pSpecialEffects->PlaySound(SPECIAL_RELATIVE, 1.0f, 1.0f, SOUND_ENEMY_EXPLOSION_06); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_SMALL, 250u); }); STAGE_FOREACH_PLAYER(pPlayer, i) { if(pPlayer->HasStatus(PLAYER_STATUS_REPAIRED)) { vGlobalOffset = coreVector2(0.0f,0.0f); g_pGame->GetBulletManagerEnemy()->ClearBulletsTyped<cQuadBullet>(true); g_pSpecialEffects->PlaySound(SPECIAL_RELATIVE, 1.0f, 1.0f, SOUND_ENEMY_EXPLOSION_06); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_SMALL, 250u); } }); } else { if(iTrapIndex) { iReset = 1u; coreUint32 i = 0u - iTrapIndex; g_pGame->GetBulletManagerEnemy()->ForEachBulletTyped<cQuadBullet>([&](cQuadBullet* OUTPUT pBullet) { if((ABS(pBullet->GetPosition().x) < FOREGROUND_AREA.x * 1.3f) && (ABS(pBullet->GetPosition().y) < FOREGROUND_AREA.y * 1.3f)) { iReset = 0u; } if(i < 8u) // two blocks { const coreVector2 vBase = nPositionFunc(0, 4, coreVector2(0.0f, (i / 4u) ? -0.25f : 0.25f)); const coreVector2 vOffset = coreVector2(I_TO_F(i % 2u) - 0.5f, I_TO_F((i / 2u) % 2u) - 0.5f) * 0.11f * FOREGROUND_AREA; const coreFloat fHeight = SIN(m_fStageSubTime * (1.0f*PI) + I_TO_F(i / 4u) * (1.0f*PI)) * 10.0f; pBullet->SetPosition(coreVector3(vBase + vOffset + coreVector2(fHeight, 0.0f), 0.0f)); } else if(i < 24u) // rotator { const coreVector2 vBase = nPositionFunc(0, 17, coreVector2(0.0f,0.5f)); const coreVector2 vOffset = coreVector2::Direction(m_fStageSubTime * (0.5f*PI) + I_TO_F(i - 8u) * (0.1f*PI)) * 18.0f; pBullet->SetPosition(coreVector3(vBase + vOffset, 0.0f)); } else if(i < 34u) // snake { const coreVector2 vBase = nPositionFunc(0, 10, coreVector2(0.0f, 0.5f + 0.2f * (I_TO_F(i - 24u) - 4.5f))); const coreFloat fHeight = SIN(m_fStageSubTime * (-1.0f*PI) + I_TO_F(i - 24u) * (0.2f*PI)) * 10.0f; pBullet->SetPosition(coreVector3(vBase + coreVector2(fHeight, 0.0f), 0.0f)); } ++i; }); } if(g_pGame->IsTask()) { for(coreUintW i = 0u; i < CALOR_CHESTS; ++i) { coreObject3D* pChest = this->GetChest(i); if(!pChest->IsEnabled(CORE_OBJECT_ENABLE_MOVE)) continue; const coreVector2 vNewPos = pChest->GetPosition().xy() + vGlobalMove; pChest->SetPosition(coreVector3(vNewPos, 0.0f)); if(this->IsChestReady(i)) { STAGE_FOREACH_PLAYER(pPlayer, j) { const coreVector2 vDiff = pPlayer->GetPosition().xy() - pChest->GetPosition().xy(); if((ABS(vDiff.x) < pChest->GetCollisionRange().x) && (ABS(vDiff.y) < pChest->GetCollisionRange().y)) { this->DisableChest(i, true); if(++iChestCount == CALOR_CHESTS) { STAGE_BADGE(0u, BADGE_EASY, pChest->GetPosition()) } else { g_pGame->GetCombatText()->DrawProgress(iChestCount, CALOR_CHESTS, pChest->GetPosition()); g_pSpecialEffects->PlaySound(pChest->GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iChestCount, CALOR_CHESTS), SOUND_ITEM_01); } } }); } } } STAGE_FOREACH_PLAYER(pPlayer, i) { if(pPlayer->HasStatus(PLAYER_STATUS_REPAIRED)) { pPlayer->SetPosition(g_pGame->GetPlayer(1u - i)->GetPosition()); STATIC_ASSERT(GAME_PLAYERS == 2u) fSpeedFix = g_pGame->IsVersion(12u) ? 1.6f : 2.1f; } }); iHitStateOld = iHitState; iHitState = 0u; STAGE_COLL_PLAYER_BULLET(pPlayer, pBullet, vIntersection, bFirstHit, COLL_REF(vGlobalMove), COLL_REF(vGlobalDir), COLL_REF(fSpeedFix), COLL_REF(iHitState), COLL_REF(iHitStateOld)) { const coreInt32 iDamage = pBullet->GetDamage(); if(iDamage == 4) return; const coreVector2 vAxis = (iDamage == 6) ? coreVector2(0.0f,1.0f) : coreVector2(1.0f,0.0f); const coreVector2 vDiff = pPlayer->GetPosition().xy() - pBullet->GetPosition().xy(); const coreVector2 vRange = vAxis.yx() * 2.1f + vAxis * 1.9f; // create alcove in corner if((ABS(vDiff.x) < vRange.x) && (ABS(vDiff.y) < vRange.y)) { const coreVector2 vDiffOld = pPlayer->GetOldPos() - (pBullet->GetPosition().xy() - vGlobalMove); const coreVector2 vNorm = AlongCrossNormal(vDiffOld * vAxis); const coreVector2 vPos = pPlayer->GetPosition().xy() * vAxis.yx() + pBullet->GetPosition().xy() * vAxis + vNorm * 1.9f; pPlayer->SetPosition(coreVector3(vPos, 0.0f)); if(SameDirection(vGlobalDir, -vNorm)) { fSpeedFix = g_pGame->IsVersion(12u) ? 1.6f : 1.1f; } const coreUintW iIndex = g_pGame->GetPlayerIndex(pPlayer); ADD_BIT(iHitState, iIndex) if(!HAS_BIT(iHitStateOld, iIndex)) { if(pPlayer->IsNormal()) pPlayer->TakeDamage(5, ELEMENT_NEUTRAL, vIntersection.xy()); } } }); } g_pGame->GetBulletManagerEnemy()->ForEachBulletTyped<cQuadBullet>([&](cQuadBullet* OUTPUT pBullet) { coreVector2 vNewPos = pBullet->GetPosition().xy() + vGlobalMove; if(m_iStageSub < 6u) { if((vNewPos.x < -FOREGROUND_AREA.x * 1.4f) && (vGlobalMove.x < 0.0f)) vNewPos.x += FOREGROUND_AREA.x * 2.8f; else if((vNewPos.x > FOREGROUND_AREA.x * 1.4f) && (vGlobalMove.x > 0.0f)) vNewPos.x -= FOREGROUND_AREA.x * 2.8f; if((vNewPos.y < -FOREGROUND_AREA.y * 1.4f) && (vGlobalMove.y < 0.0f)) vNewPos.y += FOREGROUND_AREA.y * 2.8f; else if((vNewPos.y > FOREGROUND_AREA.y * 1.4f) && (vGlobalMove.y > 0.0f)) vNewPos.y -= FOREGROUND_AREA.y * 2.8f; } pBullet->SetPosition(coreVector3(vNewPos, 0.0f)); pBullet->SetAnimation(0.2f * m_fStageTime); pBullet->SetAnimSpeed(0.0f); }); if(!pHelperBlue->HasStatus(HELPER_STATUS_DEAD)) { const coreVector2 vNewPos = pHelperBlue->GetPosition().xy() + vGlobalMove; pHelperBlue->SetPosition(coreVector3(vNewPos, 0.0f)); STAGE_FOREACH_PLAYER(pPlayer, i) { const coreVector2 vDiff = pPlayer->GetPosition().xy() - vNewPos; if(vDiff.LengthSq() < POW2(6.0f)) { pSquad1->ClearEnemies(true); pHelperBlue->Kill(true); pHelperCyan->Kill(true); this->DisableAim(true); } }); } if(!pHelperCyan->HasStatus(HELPER_STATUS_DEAD)) { const coreVector2 vNewPos = pHelperCyan->GetPosition().xy() + vGlobalMove; pHelperCyan->SetPosition(coreVector3(vNewPos, 0.0f)); } if(STAGE_BEGINNING) g_pEnvironment->SetTargetSpeedLerp(2.0f, 10.0f); if(g_pGame->IsEasy()) STAGE_WAVE(2u, "6-3", {50.0f, 70.0f, 95.0f, 115.0f, 225.0f}) // DREIUNDDREISSIG else STAGE_WAVE(2u, "6-3", {45.0f, 65.0f, 90.0f, 110.0f, 220.0f}) }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cMinerEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cQuadBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 2u}) { g_pGame->KillHelpers(); for(coreUintW i = 0u; i < CALOR_CHESTS; ++i) this->DisableChest(i, false); this->DisableAim(false); STAGE_FINISH_NOW }); // ################################################################ // change background appearance STAGE_MAIN({TAKE_ALWAYS, 3u, 4u}) { g_pEnvironment->SetTargetSpeedNow(2.0f); STAGE_FINISH_NOW }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 3u}) { STAGE_FINISH_PLAY }); // ################################################################ // stick to one side and stay there // - side movement of galaga groups helps to attack them even when they attack directly // - splitting side-groups to 4-4-4-4 (instead of 8-8) makes it easier to prevent getting hit // - if you switch at the lat moment on the side-moving enemies and keep shooting, you have to survive, the cross-point should not move (beware of coop) // - enemies cannot move too quickly in the direction of flight, you don't notice them because of the fast background // - did not work: pang (boring + slow), flip speed (too unpredictable crashing into stuff), rotate (there was no meaningful mechanic, just visual, player-controlled needs incentive and this is only done by shooting along border) // - the first group should feel like a dog-fight // - collecting items as a task is difficult because there is one degree of freedom missing and you have to wait until the items fall on your axis // - in galaga, only the bottom enemies shoot, but ignoring it here creates a better bullet-pattern // TASK: destroy the UFO // TASK: reach the top, while on full speed, with back-fire push // ACHIEVEMENT: destroy all regular enemies while shooting down // TODO 5: badge: order on first skeet-shooting // TODO 5: badge: something with/during the helper at the end // TODO 1: hardmode: direction changes all the time // TODO 1: hardmode: you get tossed up on a regular basis STAGE_MAIN({TAKE_ALWAYS, 3u}) { STAGE_ADD_PATH(pPath1) { pPath1->Reserve(4u); pPath1->AddNode(coreVector2( 0.2f, 1.3f), coreVector2(0.0f,-1.0f)); pPath1->AddNode(coreVector2(-0.7f, 0.0f), coreVector2(0.0f,-1.0f)); pPath1->AddNode(coreVector2(-0.45f,-0.25f), coreVector2(1.0f, 0.0f)); pPath1->AddNode(coreVector2(-0.2f, 0.0f), coreVector2(0.0f, 1.0f)); pPath1->Refine(); }); STAGE_ADD_PATH(pPath2) { pPath2->Reserve(6u); pPath2->AddNode(coreVector2(-1.3f, -0.8f), coreVector2( 1.0f, 0.0f)); pPath2->AddNode(coreVector2(-0.2f, 0.0f), coreVector2( 0.0f, 1.0f)); pPath2->AddNode(coreVector2(-0.45f, 0.25f), coreVector2(-1.0f, 0.0f)); pPath2->AddNode(coreVector2(-0.7f, 0.0f), coreVector2( 0.0f,-1.0f)); pPath2->AddNode(coreVector2(-0.45f,-0.25f), coreVector2( 1.0f, 0.0f)); pPath2->AddNode(coreVector2(-0.2f, 0.0f), coreVector2( 0.0f, 1.0f)); pPath2->Refine(); }); STAGE_ADD_PATH(pPath3) { pPath3->Reserve(6u); pPath3->AddNode (coreVector2(-1.0f, 1.3f), coreVector2(0.0f,-1.0f)); pPath3->AddNodes(coreVector2(-1.0f,-1.0f), coreVector2(0.0f,-1.0f), coreVector2(1.0f,0.0f)); pPath3->AddNodes(coreVector2( 1.0f,-1.0f), coreVector2(1.0f, 0.0f), coreVector2(0.0f,1.0f)); pPath3->AddNode (coreVector2( 1.0f, 1.3f), coreVector2(0.0f, 1.0f)); pPath3->Refine(); }); STAGE_ADD_PATH(pPath4) { pPath4->Reserve(2u); pPath4->AddNode(coreVector2(0.0f, 1.4f), coreVector2(0.0f,-1.0f)); pPath4->AddNode(coreVector2(0.0f,-1.4f), coreVector2(0.0f,-1.0f)); pPath4->Refine(); }); STAGE_ADD_PATH(pPath5) { pPath5->Reserve(11u); pPath5->AddNode (coreVector2(-1.2f, 0.6f), coreVector2( 1.0f, 0.0f), 0.5f); pPath5->AddNode (coreVector2( 0.2f, 0.4f), coreVector2( 1.0f, 0.0f), 2.0f); pPath5->AddNode (coreVector2( 0.4f, 0.6f), coreVector2( 0.0f, 1.0f), 1.5f); pPath5->AddNode (coreVector2( 0.2f, 0.8f), coreVector2(-1.0f, 0.0f), 1.0f); pPath5->AddNode (coreVector2( 0.0f, 0.6f), coreVector2( 0.0f,-1.0f), 0.5f); pPath5->AddNode (coreVector2( 0.0f, 0.5f), coreVector2( 0.0f,-1.0f), 0.0f); pPath5->AddNodes(coreVector2( 0.0f,-1.1f), coreVector2( 0.0f,-1.0f), coreVector2( 0.0f, 1.0f), 5.0f, 1.0f); pPath5->AddNodes(coreVector2(-0.2f,-0.6f), coreVector2(-1.0f, 1.0f).Normalized(), coreVector2( 1.0f, 0.0f), 0.0f, 1.0f); pPath5->AddNode (coreVector2( 1.2f,-0.4f), coreVector2( 1.0f, 0.0f), 3.0f); pPath5->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cArrowEnemy, 104u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.55f); pEnemy->Configure(30, COLOR_SHIP_RED); pEnemy->AddStatus(ENEMY_STATUS_DAMAGING); }); }); STAGE_ADD_SQUAD(pSquad2, cUfoEnemy, 1u) { STAGE_FOREACH_ENEMY_ALL(pSquad2, pEnemy, i) { pEnemy->SetSize (coreVector3(3.0f,1.0f,1.0f)); pEnemy->Configure(30 + 10, COLOR_SHIP_BLACK, false, true); pEnemy->AddStatus(ENEMY_STATUS_WORTHLESS); }); }); STAGE_GET_START(37u + 2u * GAME_PLAYERS) STAGE_GET_VEC2_ARRAY (avForce, 16u) STAGE_GET_VEC2_ARRAY (avTurn, GAME_PLAYERS) STAGE_GET_FLOAT_ARRAY(afBoost, 2u) STAGE_GET_FLOAT (fOldSpeed, fOldSpeed = g_pEnvironment->GetSpeed()) STAGE_GET_UINT (iPushBack, iPushBack = 1u) STAGE_GET_UINT (iFrontShot) STAGE_GET_END if(pSquad1->IsFinished()) { if(STAGE_SUB( 1u)) STAGE_RESURRECT(pSquad1, 0u, 11u) else if(STAGE_SUB( 2u)) STAGE_RESURRECT(pSquad1, 20u, 35u) else if(STAGE_SUB( 3u)) STAGE_RESURRECT(pSquad1, 36u, 51u) else if(STAGE_SUB( 4u)) STAGE_RESURRECT(pSquad1, 64u, 79u) // # else if(STAGE_SUB( 5u)) STAGE_RESURRECT(pSquad1, 52u, 63u) // # else if(STAGE_SUB( 6u)) STAGE_RESURRECT(pSquad1, 12u, 12u) else if(STAGE_SUB( 7u)) STAGE_RESURRECT(pSquad1, 13u, 13u) else if(STAGE_SUB( 8u)) STAGE_RESURRECT(pSquad1, 14u, 15u) else if(STAGE_SUB( 9u)) STAGE_RESURRECT(pSquad1, 16u, 17u) else if(STAGE_SUB(10u)) STAGE_RESURRECT(pSquad1, 18u, 19u) else if(STAGE_SUB(11u)) STAGE_RESURRECT(pSquad1, 80u, 87u) else if(STAGE_SUB(12u)) STAGE_RESURRECT(pSquad1, 88u, 103u) else if(STAGE_SUB(13u)) { STAGE_DELAY_START_CLEAR if(!iFrontShot) STAGE_BADGE(3u, BADGE_ACHIEVEMENT, g_pGame->FindPlayerDual(0u)->GetPosition()) } if(m_iStageSub == 2u) g_pGame->GetBulletManagerEnemy()->ClearBullets(true); else if(m_iStageSub == 3u) STAGE_RESURRECT(pSquad2, 0u, 0u) if((m_iStageSub == 1u) || (m_iStageSub == 2u)) { g_pSpecialEffects->PlaySound(SPECIAL_RELATIVE, 2.0f, 1.0f, SOUND_EFFECT_FLY); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_SMALL, 250u); } } if(m_iStageSub >= 1u) afBoost[0] = MIN1(afBoost[0] + 0.35f * TIME); if(m_iStageSub >= 2u) afBoost[1] = MIN1(afBoost[1] + 0.35f * TIME); const coreFloat fNewSpeed = 16.0f; const coreFloat fCurSpeed = LERPB(fOldSpeed, fNewSpeed, afBoost[0]) * 0.5f + LERPB(fOldSpeed, fNewSpeed, afBoost[1]) * 0.5f; if(m_iStageSub < 13u) { g_pEnvironment->SetTargetSpeedNow(fCurSpeed); } else { cHelper* pHelper = g_pGame->GetHelper(ELEMENT_YELLOW); if(STAGE_BEGINNING2) { pHelper->Resurrect(false); } const coreFloat fShakeDist = pPath5->TranslateDistance(7u, 0.0f); const coreFloat fTotalDist = pPath5->GetTotalDistance(); const coreFloat fRotaDist = fShakeDist + 1.0f; const coreFloat fTime = MIN(DelayTime(m_fStageSubTime, fShakeDist, 1.0f), fTotalDist); pHelper->SetPosition(coreVector3(pPath5->CalcPosition(fTime) * FOREGROUND_AREA, 0.0f)); if(STAGE_SUBTIME_POINT(fShakeDist)) { g_pEnvironment->SetTargetSpeedNow(fOldSpeed); g_pSpecialEffects->CreateSplashColor(pHelper->GetPosition(), SPECIAL_SPLASH_BIG, COLOR_ENERGY_YELLOW); g_pSpecialEffects->ShakeScreen(SPECIAL_SHAKE_BIG); g_pSpecialEffects->PlaySound(pHelper->GetPosition(), 0.5f, 1.5f, SOUND_EFFECT_SHAKE_01); g_pSpecialEffects->PlaySound(pHelper->GetPosition(), 1.0f, 0.7f, SOUND_EFFECT_SHAKE_02); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_BIG, 250u); iPushBack = 0u; STAGE_FOREACH_PLAYER(pPlayer, i) { pPlayer->ApplyForce(coreVector2(0.0f,300.0f)); avTurn[i] = pPlayer->GetDirection().xy(); }); } if(STAGE_SUBTIME_BETWEEN(fShakeDist, fRotaDist)) { const coreVector2 vRota = coreVector2::Direction(LERPB(0.0f*PI, 4.0f*PI, STEP(fShakeDist, fRotaDist, m_fStageSubTime))); STAGE_FOREACH_PLAYER(pPlayer, i) { pPlayer->SetDirection(coreVector3(MapToAxis(avTurn[i], vRota), 0.0f)); pPlayer->AddStatus (PLAYER_STATUS_NO_INPUT_TURN); }); } else if(STAGE_SUBTIME_POINT(fRotaDist)) { STAGE_FOREACH_PLAYER(pPlayer, i) { pPlayer->SetDirection(coreVector3(avTurn[i], 0.0f)); pPlayer->RemoveStatus(PLAYER_STATUS_NO_INPUT_TURN); }); } if(fTime >= fTotalDist) { pHelper->Kill(false); STAGE_DELAY_END } } if(iPushBack) { const coreFloat fEnvSpeed = g_pEnvironment->GetSpeed(); const coreVector2 vEnvDir = g_pEnvironment->GetDirection(); g_pSpecialEffects->CreateGust(STEP(fOldSpeed, fNewSpeed, ABS(fEnvSpeed)), vEnvDir.Angle()); const coreFloat fValue = (afBoost[0] ? LERPB(0.5f, -0.4f, afBoost[0]) : 0.0f) + (afBoost[1] ? LERPB(0.3f, -0.6f, afBoost[1]) : 0.0f); const coreVector2 vPush = vEnvDir * (fValue * 60.0f * TIME); // strong enough to hide Y movement (forward leaning) const coreVector2 vReverse = vEnvDir * MAX0(fValue * -12.0f * TIME); STAGE_FOREACH_PLAYER(pPlayer, i) { const coreBool bBackFire = (pPlayer->GetWeapon(0u)->GetCooldown() && SameDirection(pPlayer->GetDirection().xy(), coreVector2(0.0f,-1.0f))); pPlayer->SetPosition(pPlayer->GetPosition() + coreVector3(bBackFire ? vReverse : vPush, 0.0f)); }); } const coreVector4 vArea = PLAYER_AREA_DEFAULT * RCP(iPushBack ? LERPH3(1.0f, PLAYER_AREA_FACTOR, MIN1(m_fStageTime * 3.0f)) : 1.0f); // move player a bit away from the border const coreFloat fExaust = MAX0(SIN(LERPB(0.0f*PI, 1.0f*PI, afBoost[0])) * 0.5f + SIN(LERPB(0.0f*PI, 1.0f*PI, afBoost[1])) * 0.5f); STAGE_FOREACH_PLAYER_ALL(pPlayer, i) { pPlayer->SetArea (vArea); pPlayer->SetThrust(fExaust); if(fExaust > 0.05f) { pPlayer->SetDirection(coreVector3(0.0f,1.0f,0.0f)); pPlayer->AddStatus (PLAYER_STATUS_NO_INPUT_TURN); pPlayer->ShowArrow(0u); } else if(iPushBack) { pPlayer->RemoveStatus(PLAYER_STATUS_NO_INPUT_TURN); } }); STAGE_FOREACH_PLAYER(pPlayer, i) { if(g_pGame->IsTask()) { if((pPlayer->GetPosition().y >= vArea.w) && (afBoost[1] >= 1.0f)) { STAGE_BADGE(1u, BADGE_NORMAL, pPlayer->GetPosition()) } } }); const coreFloat fMoveSpeed = g_pGame->IsEasy() ? 0.7f : 1.0f; STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { STAGE_LIFETIME(pEnemy, ((i < 12u) ? 0.9f : ((i < 20u) ? 0.7f : ((i < 52u) ? 1.5f : ((i < 64u) ? 1.4f : 1.1f)))) * (((i >= 12u && i < 20u) || (i >= 52u)) ? fMoveSpeed : 1.0f), (i < 12u) ? (3.0f + 0.7f * I_TO_F(i)) : ((i < 20u) ? 0.0f : ((i < 52u) ? (0.2f * I_TO_F((i - 20u) % 8u) + ((i < 36u) ? 4.0f : 0.0f)) : ((i < 64u) ? (0.2f * I_TO_F(i - 52u)) : ((i < 80u) ? (0.2f * I_TO_F((i - 64u) % 16u) + 1.2f * I_TO_F(MIN((i - 64u) / 4u, 2u))) : ((i < 88u) ? (0.2f * I_TO_F(i - 80u)) : ((i < 96u) ? 0.0f : 0.1f))))))) if(i < 12u) { STAGE_REPEAT(pPath4->GetTotalDistance()) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((I_TO_F((i * 5u + 4u) % 12u) - 5.5f) * 0.15f, 0.0f); pEnemy->DefaultMovePath(pPath4, vFactor, vOffset * vFactor, fLifeTime); pEnemy->SetDirection (coreVector3(0.0f,1.0f,0.0f)); } else if(i < 20u) { STAGE_REPEAT(pPath4->GetTotalDistance()) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((I_TO_F((((i - 12u) % 4u) * 2u + 1u) % 5u) - 1.5f) * 0.3f, 0.0f); pEnemy->DefaultMovePath(pPath4, vFactor, vOffset * vFactor, fLifeTime); if(i < 16u) {} else if(i < 18u) pEnemy->Rotate90 (); else if(i < 20u) pEnemy->Rotate270(); pEnemy->InvertX(); if(STAGE_TICK_LIFETIME(15.0f, 0.0f)) { const coreVector2 vPos = pEnemy->GetPosition ().xy(); const coreVector2 vDir = pEnemy->GetDirection().xy().Rotated90(); g_pGame->GetBulletManagerEnemy()->AddBullet<cSpearBullet>(5, 1.0f, pSquad1->GetEnemy(0u), vPos, vDir)->ChangeSize(1.7f); g_pGame->GetBulletManagerEnemy()->AddBullet<cSpearBullet>(5, 1.0f, pSquad1->GetEnemy(0u), vPos, -vDir)->ChangeSize(1.7f); g_pSpecialEffects->CreateSplashColor(coreVector3(vPos, 0.0f), 10.0f, 1u, COLOR_ENERGY_YELLOW); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } } else if(i < 52u) { const coreSpline2* pPath = (i < 36u) ? pPath1 : pPath2; const coreVector2 vFactor = coreVector2((((i - 20u) % 16u) < 8u) ? 1.0f : -1.0f, 1.0f); const coreVector2 vOffset = coreVector2(0.0f,0.0f); if(pEnemy->DefaultMovePath(pPath, vFactor, vOffset * vFactor, fLifeTime)) { const coreFloat fTime = MIN1((fLifeTime - pPath->GetTotalDistance()) * 1.5f); const coreVector2 vMove = coreVector2(0.2f * SIN(m_fStageSubTime * (0.5f*PI)), 0.0f); const coreVector2 vFrom = pEnemy->GetPosition().xy(); const coreVector2 vTo = (coreVector2(I_TO_F(((i - 20u) % 4u) + (((i >= 36u) && (((i - 20u) / 2u) % 2u)) ? 2u : 1u)) * 0.15f, (((i - 20u) % 8u) < 4u) ? 0.6f : 0.4f) * vFactor.InvertedX() + vMove) * FOREGROUND_AREA; pEnemy->SetPosition(coreVector3(LERPH3(vFrom.x, vTo.x, fTime), LERP(vFrom.y, vTo.y, fTime), 0.0f)); } } else if(i < 80u) { STAGE_REPEAT(pPath3->GetTotalDistance()) const coreVector2 vFactor = coreVector2(((i < 64u) || ((i >= 68u) && (i < 72u))) ? 1.0f : -1.0f, 1.0f); const coreVector2 vOffset = coreVector2(0.0f,0.0f); pEnemy->DefaultMovePath(pPath3, vFactor, vOffset * vFactor, fLifeTime); const coreVector2 vPos = pEnemy->GetPosition().xy(); const coreVector2 vNorm = AlongCrossNormal(-vPos); const coreFloat fValue = coreVector2::Dot(vPos, vNorm.Rotated90()) / (FOREGROUND_AREA.x * 1.0f); const coreFloat fPower = ((i >= 64u) && (vPos.y <= 0.0f)) ? 0.0f : 12.0f; const coreFloat fJump = fPower * ABS(SIN(fValue * (1.0f*PI))); pEnemy->SetPosition(coreVector3(vPos + vNorm * fJump, 0.0f)); if(i < 64u) pEnemy->DefaultRotate(fLifeTime * -3.0f); } else { coreVector2& vForce = avForce[(i - 80u) % 16u]; if(STAGE_TAKEOFF) { pEnemy->SetPosition(coreVector3(-1.2f,-0.9f,0.0f) * FOREGROUND_AREA3); vForce = coreVector2::Direction(I_TO_F((i - 80u) % 8u) / 8.0f * (-0.1f*PI) - ((i < 88u) ? (0.1f*PI) : (0.05f*PI))) * 1.65f; if(i % 2u) { pEnemy->InvertX(); vForce = vForce.InvertedX(); } } vForce.y -= 1.0f * fMoveSpeed * TIME; coreVector2 vNewPos = pEnemy->GetPosition().xy() + vForce * FOREGROUND_AREA * (1.5f * fMoveSpeed * TIME); if((vNewPos.x < -FOREGROUND_AREA.x * 1.1f) && (vForce.x < 0.0f)) {vNewPos.x -= 2.0f * (vNewPos.x + FOREGROUND_AREA.x * 1.1f); vForce.x = ABS(vForce.x);} else if((vNewPos.x > FOREGROUND_AREA.x * 1.1f) && (vForce.x > 0.0f)) {vNewPos.x -= 2.0f * (vNewPos.x - FOREGROUND_AREA.x * 1.1f); vForce.x = -ABS(vForce.x);} if((vNewPos.y < -FOREGROUND_AREA.y * 1.1f) && (vForce.y < 0.0f)) {vNewPos.y -= 2.0f * (vNewPos.y + FOREGROUND_AREA.y * 1.1f); vForce.y = ABS(vForce.y);} pEnemy->SetPosition (coreVector3(vNewPos, 0.0f)); pEnemy->SetDirection(coreVector3(vForce.Normalized(), 0.0f)); } }); STAGE_FOREACH_ENEMY(pSquad2, pEnemy, i) { STAGE_LIFETIME(pEnemy, 0.7f, 2.5f) const coreVector2 vFactor = coreVector2(1.0f,1.2f); const coreVector2 vOffset = coreVector2(0.9f,0.0f); if(pEnemy->DefaultMovePath(pPath4, vFactor, vOffset * vFactor, fLifeTime)) pEnemy->Kill(false); pEnemy->SetDirection(coreVector3(1.0f,0.0f,0.0f)); pEnemy->Rotate270(); if(g_pGame->IsTask() && pEnemy->ReachedDeath()) STAGE_BADGE(0u, BADGE_EASY, pEnemy->GetPosition()) }); if((m_iStageSub == 1u) && STAGE_TIME_AFTER(1.0f) && STAGE_TICK_FREE2(3.5f, 0.0f)) { const coreFloat fSide = (s_iTick % 2u) ? -1.0f : 1.0f; const coreFloat fHeight = (s_iTick % 2u) ? -0.95f : -1.0f; const coreVector2 vPos = coreVector2(fSide * -1.3f, fHeight) * FOREGROUND_AREA; const coreVector2 vDir = coreVector2(fSide, 0.0f); g_pGame->GetBulletManagerEnemy()->AddBullet<cSpearBullet>(5, 1.0f, pSquad1->GetEnemy(0u), vPos, vDir)->ChangeSize(1.7f); } const coreBool bActive = g_pGame->IsEasy() ? ((m_iStageSub == 2u) || (m_iStageSub == 3u)) : ((m_iStageSub == 1u) || (m_iStageSub == 2u) || (m_iStageSub == 3u) || (m_iStageSub == 5u)); const coreBool bDelay = (m_iStageSub == 2u) ? STAGE_SUBTIME_AFTER(4.0f / 1.5f) : STAGE_TIME_AFTER(2.0f); const coreFloat fSpeed = ((m_iStageSub == 5u) ? 7.0f : 3.5f) * (g_pGame->IsEasy() ? 0.5f : 1.0f); if(bActive && bDelay && STAGE_TICK_FREE2(fSpeed, 0.0f)) { const coreUintW iNumEnemies = pSquad1->GetNumEnemiesAlive(); if(iNumEnemies) { coreUintW iShooter = (s_iTick * ((iNumEnemies == 11u) ? 13u : 11u)) % iNumEnemies; STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { if(iShooter--) return; const coreVector2 vPos = pEnemy->GetPosition().xy(); const coreVector2 vDir = (m_iStageSub == 2u) ? coreVector2(0.0f,-1.0f) : pEnemy->AimAtPlayerDual(i % 2u).Normalized(); if(vPos.y > 0.0f) { g_pGame->GetBulletManagerEnemy()->AddBullet<cSpearBullet>(5, 1.0f, pEnemy, vPos, vDir)->ChangeSize(1.7f); g_pSpecialEffects->CreateSplashColor(coreVector3(vPos, 0.0f), 10.0f, 3u, COLOR_ENERGY_YELLOW); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } }); } } STAGE_COLL_ENEMY_BULLET(pEnemy, pBullet, vIntersection, bFirstHit, COLL_THIS, COLL_REF(iFrontShot)) { if(!SameDirection90(pBullet->GetFlyDir(), coreVector2(0.0f,-1.0f))) { iFrontShot += 1u; STAGE_FAILTROPHY } }); STAGE_WAVE(3u, "6-4", {80.0f, 120.0f, 160.0f, 200.0f, 400.0f}) // VIERUNDDREISSIG }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cArrowEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cSpearBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 3u}) { g_pGame->KillHelpers(); STAGE_FOREACH_PLAYER_ALL(pPlayer, i) { pPlayer->SetArea (PLAYER_AREA_DEFAULT); pPlayer->SetThrust (0.0f); pPlayer->RemoveStatus(PLAYER_STATUS_NO_INPUT_TURN); }); STAGE_FINISH_NOW }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 4u}) { STAGE_FINISH_PLAY }); // ################################################################ // smaller stone is dragged by player with chain // - 2: should align with swing-direction, which is easier to hit because swing-direction is against fly-direction // - 3: clustered enemies should feel like hitting a brick-building with a wrecking ball // - 8: removing bullet-carpet is really satisfying (feedback) // - bullet-carpet pattern needs to make vertically moving enemies visible within // - bullet-carpet needs to move slower, in relation to swing-speed // - enemies within bullet-carpet should not shoot // - temporarily loose ability to remove bullet-carpet to scare and pressure player shortly // - enemies only come from above, to improve the feeling of facing an army // - attacks should stay local to enemies, the difficulty is attacking and navigating around enemies without getting hit from their local attacks // - (old: in coop, captured enemies need to be usable on the whole screen, e.g. transfer to other player) // TASK: do enough damage to Zeroth before time runs out // TASK: attack specific targets with your normal weapon // ACHIEVEMENT: in coop only capture enemies with a single player // COOP: dirrection of both morning stars is different // TODO 1: hardmode: enemies keep attacking while being swung // TODO 1: hardmode: permanent bullets from top // TODO 5: badge: destroy N bullets with morning star (+ display, but only at the end) // TODO 5: badge: some enemies are paired (swing one on the other), only once STAGE_MAIN({TAKE_ALWAYS, 4u}) { // TODO 1: only cleanup for(coreUintW i = 0u; i < CALOR_STARS; ++i) { if(m_apStarOwner[i] && (m_apStarOwner[i] != &m_Zeroth) && !m_iStarSwing) { const coreVector2 vDiff = m_apStarOwner[i]->GetPosition().xy() - this->GetStar(i)->GetPosition().xy(); if(vDiff.LengthSq() >= POW2(CALOR_CHAIN_CONSTRAINT1)) { m_Boulder.Resurrect(); m_Boulder.SetPosition(coreVector3(0.0f,-1.25f,0.0f) * FOREGROUND_AREA3); this->CatchObject(i, &m_Boulder); this->StartSwing(1.0f); g_pSpecialEffects->CreateSplashColor(coreVector3(0.0f,-1.1f,0.0f) * FOREGROUND_AREA3, SPECIAL_SPLASH_SMALL, COLOR_ENERGY_WHITE * 0.8f); g_pSpecialEffects->ShakeScreen(SPECIAL_SHAKE_SMALL); g_pSpecialEffects->PlaySound(coreVector3(0.0f,-1.1f,0.0f) * FOREGROUND_AREA3, 0.5f, 1.5f, SOUND_EFFECT_SHAKE_01); g_pSpecialEffects->RumblePlayer(NULL, SPECIAL_RUMBLE_BIG, 250u); g_pEnvironment->SetTargetSpeedLerp(6.0f, 10.0f); } } } STAGE_ADD_PATH(pPath1) { pPath1->Reserve(2u); pPath1->AddNode(coreVector2(0.0f, 1.5f), coreVector2(0.0f,-1.0f)); pPath1->AddNode(coreVector2(0.0f,-1.5f), coreVector2(0.0f,-1.0f)); pPath1->Refine(); }); STAGE_ADD_PATH(pPath2) { pPath2->Reserve(2u); pPath2->AddNode(coreVector2(0.0f,1.5f), coreVector2(0.0f,-1.0f)); pPath2->AddStop(coreVector2(0.0f,0.8f), coreVector2(0.0f,-1.0f)); pPath2->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cScoutEnemy, 63u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.3f); pEnemy->Configure(4, COLOR_SHIP_MAGENTA); pEnemy->AddStatus(ENEMY_STATUS_INVINCIBLE); }); }); STAGE_ADD_SQUAD(pSquad2, cWarriorEnemy, 9u) { STAGE_FOREACH_ENEMY_ALL(pSquad2, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 2.0f); pEnemy->Configure(30, COLOR_SHIP_CYAN); pEnemy->AddStatus(ENEMY_STATUS_INVINCIBLE); }); }); STAGE_GET_START(11u) STAGE_GET_VEC2 (vHelperMove) STAGE_GET_VEC2 (vBullPos) STAGE_GET_VEC2 (vBullMove) STAGE_GET_VEC2 (vBullMoveReal) STAGE_GET_FLOAT(fBullTime) STAGE_GET_UINT (iBullCount) STAGE_GET_UINT (iAnyCatch) STAGE_GET_END const cShip* pCatchObject1 = this->GetCatchObject(0u); const cShip* pCatchObject2 = this->GetCatchObject(1u); STATIC_ASSERT(CALOR_STARS == 2u) if(pCatchObject1 && (pCatchObject1 != g_pGame->GetPlayer(0u))) ADD_BIT(iAnyCatch, 0u) if(pCatchObject2 && (pCatchObject2 != g_pGame->GetPlayer(1u))) ADD_BIT(iAnyCatch, 1u) if(!g_pGame->IsMulti() || (coreMath::PopCount(iAnyCatch) > 1u)) STAGE_FAILTROPHY coreBool bLastCatch = false; if(pSquad1->IsFinished() && (pSquad2->GetNumEnemiesAlive() == 1u) && m_Boulder.HasStatus(ENEMY_STATUS_DEAD)) { STAGE_FOREACH_ENEMY(pSquad2, pEnemy, i) { if((pEnemy == pCatchObject1) || (pEnemy == pCatchObject2)) bLastCatch = true; }); } if(STAGE_CLEARED || bLastCatch) { if(STAGE_SUB( 1u)) STAGE_RESURRECT(pSquad1, 0u, 3u) else if(STAGE_SUB( 2u)) STAGE_RESURRECT(pSquad1, 4u, 13u) else if(STAGE_SUB( 3u)) STAGE_RESURRECT(pSquad1, 14u, 27u) else if(STAGE_SUB( 4u)) STAGE_RESURRECT(pSquad1, 28u, 34u) else if(STAGE_SUB( 5u)) STAGE_RESURRECT(pSquad2, 0u, 0u) else if(STAGE_SUB( 6u)) STAGE_RESURRECT(pSquad1, 35u, 44u) else if(STAGE_SUB( 7u)) STAGE_RESURRECT(pSquad2, 1u, 4u) else if(STAGE_SUB( 8u)) STAGE_RESURRECT(pSquad2, 5u, 5u) else if(STAGE_SUB( 9u)) STAGE_RESURRECT(pSquad1, 55u, 62u) else if(STAGE_SUB(10u)) STAGE_RESURRECT(pSquad1, 45u, 54u) else if(STAGE_SUB(11u)) STAGE_RESURRECT(pSquad2, 6u, 6u) else if(STAGE_SUB(12u)) STAGE_RESURRECT(pSquad2, 7u, 7u) else if(STAGE_SUB(13u)) STAGE_RESURRECT(pSquad2, 8u, 8u) else if(STAGE_SUB(14u)) { if(g_pGame->IsMulti() && (coreMath::PopCount(iAnyCatch) == 1u)) { STAGE_BADGE(3u, BADGE_ACHIEVEMENT, g_pGame->FindPlayerDual(0u)->GetPosition()) } // destroy possible remaining enemy pSquad2->ClearEnemies(true); } } cHelper* pHelper = g_pGame->GetHelper(ELEMENT_PURPLE); if((m_iStageSub == 6u) && STAGE_BEGINNING2) { pHelper->Resurrect(false); pHelper->SetPosition(m_Boulder.GetPosition()); vHelperMove = (m_apStarOwner[0]->GetPosition().xy() - this->GetStar(0u)->GetPosition().xy()).Rotated90().Normalized() * 140.0f; } if(!pHelper->HasStatus(HELPER_STATUS_DEAD)) { const coreVector2 vPos = pHelper->GetPosition().xy() + vHelperMove * TIME; if(!g_pForeground->IsVisiblePoint(vPos / FOREGROUND_AREA, 1.2f)) pHelper->Kill(false); pHelper->SetPosition(coreVector3(vPos, 0.0f)); } if(STAGE_TIME_POINT(4.0f)) { m_Zeroth.ResurrectIntro(); } if(g_pGame->IsTask()) { if(STAGE_BEGINNING2) { if(m_iStageSub == 3u) {fBullTime = 2.0f; vBullPos = coreVector2(-0.9f,-1.3f); vBullMove = coreVector2( 0.0f, 1.0f);} else if(m_iStageSub == 6u) {fBullTime = 2.0f; vBullPos = coreVector2( 1.3f,-0.9f); vBullMove = coreVector2(-1.0f, 0.0f);} else if(m_iStageSub == 7u) {fBullTime = 3.0f; vBullPos = coreVector2( 0.9f,-1.3f); vBullMove = coreVector2( 0.0f, 1.0f);} else if(m_iStageSub == 10u) {fBullTime = 10.0f; vBullPos = coreVector2(-1.3f, 0.9f); vBullMove = coreVector2( 1.0f, 0.0f);} } if(fBullTime > 0.0f) { const coreFloat fOldTime = fBullTime; fBullTime -= 1.0f * TIME; if(InBetween(0.0f, fBullTime, fOldTime)) { this->DisableBull(true); this->EnableBull(); m_Bull.SetPosition(coreVector3(vBullPos * FOREGROUND_AREA, 0.0f)); vBullMoveReal = vBullMove; } } if(!m_Bull.HasStatus(ENEMY_STATUS_DEAD)) { const coreVector2 vNewPos = m_Bull.GetPosition().xy() + vBullMoveReal * (30.0f * TIME); m_Bull.SetPosition(coreVector3(vNewPos, 0.0f)); if(!g_pForeground->IsVisiblePoint(vNewPos / FOREGROUND_AREA, 1.3f) || m_Bull.WasDamaged()) { this->DisableBull(false); } if(m_Bull.WasDamaged()) { if(++iBullCount == 4u) { STAGE_BADGE(1u, BADGE_NORMAL, m_Bull.GetPosition()) } else { g_pGame->GetCombatText()->DrawProgress(iBullCount, 4u, m_Bull.GetPosition()); g_pSpecialEffects->PlaySound(m_Bull.GetPosition(), 1.0f, SPECIAL_SOUND_PROGRESS(iBullCount, 4u), SOUND_ITEM_02); } g_pSpecialEffects->MacroExplosionColorSmall(m_Bull.GetPosition(), COLOR_ENERGY_ORANGE); g_pSpecialEffects->PlaySound(m_Bull.GetPosition(), 1.0f, 1.0f, SOUND_ENEMY_EXPLOSION_01); } } } if(!m_Zeroth.HasStatus(ENEMY_STATUS_DEAD)) { const coreFloat fPercent = I_TO_F(m_Zeroth.GetLostHealth ()) / 590.0f; // -10 const coreFloat fPrePercent = I_TO_F(m_Zeroth.GetPreLostHealth()) / 590.0f; for(coreUintW i = 1u; i < 5u; ++i) { if(InBetween(I_TO_F(i) * 0.2f, fPrePercent, fPercent)) { cPlayer* pPlayer = m_Zeroth.LastAttacker(); this->AddExtraScore(pPlayer, 200u, pPlayer->GetPosition()); } } if(fPercent >= 1.0f) { if(g_pGame->IsTask()) STAGE_BADGE(0u, BADGE_EASY, m_Zeroth.GetPosition()) m_Zeroth.HideTail(); } else if(fPercent >= 0.2f) { g_pGame->GetCombatText()->AttachMarker(0u, PRINT("%.0f%%", CEIL((1.0f - fPercent) * 100.0f)), m_Zeroth.GetPosition(), COLOR_MENU_INSIDE, false); } } STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { if((pEnemy == pCatchObject1) || (pEnemy == pCatchObject2)) return; STAGE_LIFETIME(pEnemy, 0.7f, (i < 4u) ? (0.2f * I_TO_F(i)) : ((i < 14u) ? (0.2f * I_TO_F((i - 4u) % 10u)) : ((i < 35u) ? (1.2f * I_TO_F(((i - 14u) / 7u) % 2u)) : ((i < 55u) ? (0.2f * I_TO_F((i - 35u) % 10u) + ((i < 45u) ? 0.0f : 5.0f)) : (0.4f * I_TO_F(i - 55u)))))) STAGE_REPEAT(pPath1->GetTotalDistance()) if(i < 14u) { const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((i < 4u) ? ((((i / 2u) % 2u) ? 0.9f : 0.7f) * ((i % 2u) ? 1.0f : -1.0f)) : ((I_TO_F(i - 4u) - 4.5f) * -0.15f), 0.0f); pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); } else if(i < 35u) { const coreUintW A = (i - 14u) % 7u; const coreUintW B = (i - 14u) / 7u; const coreVector2 vBase = coreVector2::Direction((I_TO_F(A) / 6.0f) * (2.0f*PI)) * (A ? 0.2f : 0.0f); const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2(((B % 2u) ? 0.5f : -0.5f) * ((i < 28u) ? 1.0f : COS(fLifeTime * 2.0f)), 0.0f) + vBase; pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); } else if(i < 55u) { const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((I_TO_F(((i - 35u) * 2u) % 5u) - 2.0f) * ((i < 45u) ? -0.4f : 0.4f), 0.0f); pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); } else if(i < 63u) { const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2((I_TO_F((i - 55u) % 4u) - 1.5f) * 0.5f + ((i < 59u) ? -0.125f : 0.125f), 0.0f); pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); pEnemy->Rotate90(); if(!(i % 2u)) pEnemy->InvertX(); } if((i < 45u || i >= 55u) && STAGE_TICK_LIFETIME(10.0f, 0.0f) && (!g_pGame->IsEasy() || (s_iTick < 3u))) { const coreVector2 vPos = pEnemy->GetPosition ().xy(); const coreVector2 vDir = pEnemy->GetDirection().xy(); g_pGame->GetBulletManagerEnemy()->AddBullet<cFlipBullet>(5, 1.4f, pEnemy, vPos, vDir)->ChangeSize(1.5f); g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } }); STAGE_FOREACH_ENEMY(pSquad2, pEnemy, i) { if((pEnemy == pCatchObject1) || (pEnemy == pCatchObject2)) return; STAGE_LIFETIME(pEnemy, 0.7f, (i < 1u) ? 0.0f : ((i < 6u) ? (0.8f * I_TO_F((i - 1u) % 4u)) : 0.5f)) const coreSpline2* pPath = (i == 6u) ? pPath2 : pPath1; if(pPath == pPath1) STAGE_REPEAT(pPath->GetTotalDistance()) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2(((i >= 1u && i < 6u) ? (((i - 1u) % 2u) ? -0.4f : 0.4f) : 0.0f) * ((i < 5u) ? 1.0f : COS(fLifeTime * 2.0f)), 0.0f); pEnemy->DefaultMovePath(pPath, vFactor, vOffset * vFactor, fLifeTime); if((i < 6u) && STAGE_TICK_LIFETIME(10.0f, 0.0f)) { const coreVector2 vPos = pEnemy->GetPosition().xy(); for(coreUintW j = 3u; j--; ) { if(g_pGame->IsEasy() && (j != 1u)) continue; const coreVector2 vDir = coreVector2::Direction(DEG_TO_RAD((I_TO_F(j) - 1.0f) * 20.0f) + (1.0f*PI)); g_pGame->GetBulletManagerEnemy()->AddBullet<cFlipBullet>(5, 1.4f, pEnemy, vPos, vDir)->ChangeSize(1.5f); } g_pSpecialEffects->PlaySound(coreVector3(vPos, 0.0f), 1.0f, 1.0f, SOUND_WEAPON_ENEMY); } }); if((m_iStageSub >= 10u) && (m_iStageSub <= 13u) && STAGE_TICK_FREE(g_pGame->IsEasy() ? 1.5f : 2.0f, 0.0f)) { cEnemy* pDummy = pSquad1->GetEnemy(0u); for(coreUintW j = 30u; j--; ) { if(g_pGame->IsEasy() && (((j + s_iTick * 2u) / 3u) % 2u)) continue; const coreVector2 vPos = coreVector2((I_TO_F(j) - 14.5f + ((s_iTick % 2u) ? -0.25f : 0.25f)) * 0.08f, (j % 2u) ? 1.2f : 1.22f) * FOREGROUND_AREA; const coreVector2 vDir = coreVector2(0.0f,-1.0f); g_pGame->GetBulletManagerEnemy()->AddBullet<cFlipBullet>(5, 0.5f, pDummy, vPos, vDir)->ChangeSize(1.5f); } } g_pGame->GetBulletManagerPlayer()->ForEachBullet([](cBullet* OUTPUT pBullet) // prevent annoying multi-bounce (sound) { if(pBullet->HasStatus(BULLET_STATUS_REFLECTED)) { pBullet->AddStatus(BULLET_STATUS_GHOST); } }); STAGE_WAVE(4u, "6-5", {70.0f, 105.0f, 140.0f, 175.0f, 350.0f}) // FÜNFUNDDREISSIG }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cScoutEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cWarriorEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cOrbBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cSpearBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cTriangleBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cFlipBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 4u}) { g_pGame->KillHelpers(); this->DisableBull(false); STAGE_FINISH_NOW }); #if defined(_P1_UNUSED_) // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 5u}) { STAGE_FINISH_PLAY }); // ################################################################ // keep shooting and charge it (astral chain) // - enemies must be positioned in such a way that you have to let go of the king // - first few orbs need to introduce player to orb-charge, and orb-attack (TODO 1: and orb-shrink if possible) // - orb must shrink fast enough, to make it meaningful // - enemies to the left and right of the king should not be too close, otherwise you will accidentally shoot them while dodging // TODO 1: force bullet order ? red more important ? // TODO 1: continuous sound to improve understanding grow<>shrink // TODO 1: turn sparks into waves when the ball is charged ? // TODO 1: beginnt sich ab nem bestimmten orb zu bewegen (vaus ball ?) ? // TODO 1: how to practically introduce orb shrinking ? through (light) movement ? // TODO 1: do not remove energy-effect on king after resurrection ? // TODO 1: maybe he is caught in an ice block // TODO 1: rotate (all balls) helper around enemy to highlight state // TODO 1: MAIN: task-check, helper, easy, hard idea, coop, regular score, extra score, badges, medal goal, juiciness (move, rota, muzzle, effects), auf boss übertragen (general, easy, coop), sound, attack size/count/speed, enemy size, object size, background rota/speed STAGE_MAIN({TAKE_ALWAYS, 5u}) { STAGE_ADD_PATH(pPath1) { pPath1->Reserve(2u); pPath1->AddNode(coreVector2(0.0f,1.3f), coreVector2(0.0f,-1.0f)); pPath1->AddStop(coreVector2(0.0f,0.5f), coreVector2(0.0f,-1.0f)); pPath1->Refine(); }); STAGE_ADD_PATH(pPath2) { pPath2->Reserve(2u); pPath2->AddNode(coreVector2(-1.2f,0.95f), coreVector2(1.0f,0.0f)); pPath2->AddStop(coreVector2( 0.0f,0.95f), coreVector2(1.0f,0.0f)); pPath2->Refine(); }); STAGE_ADD_SQUAD(pSquad1, cWarriorEnemy, 1u) { STAGE_FOREACH_ENEMY_ALL(pSquad1, pEnemy, i) { pEnemy->SetSize (coreVector3(1.0f,1.0f,1.0f) * 1.8f); pEnemy->Configure(200, COLOR_SHIP_GREY); pEnemy->AddStatus(ENEMY_STATUS_INVINCIBLE); pEnemy->Resurrect(); }); }); STAGE_ADD_SQUAD(pSquad2, cScoutEnemy, 16u) { STAGE_FOREACH_ENEMY_ALL(pSquad2, pEnemy, i) { pEnemy->Configure(10, COLOR_SHIP_YELLOW); }); }); STAGE_GET_START(1u) STAGE_GET_FLOAT(fRotation, fRotation = 1.0f*PI) STAGE_GET_END cEnemy* pKing = pSquad1->GetEnemy(0u); if(STAGE_BEGINNING) { this->EnableLoad(pKing); } const coreUintW iLoadStage = (F_TO_UI(m_afLoadPower[0]) != F_TO_UI(m_afLoadPower[1])) ? F_TO_UI(m_afLoadPower[0]) : 0u; if(iLoadStage == 4u) STAGE_RESURRECT(pSquad2, 0u, 3u) else if(iLoadStage == 7u) STAGE_RESURRECT(pSquad2, 4u, 7u) else if(iLoadStage == 10u) STAGE_RESURRECT(pSquad2, 8u, 15u) else if(iLoadStage == 12u) { this->DisableLoad(true); pKing->SetBaseColor(COLOR_SHIP_RED); pKing->RemoveStatus(ENEMY_STATUS_INVINCIBLE); pKing->Kill(false); // reset lifetime pKing->Resurrect(); g_pSpecialEffects->MacroExplosionColorBig(pKing->GetPosition(), COLOR_ENERGY_RED); if(pSquad2->GetNumEnemies() == pSquad2->GetNumEnemiesAlive()) STAGE_BADGE(2u, BADGE_HARD, pKing->GetPosition()) } STAGE_FOREACH_ENEMY(pSquad1, pEnemy, i) { STAGE_LIFETIME(pEnemy, 0.5f, 0.0f) fRotation = FMOD(fRotation + m_afLoadPower[0] * 0.7f * TIME, 2.0f*PI); if(pEnemy->HasStatus(ENEMY_STATUS_INVINCIBLE)) { const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2(0.0f,0.0f); pEnemy->DefaultMovePath(pPath1, vFactor, vOffset * vFactor, fLifeTime); pEnemy->DefaultRotate (fRotation); if(STAGE_TICK_TIME(0.6f, 0.75f)) // TODO 1: STAGE_TICK_TIME2 instead ? { for(coreUintW j = 0u, je = F_TO_UI(m_afLoadPower[0]); j < je; ++j) { const coreVector2 vPos = m_aLoadRaw[j].GetPosition().xy(); const coreVector2 vDir = (g_pGame->FindPlayerDual(s_iTick % 2u)->GetPosition().xy() - vPos).Normalized(); g_pGame->GetBulletManagerEnemy()->AddBullet<cTriangleBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.1f); } } } else { pEnemy->SetPosition (coreVector3(SIN(fLifeTime) * 1.0f * FOREGROUND_AREA.x, pEnemy->GetPosition().yz())); pEnemy->DefaultRotate(fRotation); if(STAGE_TICK_LIFETIME(30.0f, 0.0f) && ((s_iTick % 8u) < 2u)) { const coreVector2 vPos = pEnemy->GetPosition().xy(); const coreFloat fBase = I_TO_F(s_iTick) * (0.015f*PI); for(coreUintW j = 6u; j--; ) { const coreVector2 vDir = coreVector2::Direction(DEG_TO_RAD(I_TO_F(j) * 30.0f) + fBase); g_pGame->GetBulletManagerEnemy()->AddBullet<cTriangleBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.1f); g_pGame->GetBulletManagerEnemy()->AddBullet<cTriangleBullet>(5, 1.1f, pEnemy, vPos, -vDir)->ChangeSize(1.1f); } } } }); STAGE_FOREACH_ENEMY(pSquad2, pEnemy, i) { STAGE_LIFETIME(pEnemy, 4.0f, 0.0f) const coreVector2 vFactor = coreVector2(1.0f,1.0f); const coreVector2 vOffset = coreVector2(I_TO_F(i % 4u) * -0.18f - 0.41f, 0.0f); pEnemy->DefaultMovePath(pPath2, vFactor, vOffset * vFactor, fLifeTime); if(i < 4u) pEnemy->Rotate270(); else if(i < 8u) pEnemy->Rotate270()->InvertX(); else if(i < 12u) pEnemy->InvertX(); else if(i < 16u) {} const coreVector2 vDir = pEnemy->AimAtPlayerSide().Normalized(); pEnemy->SetDirection(coreVector3(vDir, 0.0f)); if(STAGE_LIFETIME_AFTER(2.0f) && STAGE_TICK_TIME(0.6f, ((i % 8u) < 4u) ? 0.0f : 0.5f)) // TODO 1: STAGE_TICK_TIME2 instead ? { const coreVector2 vPos = pEnemy->GetPosition().xy(); g_pGame->GetBulletManagerEnemy()->AddBullet<cSpearBullet>(5, 1.1f, pEnemy, vPos, vDir)->ChangeSize(1.6f); } }); STAGE_COLL_ENEMY_BULLET(pEnemy, pBullet, vIntersection, bFirstHit, COLL_THIS) { if(!bFirstHit || (pEnemy->GetID() != cWarriorEnemy::ID)) return; this->BumpLoad(I_TO_F(pBullet->GetDamage()) * 0.03f * RCP(I_TO_F(g_pGame->GetNumPlayers()))); pBullet->Deactivate(true, vIntersection.xy()); }); STAGE_WAVE(5u, "6-?", {60.0f, 80.0f, 100.0f, 120.0f, 240.0f}) // SECHSUNDDREISSIG }, STAGE_PRE() { g_pGame->GetEnemyManager()->PrefetchEnemy<cScoutEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cWarriorEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cStarEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cArrowEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cMinerEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cFreezerEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cCinderEnemy>(); g_pGame->GetEnemyManager()->PrefetchEnemy<cMeteorEnemy>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cOrbBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cConeBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cWaveBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cSpearBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cTriangleBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cFlipBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cQuadBullet>(); g_pGame->GetBulletManagerEnemy()->PrefetchBullet<cViewBullet>(); }); // ################################################################ // reset helper STAGE_MAIN({TAKE_ALWAYS, 5u}) { g_pGame->KillHelpers(); this->DisableLoad(false); STAGE_FINISH_NOW }); #endif // ################################################################ // STAGE_MAIN({TAKE_ALWAYS, 5u}) { if(!this->IsStarEnabled(0u)) { // g_pGame->ForEachPlayer([&](const cPlayer* pPlayer, const coreUintW i) { this->EnableStar(i, pPlayer, coreVector2(0.0f,0.0f)); this->SetStarLength(i, 0.0f); }); // this->StartSwing(1.0f); } STAGE_FINISH_NOW }); // ################################################################ // change background appearance STAGE_MAIN({TAKE_ALWAYS, 5u}) { g_pEnvironment->SetTargetDirectionNow(coreVector2(0.0f,1.0f)); g_pEnvironment->SetTargetSpeedNow (6.0f); STAGE_FINISH_NOW }); // ################################################################ // wait for play STAGE_MAIN({TAKE_ALWAYS, 5u}) { STAGE_FINISH_PLAY }); // ################################################################ // boss STAGE_MAIN({TAKE_ALWAYS, 5u}) { STAGE_BOSS(m_Zeroth, {160.0f, 240.0f, 320.0, 400.0f, 800.0f}) // +5 }, STAGE_PRE() { g_pGame->PrefetchBoss(); }); // ################################################################ // STAGE_MAIN({TAKE_ALWAYS, 5u}) { if(!g_pGame->GetItemManager()->GetNumItems() && !g_pGame->GetInterface()->IsFragmentActive()) { STAGE_FINISH_NOW } }); // ################################################################ // story if(m_bStory) STAGE_MAIN({TAKE_ALWAYS, 5u}) { cTracker* pTracker = g_pGame->GetTracker(); if(STAGE_BEGINNING) { pTracker->Resurrect(); pTracker->EnableWind(); pTracker->EnableRange(); pTracker->SetPosition(coreVector3(0.0f,1.5f,0.0f) * FOREGROUND_AREA3); pTracker->SetDirection(coreVector3(0.0f,-1.0f,0.0f)); } if(STAGE_TIME_POINT(2.0f)) { g_pGame->FadeMusic(0.35f); m_pNightmareSound->PlayRelative(this, 0.5f, 1.0f, false, SOUND_EFFECT); } if(STAGE_TIME_BEFORE(5.0f)) { g_pPostProcessing->SetChroma(STEP(3.0f, 5.0f, m_fStageTime) * 1.5f); pTracker->SetPosition(coreVector3(0.0f, LERPB(1.5f, 0.6f, STEP(2.0f, 4.0f, m_fStageTime)), 0.0f) * FOREGROUND_AREA3); } else if(STAGE_TIME_POINT(5.3f)) { g_pPostProcessing->SetDirectionGame(coreVector2(0.0f,-1.0f)); } else if(STAGE_TIME_POINT(5.6f)) { pTracker->Kill(false); g_pPostProcessing->SetDirectionGame(coreVector2(0.0f,1.0f)); g_pEnvironment->ChangeBackground(cDarkBackground::ID, ENVIRONMENT_MIX_FADE, 0.0f); g_pEnvironment->SetTargetSpeedNow(0.0f); Core::Manager::Resource->UpdateWait(); STAGE_FOREACH_PLAYER_ALL(pPlayer, i) { pPlayer->AddStatus(PLAYER_STATUS_NO_INPUT_ALL); pPlayer->SetPosition(coreVector3(HIDDEN_POS, 0.0f)); }); g_pGame->GetBulletManagerPlayer()->ClearBullets(false); } else if(STAGE_TIME_POINT(5.9f)) { g_pPostProcessing->SetChroma(0.0f); g_pEnvironment->ChangeBackground(cNoBackground::ID, ENVIRONMENT_MIX_FADE, 0.0f); g_pPostProcessing->SetFrameValue(1.95f); g_pPostProcessing->SetFrameAnimation(7.0f); } else if(STAGE_TIME_POINT(6.7f)) { g_pPostProcessing->SetFrameValue(0.0f); this->EnableRanges(); } else if(STAGE_TIME_POINT(7.5f)) { g_pPostProcessing->SetChroma(1.5f); g_pEnvironment->ChangeBackground(cDarkBackground::ID, ENVIRONMENT_MIX_FADE, 0.0f); Core::Manager::Resource->UpdateWait(); } else if(STAGE_TIME_POINT(7.8f)) { g_pPostProcessing->SetChroma(0.0f); g_pEnvironment->ChangeBackground(cSnowBackground::ID, ENVIRONMENT_MIX_FADE, 0.0f); g_pEnvironment->SetTargetSpeedNow(ENVIRONMENT_DEFAULT_SPEED); Core::Manager::Resource->UpdateWait(); STAGE_FOREACH_PLAYER_ALL(pPlayer, i) { const coreFloat fSide = g_pGame->IsMulti() ? (20.0f * (I_TO_F(i) - 0.5f * I_TO_F(GAME_PLAYERS - 1u))) : 0.0f; pPlayer->RemoveStatus(PLAYER_STATUS_NO_INPUT_ALL); pPlayer->AddStatus (PLAYER_STATUS_NO_INPUT_SHOOT); pPlayer->SetPosition(coreVector3(fSide, -0.75f * FOREGROUND_AREA.y, 0.0f)); }); this->DisableRanges(false); if(m_pNightmareSound->EnableRef(this)) m_pNightmareSound->Stop(); } else if(STAGE_TIME_AFTER(8.2f)) { STAGE_FINISH_NOW } }); // ################################################################ // end STAGE_MAIN({TAKE_ALWAYS, 5u}) { STAGE_FINISH_AFTER(MISSION_WAIT_OUTRO) }); // ################################################################ // story state STAGE_MAIN({TAKE_ALWAYS, 5u}) { ADD_BIT_EX(g_pSave->EditProgress()->aiState, STATE_STORY_CALOR) STAGE_FINISH_NOW }); // ################################################################ // ################################################################ }