#include "Runtime/GuiSys/CScanDisplay.hpp" #include "Runtime/CSimplePool.hpp" #include "Runtime/GameGlobalObjects.hpp" #include "Runtime/Audio/CSfxManager.hpp" #include "Runtime/Graphics/CCubeRenderer.hpp" #include "Runtime/Graphics/CGraphics.hpp" #include "Runtime/GuiSys/CAuiImagePane.hpp" #include "Runtime/GuiSys/CGuiCamera.hpp" #include "Runtime/GuiSys/CGuiModel.hpp" #include "Runtime/GuiSys/CGuiTextPane.hpp" #include "Runtime/GuiSys/CGuiWidget.hpp" #include "Runtime/GuiSys/CStringTable.hpp" #include "Runtime/Input/CFinalInput.hpp" #include "Runtime/MP1/CPauseScreenBase.hpp" #include namespace metaforce { void CScanDisplay::CDataDot::Update(float dt) { if (x20_remTime > 0.f) { x20_remTime = std::max(0.f, x20_remTime - dt); float d = 0.f; if (x1c_transDur > 0.f) d = x20_remTime / x1c_transDur; xc_curPos = zeus::CVector2f::lerp(x14_targetPos, x4_startPos, d); } if (x24_alpha > x28_desiredAlpha) { float tmp = x24_alpha - 2.f * dt; x24_alpha = std::max(tmp, x28_desiredAlpha); } else if (x24_alpha < x28_desiredAlpha) { float tmp = 2.f * dt + x24_alpha; x24_alpha = std::min(tmp, x28_desiredAlpha); } } void CScanDisplay::CDataDot::Draw(const zeus::CColor& col, float radius) { if (x24_alpha == 0.f || x0_dotState == EDotState::Hidden) { return; } const zeus::CTransform xf = zeus::CTransform::Translate(xc_curPos.x(), 0.f, xc_curPos.y()); g_Renderer->SetModelMatrix(xf); CGraphics::StreamBegin(GX_TRIANGLESTRIP); zeus::CColor useColor = col; useColor.a() *= x24_alpha; CGraphics::StreamColor(useColor); CGraphics::StreamTexcoord(0.f, 1.f); CGraphics::StreamVertex(-radius, 0.f, radius); CGraphics::StreamTexcoord(0.f, 0.f); CGraphics::StreamVertex(-radius, 0.f, -radius); CGraphics::StreamTexcoord(1.f, 1.f); CGraphics::StreamVertex(radius, 0.f, radius); CGraphics::StreamTexcoord(1.f, 0.f); CGraphics::StreamVertex(radius, 0.f, -radius); CGraphics::StreamEnd(); } void CScanDisplay::CDataDot::StartTransitionTo(const zeus::CVector2f& vec, float dur) { x20_remTime = dur; x1c_transDur = dur; x4_startPos = xc_curPos; x14_targetPos = vec; } void CScanDisplay::CDataDot::SetDestPosition(const zeus::CVector2f& pos) { if (x20_remTime <= 0.f) xc_curPos = pos; else x14_targetPos = pos; } CScanDisplay::CScanDisplay(const CGuiFrame& selHud) : xa0_selHud(selHud) { x0_dataDot = g_SimplePool->GetObj("TXTR_DataDot"); for (size_t i = 0; i < xbc_dataDots.capacity(); ++i) { xbc_dataDots.emplace_back(x0_dataDot); } x170_paneStates.resize(x170_paneStates.capacity()); } void CScanDisplay::ProcessInput(const CFinalInput& input) { if (xc_state == EScanState::Inactive || xc_state == EScanState::Done) return; if (xc_state == EScanState::DownloadComplete && x1a4_xAlpha == 0.f) { if (input.PA() || input.PSpecialKey(ESpecialKey::Enter) || input.PMouseButton(EMouseButton::Primary)) { if (xa8_message->TextSupport().GetCurTime() < xa8_message->TextSupport().GetTotalAnimationTime()) { xa8_message->TextSupport().SetCurTime(xa8_message->TextSupport().GetTotalAnimationTime()); } else { xc_state = EScanState::ViewingScan; x1a4_xAlpha = 1.f; CSfxManager::SfxStart(SFXui_scan_next_page, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); } } } else if (xc_state == EScanState::ViewingScan) { int oldCounter = x1ac_pageCounter; int totalPages = xac_scrollMessage->TextSupport().GetTotalPageCount(); if ((input.PA() || input.PSpecialKey(ESpecialKey::Enter) || input.PMouseButton(EMouseButton::Primary)) && totalPages != -1) { CGuiTextSupport& supp = !x1ac_pageCounter ? xa8_message->TextSupport() : xac_scrollMessage->TextSupport(); if (supp.GetCurTime() < supp.GetTotalAnimationTime()) supp.SetCurTime(supp.GetTotalAnimationTime()); else x1ac_pageCounter = std::min(totalPages, x1ac_pageCounter + 1); } if (x1ac_pageCounter != oldCounter) { CSfxManager::SfxStart(SFXui_scan_next_page, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); if (x1ac_pageCounter == 0) { xa8_message->SetIsVisible(true); xac_scrollMessage->SetIsVisible(false); } else { if (oldCounter == 0) { xa8_message->SetIsVisible(false); xac_scrollMessage->SetIsVisible(true); } xac_scrollMessage->TextSupport().SetPage(x1ac_pageCounter - 1); SetScanMessageTypeEffect(xac_scrollMessage, !x1b4_scanComplete); } } } float xAlpha = 0.f; float aAlpha = 0.f; float dashAlpha = 0.f; if (xc_state == EScanState::DownloadComplete) { xAlpha = std::min(2.f * x1a4_xAlpha, 1.f); aAlpha = (1.f - xAlpha) * std::fabs(x1b0_aPulse); } else if (xc_state == EScanState::ViewingScan) { if (x1ac_pageCounter < xac_scrollMessage->TextSupport().GetTotalPageCount()) aAlpha = std::fabs(x1b0_aPulse); else dashAlpha = 1.f; } xb0_xmark->SetVisibility(xAlpha > 0.f, ETraversalMode::Children); xb4_abutton->SetVisibility(aAlpha > 0.f, ETraversalMode::Children); xb8_dash->SetVisibility(dashAlpha > 0.f, ETraversalMode::Children); xb0_xmark->SetColor(zeus::CColor(1.f, xAlpha)); xb4_abutton->SetColor(zeus::CColor(1.f, aAlpha)); xb8_dash->SetColor(zeus::CColor(0.53f, 0.84f, 1.f, dashAlpha)); } float CScanDisplay::GetDownloadStartTime(size_t idx) const { if (!x14_scannableInfo) { return 0.f; } const float nTime = x14_scannableInfo->GetBucket(idx).x4_appearanceRange; float maxTime = 0.f; for (size_t i = 0; i < CScannableObjectInfo::NumBuckets; ++i) { const float iTime = x14_scannableInfo->GetBucket(i).x4_appearanceRange; if (iTime < nTime) { maxTime = std::max(iTime, maxTime); } } return maxTime + g_tweakGui->GetScanAppearanceDuration(); } float CScanDisplay::GetDownloadFraction(size_t idx, float scanningTime) const { if (!x14_scannableInfo) { return 0.f; } const float appearTime = GetDownloadStartTime(idx); const float appearRange = x14_scannableInfo->GetBucket(idx).x4_appearanceRange; if (appearTime == appearRange) { return 1.f; } return zeus::clamp(0.f, (scanningTime - appearTime) / (appearRange - appearTime), 1.f); } void CScanDisplay::StartScan(TUniqueId id, const CScannableObjectInfo& scanInfo, CGuiTextPane* message, CGuiTextPane* scrollMessage, CGuiWidget* textGroup, CGuiModel* xmark, CGuiModel* abutton, CGuiModel* dash, float scanTime) { x1b4_scanComplete = scanTime >= scanInfo.GetTotalDownloadTime(); x14_scannableInfo.emplace(scanInfo); x10_objId = id; xc_state = EScanState::Downloading; x1ac_pageCounter = 0; x1a4_xAlpha = 0.f; xa8_message = message; xac_scrollMessage = scrollMessage; xa4_textGroup = textGroup; xb0_xmark = xmark; xb4_abutton = abutton; xb8_dash = dash; xa4_textGroup->SetVisibility(true, ETraversalMode::Children); xa4_textGroup->SetColor(zeus::CColor(1.f, 0.f)); xa8_message->TextSupport().SetText(u""); xac_scrollMessage->TextSupport().SetText(u""); for (size_t i = 0; i < 20; ++i) { auto* pane = static_cast(xa0_selHud.FindWidget(MP1::CPauseScreenBase::GetImagePaneName(i))); zeus::CColor color = g_tweakGuiColors->GetScanDisplayImagePaneColor(); color.a() = 0.f; pane->SetColor(color); pane->SetTextureID0({}, g_SimplePool); pane->SetAnimationParms(zeus::skZero2f, 0.f, 0.f); size_t pos = SIZE_MAX; for (size_t j = 0; j < CScannableObjectInfo::NumBuckets; ++j) { if (x14_scannableInfo->GetBucket(j).x8_imagePos == i) { pos = j; break; } } if (pos != SIZE_MAX) { x170_paneStates[pos].second = pane; } } for (size_t i = 0; i < x170_paneStates.size(); ++i) { std::pair& state = x170_paneStates[i]; if (state.second) { const CScannableObjectInfo::SBucket& bucket = x14_scannableInfo->GetBucket(i); if (bucket.x14_interval > 0.f) { state.second->SetAnimationParms(zeus::CVector2f(bucket.xc_size.x, bucket.xc_size.y), bucket.x14_interval, bucket.x18_fadeDuration); } state.second->SetTextureID0(bucket.x0_texture, g_SimplePool); state.second->SetFlashFactor(0.f); const float startTime = GetDownloadStartTime(i); if (scanTime >= startTime) { x170_paneStates[i].first = 0.f; } else { x170_paneStates[i].first = -1.f; } } } const CAssetId strId = x14_scannableInfo->GetStringTableId(); if (strId.IsValid()) { x194_scanStr = g_SimplePool->GetObj({FOURCC('STRG'), strId}); } for (size_t i = 0; i < CScannableObjectInfo::NumBuckets; ++i) { const u32 pos = x14_scannableInfo->GetBucket(i).x8_imagePos; CDataDot& dot = xbc_dataDots[i]; if (pos != UINT32_MAX) { if (GetDownloadStartTime(i) - g_tweakGui->GetScanAppearanceDuration() < scanTime) { dot.SetAlpha(0.f); dot.SetDotState(CDataDot::EDotState::Done); } else { dot.SetDesiredAlpha(1.f); dot.SetDotState(CDataDot::EDotState::Seek); dot.StartTransitionTo(zeus::skZero2f, FLT_EPSILON); dot.Update(FLT_EPSILON); } } else { dot.SetDotState(CDataDot::EDotState::Hidden); } } } void CScanDisplay::StopScan() { if (xc_state == EScanState::Done || xc_state == EScanState::Inactive) { return; } xc_state = EScanState::Done; for (auto& dataDot : xbc_dataDots) { dataDot.SetDesiredAlpha(0.f); } } void CScanDisplay::SetScanMessageTypeEffect(CGuiTextPane* pane, bool type) { if (type) pane->TextSupport().SetTypeWriteEffectOptions(true, 0.1f, 60.f); else pane->TextSupport().SetTypeWriteEffectOptions(false, 0.f, 0.f); } void CScanDisplay::Update(float dt, float scanningTime) { if (xc_state == EScanState::Inactive) return; bool active = false; if (xc_state == EScanState::Done) { x1a8_bodyAlpha = std::max(0.f, x1a8_bodyAlpha - 2.f * dt); if (x1a8_bodyAlpha > 0.f) active = true; } else { active = true; x1a8_bodyAlpha = std::min(x1a8_bodyAlpha + 2.f * dt, 1.f); if (xc_state == EScanState::DownloadComplete) { if (xac_scrollMessage->TextSupport().GetIsTextSupportFinishedLoading()) x1a4_xAlpha = std::max(0.f, x1a4_xAlpha - dt); if (x1a4_xAlpha < 0.5f) { x1b0_aPulse += 2.f * dt; if (x1b0_aPulse > 1.f) x1b0_aPulse -= 2.f; } } else if (xc_state == EScanState::ViewingScan) { x1b0_aPulse += 2.f * dt; if (x1b0_aPulse > 1.f) x1b0_aPulse -= 2.f; if (x1a4_xAlpha == 1.f) { xa8_message->TextSupport().SetText(x194_scanStr->GetString(0)); SetScanMessageTypeEffect(xa8_message, !x1b4_scanComplete); } } else if (xc_state == EScanState::Downloading && scanningTime >= x14_scannableInfo->GetTotalDownloadTime() && x194_scanStr.IsLoaded()) { if (x1b4_scanComplete || x14_scannableInfo->GetCategory() == 0) { xc_state = EScanState::ViewingScan; x1b0_aPulse = x1a4_xAlpha = 1.f; CSfxManager::SfxStart(SFXui_scan_complete, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); } else { xc_state = EScanState::DownloadComplete; x1b0_aPulse = x1a4_xAlpha = 1.f; xa8_message->TextSupport().SetText(std::u16string(g_MainStringTable->GetString(29)) + g_MainStringTable->GetString(x14_scannableInfo->GetCategory() + 30) + g_MainStringTable->GetString(30)); SetScanMessageTypeEffect(xa8_message, true); CSfxManager::SfxStart(SFXui_new_scan_complete, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); } if (x194_scanStr->GetStringCount() > 2) { xac_scrollMessage->TextSupport().SetText(x194_scanStr->GetString(2), true); SetScanMessageTypeEffect(xac_scrollMessage, !x1b4_scanComplete); } xac_scrollMessage->SetIsVisible(false); } } for (size_t i = 0; i < x170_paneStates.size(); ++i) { if (x170_paneStates[i].second == nullptr) { continue; } if (x170_paneStates[i].first > 0.f) { x170_paneStates[i].first = std::max(0.f, x170_paneStates[i].first - dt); float tmp; if (x170_paneStates[i].first > g_tweakGui->GetScanPaneFadeOutTime()) { tmp = 1.f - (x170_paneStates[i].first - g_tweakGui->GetScanPaneFadeOutTime()) / g_tweakGui->GetScanPaneFadeInTime(); } else { tmp = x170_paneStates[i].first / g_tweakGui->GetScanPaneFadeOutTime(); } x170_paneStates[i].second->SetFlashFactor(tmp * g_tweakGui->GetScanPaneFlashFactor() * x1a8_bodyAlpha); } const float alphaMul = ((xc_state == EScanState::Downloading) ? GetDownloadFraction(i, scanningTime) : 1.f) * x1a8_bodyAlpha; zeus::CColor color = g_tweakGuiColors->GetScanDisplayImagePaneColor(); color.a() *= alphaMul; x170_paneStates[i].second->SetColor(color); x170_paneStates[i].second->SetDeResFactor(1.f - alphaMul); if (GetDownloadStartTime(i) - g_tweakGui->GetScanAppearanceDuration() < scanningTime) { CDataDot& dot = xbc_dataDots[i]; switch (dot.GetDotState()) { case CDataDot::EDotState::Seek: case CDataDot::EDotState::Hold: dot.SetDotState(CDataDot::EDotState::RevealPane); dot.StartTransitionTo(zeus::skZero2f, g_tweakGui->GetScanAppearanceDuration()); break; case CDataDot::EDotState::RevealPane: { const float tmp = dot.GetTransitionFactor(); if (tmp == 0.f) { dot.SetDotState(CDataDot::EDotState::Done); dot.SetDesiredAlpha(0.f); CSfxManager::SfxStart(SFXui_scan_pane_reveal, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId); x170_paneStates[i].first = g_tweakGui->GetScanPaneFadeOutTime() + g_tweakGui->GetScanPaneFadeInTime(); } break; } default: break; } } } for (size_t i = 0; i < xbc_dataDots.size(); ++i) { CDataDot& dot = xbc_dataDots[i]; switch (dot.GetDotState()) { case CDataDot::EDotState::Hidden: continue; case CDataDot::EDotState::Seek: case CDataDot::EDotState::Hold: { float tmp = dot.GetTransitionFactor(); if (tmp == 0.f) { float vpRatio = CGraphics::GetViewportHeight() / 480.f; float posRand = g_tweakGui->GetScanDataDotPosRandMagnitude() * vpRatio; float durMin = dot.GetDotState() == CDataDot::EDotState::Hold ? g_tweakGui->GetScanDataDotHoldDurationMin() : g_tweakGui->GetScanDataDotSeekDurationMin(); float durMax = dot.GetDotState() == CDataDot::EDotState::Hold ? g_tweakGui->GetScanDataDotHoldDurationMax() : g_tweakGui->GetScanDataDotSeekDurationMax(); zeus::CVector2f vec( dot.GetDotState() == CDataDot::EDotState::Hold ? dot.GetCurrPosition().x() : (posRand * (rand() / float(RAND_MAX)) - 0.5f * posRand), dot.GetDotState() == CDataDot::EDotState::Hold ? dot.GetCurrPosition().y() : (posRand * (rand() / float(RAND_MAX)) - 0.5f * posRand)); float dur = (durMax - durMin) * (rand() / float(RAND_MAX)) + durMin; dot.StartTransitionTo(vec, dur); dot.SetDotState(dot.GetDotState() == CDataDot::EDotState::Hold ? CDataDot::EDotState::Seek : CDataDot::EDotState::Hold); } break; } case CDataDot::EDotState::RevealPane: case CDataDot::EDotState::Done: { const zeus::CVector3f& panePos = x170_paneStates[i].second->GetWorldPosition(); zeus::CVector3f screenPos = xa0_selHud.GetFrameCamera()->ConvertToScreenSpace(panePos); zeus::CVector2f viewportCoords(screenPos.x() * CGraphics::GetViewportWidth() * 0.5f, screenPos.y() * CGraphics::GetViewportHeight() * 0.5f); dot.SetDestPosition(viewportCoords); break; } default: break; } dot.Update(dt); } if (!active) { xc_state = EScanState::Inactive; x10_objId = kInvalidUniqueId; x14_scannableInfo = std::nullopt; xa8_message->TextSupport().SetText(u""); xac_scrollMessage->TextSupport().SetText(u""); xa4_textGroup->SetVisibility(false, ETraversalMode::Children); xb0_xmark->SetVisibility(false, ETraversalMode::Children); xb4_abutton->SetVisibility(false, ETraversalMode::Children); xb8_dash->SetVisibility(false, ETraversalMode::Children); xa8_message = nullptr; xac_scrollMessage = nullptr; xa4_textGroup = nullptr; xb0_xmark = nullptr; xb4_abutton = nullptr; xb8_dash = nullptr; x170_paneStates.clear(); x170_paneStates.resize(4); x194_scanStr = TLockedToken(); x1ac_pageCounter = 0; x1b4_scanComplete = false; } else { xa4_textGroup->SetColor(zeus::CColor(1.f, x1a8_bodyAlpha)); } } void CScanDisplay::Draw() { if (!x0_dataDot.IsLoaded()) { return; } SCOPED_GRAPHICS_DEBUG_GROUP("CScanDisplay::Draw", zeus::skGreen); g_Renderer->SetDepthReadWrite(false, false); g_Renderer->SetViewportOrtho(true, -4096.f, 4096.f); g_Renderer->SetBlendMode_AdditiveAlpha(); CGraphics::SetTevOp(ERglTevStage::Stage0, CTevCombiners::sTevPass805a5ebc); x0_dataDot->Load(GX_TEXMAP0, EClampMode::Repeat); const float vpRatio = CGraphics::GetViewportHeight() / 480.f; for (CDataDot& dot : xbc_dataDots) { dot.Draw(g_tweakGuiColors->GetScanDataDotColor(), g_tweakGui->GetScanDataDotRadius() * vpRatio); } } } // namespace metaforce