KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
Popup_Result.cpp
이 파일의 문서화 페이지로 가기
1// Copyright (c) 2025 Doppleddiggong. All rights reserved. Unauthorized copying, modification, or distribution of this file, via any medium is strictly prohibited. Proprietary and confidential.
2
3
4#include "Popup_Result.h"
5
6#include "ALingoGameState.h"
7#include "APlayerControl.h"
8#include "FResultStatData.h"
9#include "GameLogging.h"
10#include "UImageButton.h"
12#include "ULingoGameHelper.h"
13#include "UPopupManager.h"
14#include "UResultStatWidget.h"
15#include "UTextureButton.h"
16#include "UAnswerItem.h"
17#include "UGameDataManager.h"
18#include "UBroadcastManager.h"
19#include "Components/HorizontalBox.h"
20#include "Components/ScrollBox.h"
21#include "Components/TextBlock.h"
22#include "Blueprint/UserWidget.h"
23#include "Components/Image.h"
24#include "Components/Spacer.h"
25#include "Components/VerticalBox.h"
26
28{
29 Super::NativeDestruct();
30}
31
32void UPopup_Result::InitPopup(const EQuestType InQuestType)
33{
34 // 결과 팝업이 뜰 때 MissionTimer 멈추기
35 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
36 {
37 GS->StopMissionTimer();
38 }
39
40 if (Btn_Confirm)
41 {
42 Btn_Confirm->OnButtonClickedEvent.RemoveDynamic(this, &UPopup_Result::OnClickClose);
43 Btn_Confirm->OnButtonClickedEvent.AddDynamic(this, &UPopup_Result::OnClickClose);
44 }
45
46 this->QuestType =InQuestType;
47
50
51 // GameState에서 결과 확인
52 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
53 {
55 {
56 GS->OnReadResultUpdated.RemoveDynamic(this, &UPopup_Result::InitReadResult);
57 GS->OnReadResultUpdated.AddDynamic(this, &UPopup_Result::InitReadResult);
58 }
59 else if (QuestType == EQuestType::Listen)
60 {
61 GS->OnListenResultUpdated.RemoveDynamic(this, &UPopup_Result::InitListenResult);
62 GS->OnListenResultUpdated.AddDynamic(this, &UPopup_Result::InitListenResult);
63 }
64
65 bool bHasResult = false;
66
67 if (QuestType == EQuestType::Read && !GS->ReadResult.grade.IsEmpty())
68 {
69 // 이미 결과가 있으면 바로 표시
70 PRINTLOG(TEXT("[Popup_Result] Read result already exists in GameState"));
71 InitReadResult(GS->ReadResult);
72 bHasResult = true;
73 }
74 else if (QuestType == EQuestType::Listen && !GS->ListenResult.grade.IsEmpty())
75 {
76 // 이미 결과가 있으면 바로 표시
77 PRINTLOG(TEXT("[Popup_Result] Listen result already exists in GameState"));
78 InitListenResult(GS->ListenResult);
79 bHasResult = true;
80 }
81
82 // 결과가 없으면 Host만 요청
83 if (!bHasResult && GetOwningPlayer()->HasAuthority())
84 {
86 }
87 }
88}
89
91{
92 // PopupManager를 통해 팝업 닫기 (마우스 커서 처리 포함)
93 if (UPopupManager* PopupMgr = UPopupManager::Get(GetWorld()))
94 {
95 PopupMgr->HideCurrentPopup();
96 }
97}
98
99
100
102{
103 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
104 {
106 {
107 Txt_Kor->SetText(FText::FromString(GS->ReadScenarioData.full_data.Kor));
108 Txt_Eng->SetText(FText::FromString(GS->ReadScenarioData.full_data.Eng));
109
110 Txt_Title->SetText(FText::FromString(TEXT("Read Quest")));
111 if (UTexture2D* Texture = UGameDataManager::Get(this)->GetTexture(EResourceTextureType::Read))
112 Image_Symbol->SetBrushFromTexture(Texture);
113 }
114 else if ( QuestType == EQuestType::Listen )
115 {
116 Txt_Kor->SetText(FText::FromString(GS->ListenScenarioData.full_data.Kor));
117 Txt_Eng->SetText(FText::FromString(GS->ListenScenarioData.full_data.Eng));
118
119 Txt_Title->SetText(FText::FromString(TEXT("Listen Quest")));
120 if (UTexture2D* Texture = UGameDataManager::Get(this)->GetTexture(EResourceTextureType::Listen))
121 Image_Symbol->SetBrushFromTexture(Texture);
122 }
123 }
124}
125
127{
128 ALingoGameState* GS = Cast<ALingoGameState>(GetWorld()->GetGameState());
129 if (!GS)
130 return;
131
132 // 틀린 인덱스 리스트
133 TArray<int32> WrongList;
134 // 전체 캐리어 정보
135 TArray<FScenarioTargetData> ScenarioData;
136 FScenarioTargetData CorrectData;
137
139 {
140 WrongList = GS->WrongReadAnswerList;
141 ScenarioData = GS->GetReadScenarioData().target_data;
142 CorrectData = GS->GetReadScenarioData().GetCorrectAnswerData();
143 }
144 else if ( QuestType == EQuestType::Listen )
145 {
146 WrongList = GS->WrongListenAnswerList;
147 ScenarioData = GS->TryListenAnswerData.target_data;
148 CorrectData = GS->GetListenScenarioData().GetCorrectAnswerData();
149 }
150
151 // 기존 항목 제거
152 VerticalBox->ClearChildren();
153 // WrongList의 각 항목을 UAnswerItem으로 추가
154 for (int32 i = 0; i < WrongList.Num(); i++)
155 {
156 const FScenarioTargetData& SD = ScenarioData[WrongList[i]];
157
158 // UAnswerItem 위젯 생성
159 if (AnswerItemClass)
160 {
161 UAnswerItem* AnswerItem = CreateWidget<UAnswerItem>(this, AnswerItemClass);
162 if (AnswerItem)
163 {
164 // AnswerItem 초기화
165 AnswerItem->InitInfo(QuestType, i + 1, SD, CorrectData);
166
167 // HorizontalBox에 추가
168 VerticalBox->AddChild(AnswerItem);
169
170 {
171 USpacer* Spacer = NewObject<USpacer>(VerticalBox);
172 Spacer->SetSize(FVector2D(1.f, 10.f));
173 VerticalBox->AddChild(Spacer);
174 }
175 }
176 }
177 }
178}
179
181{
182 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
183 {
184 // Symbol로 타임 처리
185 auto TimeTaken = GS->GetTimeTaken();
186 const int32 Minutes = FMath::FloorToInt(TimeTaken / 60.f);
187 const int32 Seconds = FMath::FloorToInt(TimeTaken) % 60;
188
189 FResultStatData TimeResultData;
191 TimeResultData.ColorType = EColorStyleType::Gray;
192 TimeResultData.TitleText = FText::FromString(TEXT("Time"));
194 TimeResultData.SymbolValue = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds);
195 Result_Time->InitData(TimeResultData);
196 }
197
198 FResultStatData GradeResultData;
200 GradeResultData.ColorType = EColorStyleType::Gray;
201 GradeResultData.TitleText = FText::FromString(TEXT("Grade"));
202 GradeResultData.GradeTextureType = ULingoGameHelper::ConvertGradeString(ResponseData.grade);
203 Result_Grade->InitData(GradeResultData);
204
205 FResultStatData TopRateResultData;
206 TopRateResultData.WidgetType = EResultItemWidgetType::Rate;
207 TopRateResultData.ColorType = EColorStyleType::Gray;
208 TopRateResultData.TitleText = FText::FromString(TEXT("Rate"));
209 TopRateResultData.RatePercent = ResponseData.top_percent;
210 Result_TopRate->InitData(TopRateResultData);
211
212 FResultStatData AverageScoreResultData;
213 AverageScoreResultData.WidgetType = EResultItemWidgetType::Symbol;
214 AverageScoreResultData.ColorType = EColorStyleType::Gray;
215 AverageScoreResultData.TitleText = FText::FromString(TEXT("Score"));
216 AverageScoreResultData.SymbolTextureType = EResourceTextureType::Score;
217 AverageScoreResultData.SymbolValue = FString::Printf(TEXT("%d"), static_cast<int>(ResponseData.average_score));
218 Result_AverageScore->InitData(AverageScoreResultData);
219}
220
222{
223 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
224 {
225 // Symbol로 타임 처리
226 auto TimeTaken = GS->GetTimeTaken();
227 const int32 Minutes = FMath::FloorToInt(TimeTaken / 60.f);
228 const int32 Seconds = FMath::FloorToInt(TimeTaken) % 60;
229
230 FResultStatData TimeResultData;
232 TimeResultData.ColorType = EColorStyleType::Gray;
233 TimeResultData.TitleText = FText::FromString(TEXT("TIME"));
235 TimeResultData.SymbolValue = FString::Printf(TEXT("%02d:%02d"), Minutes, Seconds);
236 Result_Time->InitData(TimeResultData);
237 }
238
239 FResultStatData GradeResultData;
241 GradeResultData.ColorType = EColorStyleType::Gray;
242 GradeResultData.TitleText = FText::FromString(TEXT("GRADE"));
243 GradeResultData.GradeTextureType = ULingoGameHelper::ConvertGradeString(ResponseData.grade);
244 Result_Grade->InitData(GradeResultData);
245
246 FResultStatData TopRateResultData;
247 TopRateResultData.WidgetType = EResultItemWidgetType::Rate;
248 TopRateResultData.ColorType = EColorStyleType::Gray;
249 TopRateResultData.TitleText = FText::FromString(TEXT("TOP"));
250 TopRateResultData.RatePercent = ResponseData.top_percent;
251 Result_TopRate->InitData(TopRateResultData);
252
253 FResultStatData AverageScoreResultData;
254 AverageScoreResultData.WidgetType = EResultItemWidgetType::Symbol;
255 AverageScoreResultData.ColorType = EColorStyleType::Gray;
256 AverageScoreResultData.TitleText = FText::FromString(TEXT("SCORE"));
257 AverageScoreResultData.SymbolTextureType = EResourceTextureType::Score;
258 AverageScoreResultData.SymbolValue = FString::Printf(TEXT("%d"), static_cast<int>(ResponseData.average_score));
259 Result_AverageScore->InitData(AverageScoreResultData);
260}
261
263{
264 APlayerController* PC = GetOwningPlayer();
265 if (!PC)
266 return;
267
268 // Host 플레이어(첫 번째 플레이어)인지 확인
269 bool bIsHost = false;
270 if (APlayerState* PS = PC->GetPlayerState<APlayerState>())
271 {
272 if (AGameStateBase* GameStateBase = GetWorld()->GetGameState())
273 {
274 int32 PlayerIndex = GameStateBase->PlayerArray.IndexOfByKey(PS);
275 bIsHost = (PlayerIndex == 0);
276 }
277 }
278
279 // Host가 아니면 요청하지 않음
280 if (!bIsHost)
281 {
282 PRINTLOG(TEXT("[Popup_Result] Guest player - skipping request, waiting for Host"));
283 return;
284 }
285
286 PRINTLOG(TEXT("[Popup_Result] Host player - sending result request"));
287
288 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
289 {
290 // 네트워크 송신 (Host만)
291 if (UKLingoNetworkSystem* Network = UKLingoNetworkSystem::Get(GetWorld()))
292 {
294 {
295 FRequestReadResult ReadRequest;
296 ReadRequest.room_id = GS->GetRoomId();
297 ReadRequest.user_id = Cast<APlayerControl>(GetOwningPlayer())->GetUserId();
298 ReadRequest.scenario_id = 1;
300 ReadRequest.state_type = 0;
301 ReadRequest.result_time = GS->GetTimeTaken();
302 ReadRequest.wrong_idx = GS->WrongReadAnswerList;
303 Network->RequestReadResult(ReadRequest,
304 FResponseReadResultDelegate::CreateUObject(this, &UPopup_Result::OnResponseReadResult));
305 }
306 else if ( QuestType == EQuestType::Listen )
307 {
308 FRequestListenResult ListenRequest;
309 ListenRequest.room_id = GS->GetRoomId();
310 ListenRequest.user_id = Cast<APlayerControl>(GetOwningPlayer())->GetUserId();
311 ListenRequest.scenario_id = 1;
313 ListenRequest.state_type = 0;
314 ListenRequest.result_time = GS->GetTimeTaken();
315 ListenRequest.wrong_idx = GS->WrongListenAnswerList;
316 Network->RequestListenResult(ListenRequest,
317 FResponseListenResultDelegate::CreateUObject(this, &UPopup_Result::OnResponseListenResult));
318 }
319 }
320 }
321}
322
323void UPopup_Result::OnResponseReadResult(FResponseReadResult& ResponseData, bool bWasSuccessful)
324{
325 if (bWasSuccessful)
326 {
327 PRINTLOG(TEXT("[Result] Grade: %s, average_score: %.2f%%, Top Percent: %.2f%%"),
328 *ResponseData.grade, ResponseData.average_score, ResponseData.top_percent);
329
330 // GameState에 결과 저장 (자동으로 복제됨)
331 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
332 {
333 GS->ReadResult = ResponseData;
334 GS->OnReadResultUpdated.Broadcast(ResponseData);
335 }
336
337 this->InitReadResult(ResponseData);
338 }
339 else
340 {
341 PRINTLOG(TEXT("[Result] Quest result Failed"));
342 }
343}
344
345void UPopup_Result::OnResponseListenResult(FResponseListenResult& ResponseData, bool bWasSuccessful)
346{
347 if (bWasSuccessful)
348 {
349 // GameState에 결과 저장 (자동으로 복제됨)
350 if (auto GS = Cast<ALingoGameState>(GetWorld()->GetGameState()))
351 {
352 GS->ListenResult = ResponseData;
353 GS->OnListenResultUpdated.Broadcast(ResponseData);
354 }
355
356 this->InitListenResult(ResponseData);
357 }
358 else
359 {
360 PRINTLOG(TEXT("[Result] Quest result Failed"));
361 }
362}
EQuestType
APlayerControl 선언에 대한 Doxygen 주석을 제공합니다.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
Definition GameLogging.h:30
UGameDataManager 클래스를 선언합니다.
KLingo API 요청을 담당하는 서브시스템을 선언합니다.
TArray< int32 > WrongListenAnswerList
FORCEINLINE const FResponseListenScenario & GetListenScenarioData() const
FResponseListenScenario TryListenAnswerData
TArray< int32 > WrongReadAnswerList
FORCEINLINE const FResponseReadScenario & GetReadScenarioData() const
void InitInfo(EQuestType QuestType, int32 InOrder, FScenarioTargetData TargetData, FScenarioTargetData CorrectData)
KLingo 서버와의 HTTP 요청을 중재하는 게임 인스턴스 서브시스템입니다.
static int32 GetStageTypeIndex(const EQuestType QuestType)
static EResourceTextureType ConvertGradeString(const FString &Grade)
팝업 관리자
TObjectPtr< class UTextBlock > Txt_Title
TObjectPtr< class UResultStatWidget > Result_Time
EQuestType QuestType
TObjectPtr< class UResultStatWidget > Result_Grade
TObjectPtr< class UTextBlock > Txt_Eng
TObjectPtr< class UResultStatWidget > Result_AverageScore
TObjectPtr< class UImage > Image_Symbol
virtual void NativeDestruct() override
void InitListenResult(const FResponseListenResult &ResponseData)
TObjectPtr< class UTextBlock > Txt_Kor
void InitPopup(EQuestType InQuestType)
TObjectPtr< class UResultStatWidget > Result_TopRate
void InitReadResult(const FResponseReadResult &ResponseData)
void OnResponseReadResult(FResponseReadResult &ResponseData, bool bWasSuccessful)
TObjectPtr< class UImageButton > Btn_Confirm
TObjectPtr< class UVerticalBox > VerticalBox
TSubclassOf< class UAnswerItem > AnswerItemClass
void OnResponseListenResult(FResponseListenResult &ResponseData, bool bWasSuccessful)
TArray< int32 > wrong_idx
TArray< int32 > wrong_idx
FScenarioTargetData GetCorrectAnswerData() const
TArray< FScenarioTargetData > target_data
FScenarioTargetData GetCorrectAnswerData() const
TArray< FScenarioTargetData > target_data
Result Stat 위젯 통합 데이터 구조 위젯 타입, 색상 스타일, 각 타입별 데이터를 통합 관리
EResourceTextureType SymbolTextureType
Symbol 타입 전용: 심볼 문자열
float RatePercent
Rate 타입 전용: 퍼센트 값 (0.0 ~ 1.0)
EColorStyleType ColorType
색상 스타일
FText TitleText
타이틀 텍스트
EResultItemWidgetType WidgetType
위젯 타입
EResourceTextureType GradeTextureType
Grade 타입 전용: 텍스처 타입
Scenario 타겟 데이터입니다.