KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
ALingoGameState.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 "ALingoGameState.h"
5
6#include "Net/UnrealNetwork.h"
7#include "APlayerActor.h"
8#include "GameLogging.h"
9#include "ULingoGameHelper.h"
11#include "APlayerControl.h"
13#include "EngineUtils.h"
14#include "UMainWidget.h"
15#include "UPopupManager.h"
16#include "Popup_Result.h"
17#include "GameFramework/PlayerController.h"
18#include "Engine/World.h"
19#include "Kismet/GameplayStatics.h"
20#include "Onepiece/Onepiece.h"
21
23{
25 bIsTimerActive = false;
26
27 PrimaryActorTick.bCanEverTick = true;
28
29 // RoomId, RoomLevel은 Host가 생성합니다
30 RoomId = 0;
31 RoomLevel = 1;
32
36 Bot.is_active = true;
37
38 Bot.my_avatar = TEXT("[BOT]");
39 Bot.my_color = TEXT("[BOT]");
40}
41
42void ALingoGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
43{
44 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
45
46 DOREPLIFETIME(ALingoGameState, RemainMissionTime);
47 DOREPLIFETIME(ALingoGameState, bIsTimerActive);
48 DOREPLIFETIME(ALingoGameState, QuestType);
49 DOREPLIFETIME(ALingoGameState, RoomId);
50 DOREPLIFETIME(ALingoGameState, RoomLevel);
51
52 DOREPLIFETIME(ALingoGameState, ReadScenarioData);
54 DOREPLIFETIME(ALingoGameState, ReqReadResult);
55 DOREPLIFETIME(ALingoGameState, ReadResult);
56
57 DOREPLIFETIME(ALingoGameState, ListenScenarioData);
59 DOREPLIFETIME(ALingoGameState, ReqListenResult);
60 DOREPLIFETIME(ALingoGameState, ListenResult);
62}
63
65{
66 Super::BeginPlay();
67
68 // Host(서버)만 RoomId를 생성합니다
69 if (HasAuthority() && RoomId == 0)
70 {
72
73 // TODO임시
74 this->RoomLevel = 2;
75
76 PRINTLOG(TEXT("[LingoGameState] RoomId generated by Host: %lld"), RoomId);
77 }
78}
79
80void ALingoGameState::Tick(float DeltaSeconds)
81{
82 Super::Tick(DeltaSeconds);
83
84 // 서버에서만 타이머 처리
85 if (!HasAuthority() || !bIsTimerActive)
86 return;
87
88 RemainMissionTime -= DeltaSeconds;
89
90 if (RemainMissionTime <= 0.f)
91 {
94 }
95}
96
97void ALingoGameState::AddWrongReadAnswer(const int32 InValue)
98{
99 if (!WrongReadAnswerList.Contains(InValue))
100 {
101 WrongReadAnswerList.Add(InValue);
102 }
103}
104
106{
107 if (!WrongListenAnswerList.Contains(InValue))
108 {
109 WrongListenAnswerList.Add(InValue);
110 }
111}
112
113void ALingoGameState::SetCompassVisibilityByTag(FName Tag, bool bVisible)
114{
115 if (!HasAuthority()) return;
116
118}
119
120void ALingoGameState::SetCompassVisibilityByClass(TSubclassOf<AActor> ActorClass, bool bVisible)
121{
122 if (!HasAuthority()) return;
123
124 Multicast_SetCompassVisibilityByClass(ActorClass, bVisible);
125}
126
128{
129 if (!HasAuthority()) return;
130
132}
133
134void ALingoGameState::Multicast_SetAllCompassVisibility_Implementation(bool bVisible)
135{
136 for (TActorIterator<AActor> It(GetWorld()); It; ++It)
137 {
138 // 플레이어는 제외 (항상 표시)
139 if (It->IsA(APlayerActor::StaticClass()))
140 continue;
141
142 if (ICompassTargetInterface* Target = Cast<ICompassTargetInterface>(*It))
143 {
144 Target->SetShowOnCompass(bVisible);
145 }
146 }
147}
148
149void ALingoGameState::Multicast_SetCompassVisibilityByClass_Implementation(TSubclassOf<AActor> ActorClass,
150 bool bVisible)
151{
152 TArray<AActor*> FoundActors;
153 UGameplayStatics::GetAllActorsOfClass(GetWorld(), ActorClass, FoundActors);
154
155 for (AActor* Actor : FoundActors)
156 {
157 if (ICompassTargetInterface* Target = Cast<ICompassTargetInterface>(Actor))
158 {
159 Target->SetShowOnCompass(bVisible);
160 }
161 }
162}
163
164void ALingoGameState::Multicast_SetCompassVisibilityByTag_Implementation(FName Tag, bool bVisible)
165{
166 TArray<AActor*> FoundActors;
167 UGameplayStatics::GetAllActorsWithTag(GetWorld(), Tag, FoundActors);
168
169 for (AActor* Actor : FoundActors)
170 {
171 if (ICompassTargetInterface* Target = Cast<ICompassTargetInterface>(Actor))
172 {
173 Target->SetShowOnCompass(bVisible);
174 }
175 }
176}
177
179{
180 // 반드시 서버 전용
181 if (!HasAuthority())
182 return;
183
185 this->ReadScenarioData = InResponseData;
186
187 // 미션 타이머 시작
189
190 // 모든 플레이어의 ReadQuest 진행 상태 설정
191 for (APlayerState* PS : PlayerArray)
192 {
193 if (ALingoPlayerState* LingoPS = Cast<ALingoPlayerState>(PS))
194 {
195 LingoPS->SetReadQuestIng(true);
196 }
197 }
198
199 if (HasAuthority())
200 {
203 }
204}
205
207{
208 // 반드시 서버 전용
209 if (!HasAuthority())
210 return;
211
213 this->ListenScenarioData = InResponseData;
214
215 // 미션 타이머 시작
217
218 // 모든 플레이어의 ListenQuest 진행 상태 설정
219 for (APlayerState* PS : PlayerArray)
220 {
221 if (ALingoPlayerState* LingoPS = Cast<ALingoPlayerState>(PS))
222 {
223 LingoPS->SetListenQuestIng(true);
224 }
225 }
226
227 if (HasAuthority())
228 {
231 }
232}
233
234void ALingoGameState::SetRoomLevel(int32 InRoomLevel)
235{
236 // 서버에서만 실행
237 if (!HasAuthority())
238 {
239 PRINTLOG(TEXT("[SetRoomLevel] Failed: Not authority"));
240 return;
241 }
242
243 RoomLevel = InRoomLevel;
244 PRINTLOG(TEXT("[SetRoomLevel] RoomLevel set to %d"), RoomLevel);
245
246 // 서버(Host)는 OnRep가 호출되지 않으므로 직접 델리게이트 브로드캐스트
247 // 클라이언트는 Replication으로 인해 OnRep_RoomLevel이 호출되어 델리게이트가 브로드캐스트됨
248 OnRoomLevelUpdated.Broadcast(RoomLevel);
249}
250
252{
253 // 서버에서만 실행
254 if (!HasAuthority())
255 return;
256
257 this->TimeLimit = InTimeLimit;
258 this->RemainMissionTime = InTimeLimit;
259 this->bIsTimerActive = true;
260
261 if (ANetworkBroadcastActor* BroadcastActor = ANetworkBroadcastActor::Get(this))
262 BroadcastActor->SendUpdateMissionTimerState(true, InTimeLimit, this);
263}
264
266{
267 if (!HasAuthority())
268 return;
269
270 // 이미 타이머가 멈춰있으면 중복 호출 방지
271 if (!bIsTimerActive)
272 return;
273
274 bIsTimerActive = false;
275
276 // NetworkBroadcastActor를 통해 모든 클라이언트에 타이머 중지 알림
277 if (ANetworkBroadcastActor* BroadcastActor = ANetworkBroadcastActor::Get(this))
278 {
279 BroadcastActor->SendUpdateMissionTimerState(false, 0.0f, this);
280 }
281}
282
284{
285 this->RemainMissionTime = FMath::Max(0.f, RemainMissionTime - InValue);
286}
287
288void ALingoGameState::UpdateRemainMissionTime(const float InTimeLimit)
289{
290 this->TimeLimit = InTimeLimit;
291 this->RemainMissionTime = InTimeLimit;
292}
293
295{
296 if (!HasAuthority())
297 return;
298
299 bIsTimerActive = false;
300
301 // NetworkBroadcastActor를 통해 모든 클라이언트에 타이머 종료 알림
302 if (ANetworkBroadcastActor* BroadcastActor = ANetworkBroadcastActor::Get(this))
303 BroadcastActor->SendUpdateMissionTimerState(false, 0.0f, this);
304
306
307 PRINTLOG( TEXT("[GameState] Mission Timer Ended - Sending: %s"), *EndMessage);
308
309 // Read, Listen Quest일 때 자동으로 결과 팝업 표시
311 {
312 if (auto Popup = UPopupManager::ShowPopupAs<UPopup_Result>(GetWorld(), EPopupType::Result))
313 {
314 Popup->InitPopup(QuestType);
315 }
316 }
317
318 // 모든 플레이어에게 메시지 전송
319 if (UWorld* World = GetWorld())
320 {
321 for (FConstPlayerControllerIterator It = World->GetPlayerControllerIterator(); It; ++It)
322 {
323 if (APlayerController* PC = It->Get())
324 {
325 if (APlayerActor* PlayerActor = Cast<APlayerActor>(PC->GetPawn()))
326 {
327 PlayerActor->OnGameMessage(EndMessage);
328 }
329 }
330 }
331 }
332}
333
334
335void ALingoGameState::Multicast_UpdateQuestType_Implementation(const EQuestType InQuestType)
336{
337 // 0.5초 딜레이 후 팝업 표시
338 if (UWorld* World = GetWorld())
339 {
340 FTimerHandle TimerHandle;
341 World->GetTimerManager().SetTimer(TimerHandle, [this, InQuestType]()
342 {
343 OnQuestScenarioDataUpdated.Broadcast();
344 }, 0.5f, false);
345 }
346}
347
348
349void ALingoGameState::Multicast_ShowReadQuestPopup_Implementation(const FResponseReadScenario& InScenarioData)
350{
351 // 서버에서만 실행 (클라이언트는 OnRep_ReadScenarioData에서 처리)
352 if (!HasAuthority())
353 return;
354
355 // 1.0초 딜레이 후 팝업 표시 (플레이어 Role 동기화를 위함)
356 if (UWorld* World = GetWorld())
357 {
358 FTimerHandle TimerHandle;
359 World->GetTimerManager().SetTimer(TimerHandle, [this, InScenarioData]()
360 {
361 for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
362 {
363 if (APlayerController* PC = It->Get())
364 {
365 if (APlayerActor* PlayerActor = Cast<APlayerActor>(PC->GetPawn()))
366 {
367 PlayerActor->Cmd_Info();
368 }
369 }
370 }
371
372 }, 1.0f, false);
373 }
374}
375
376void ALingoGameState::Multicast_ShowListenQuestPopup_Implementation(const FResponseListenScenario& InScenarioData)
377{
378 // 서버에서만 실행 (클라이언트는 OnRep_ListenScenarioData에서 처리)
379 if (!HasAuthority())
380 return;
381
382 // 1.0초 딜레이 후 팝업 표시 (플레이어 Role 동기화를 위함)
383 if (UWorld* World = GetWorld())
384 {
385 FTimerHandle TimerHandle;
386 World->GetTimerManager().SetTimer(TimerHandle, [this, InScenarioData]()
387 {
388 for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
389 {
390 if (APlayerController* PC = It->Get())
391 {
392 if (APlayerActor* PlayerActor = Cast<APlayerActor>(PC->GetPawn()))
393 {
394 PlayerActor->Cmd_Info();
395 }
396 }
397 }
398
399 }, 1.0f, false);
400 }
401}
402
404{
405 OnQuestScenarioDataUpdated.Broadcast();
406
407 // 클라이언트에서 데이터가 복제된 후 팝업 표시
408 if (!HasAuthority())
409 {
410 // 데이터 유효성 확인
411 if (ReadScenarioData.target_data.Num() == 0)
412 {
413 PRINTLOG(TEXT("[OnRep_ReadScenarioData] Data is empty, skipping Cmd_Info"));
414 return;
415 }
416
417 // 1.0초 딜레이 후 Cmd_Info 호출 (플레이어 Role 동기화를 위함)
418 if (UWorld* World = GetWorld())
419 {
420 FTimerHandle TimerHandle;
421 World->GetTimerManager().SetTimer(TimerHandle, [this]()
422 {
423 for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
424 {
425 if (APlayerController* PC = It->Get())
426 {
427 if (APlayerActor* PlayerActor = Cast<APlayerActor>(PC->GetPawn()))
428 {
429 PlayerActor->Cmd_Info();
430 }
431 }
432 }
433 }, 1.0f, false);
434 }
435 }
436}
437
442
444{
445 OnQuestScenarioDataUpdated.Broadcast();
446
447 // 클라이언트에서 데이터가 복제된 후 팝업 표시
448 if (!HasAuthority())
449 {
450 // 데이터 유효성 확인
451 if (ListenScenarioData.full_data.Kor.IsEmpty() &&
452 ListenScenarioData.word_data1.Kor.IsEmpty() &&
454 {
455 PRINTLOG(TEXT("[OnRep_ListenScenarioData] Data is empty, skipping Cmd_Info"));
456 return;
457 }
458
459 // 1.0초 딜레이 후 Cmd_Info 호출 (플레이어 Role 동기화를 위함)
460 if (UWorld* World = GetWorld())
461 {
462 FTimerHandle TimerHandle;
463 World->GetTimerManager().SetTimer(TimerHandle, [this]()
464 {
465 for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
466 {
467 if (APlayerController* PC = It->Get())
468 {
469 if (APlayerActor* PlayerActor = Cast<APlayerActor>(PC->GetPawn()))
470 {
471 PlayerActor->Cmd_Info();
472 }
473 }
474 }
475 }, 1.0f, false);
476 }
477 }
478}
479
484
486{
487 OnRoomIdUpdated.Broadcast(RoomId);
488}
489
494
495void ALingoGameState::MulticastRPC_SendChat_Implementation(FResponseUserMe sendUser, const FText& inMessage, int32 PlayerIndex)
496{
497 // MainWidget의 SendChatMessage() 함수 호출
498 if (auto* PC = Cast<APlayerControl>(GetWorld()->GetFirstPlayerController()))
499 {
500 // PRINTLOG(TEXT("[SendChat] ALingoGameState::MulticastRPC_SendChat - %s"), *inMessage.ToString());
501 if (auto* player = Cast<APlayerActor>(PC->GetPawn()))
502 {
503 player->GetMainWidget()->SendChatMessage(sendUser, inMessage, PlayerIndex);
504 }
505 }
506}
EQuestType
네트워크 복제를 위한 전역 브로드캐스트 Actor
Declares the player-controlled character actor.
APlayerControl 선언에 대한 Doxygen 주석을 제공합니다.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
Definition GameLogging.h:30
TArray< int32 > WrongListenAnswerList
int64 RoomId
방 ID (Host가 생성하고 Guest들과 공유)
void SetRoomLevel(int32 InRoomLevel)
방 레벨 설정 (서버 전용)
void Multicast_SetCompassVisibilityByTag(FName Tag, bool bVisible)
void SetListenScenarioData(const FResponseListenScenario &InResponseData)
void Multicast_SetAllCompassVisibility(bool bVisible)
FResponseReadScenario ReadScenarioData
virtual void Tick(float DeltaSeconds) override
FResponseListenScenario TryListenAnswerData
void SetReadScenarioData(const FResponseReadScenario &InResponseData)
void UpdateRemainMissionTime(const float InTimeLimit)
void Multicast_UpdateQuestType(const EQuestType InQuestType)
virtual void BeginPlay() override
FOnRoomLevelUpdated OnRoomLevelUpdated
void OnMissionTimerEnd()
타이머 종료 시 호출됩니다 (서버에서만 실행)
void Multicast_SetCompassVisibilityByClass(TSubclassOf< AActor > ActorClass, bool bVisible)
void StopMissionTimer()
미션 타이머를 중지합니다
FResponseListenResult ListenResult
void Multicast_ShowListenQuestPopup(const FResponseListenScenario &InScenarioData)
void DecreaseMissionTimer(const float InValue)
void SetCompassVisibilityByClass(TSubclassOf< AActor > ActorClass, bool bVisible)
void SetCompassVisibilityByTag(FName Tag, bool bVisible)
FResponseReadResult ReqReadResult
void SetAllCompassVisibility(bool bVisible)
FResponseReadResult ReadResult
void AddWrongListenAnswer(int32 Value)
FOnQuestScenarioDataUpdated OnQuestScenarioDataUpdated
virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty > &OutLifetimeProps) const override
FResponseListenScenario ListenScenarioData
bool bIsTimerActive
타이머 활성화 상태
TArray< int32 > WrongReadAnswerList
FOnListenResultUpdated OnListenResultUpdated
FOnReadResultUpdated OnReadResultUpdated
void StartMissionTimer(float TimeLimit)
미션 타이머를 시작합니다 (서버에서만 호출)
void Multicast_ShowReadQuestPopup(const FResponseReadScenario &InScenarioData)
FOnRoomIdUpdated OnRoomIdUpdated
void AddWrongReadAnswer(int32 Value)
FResponseListenResult ReqListenResult
FResponseUserMe Bot
네트워크 복제를 위한 전역 브로드캐스트 Actor
static ANetworkBroadcastActor * Get(const UObject *WorldContextObject)
싱글톤 인스턴스 가져오기
Main character driven directly by the player.
static int64 GetUnixTimestampInt64()
static float GetMissionPlayTime()
static FString GetStageEndMessage(const EQuestType QuestType)
static const int32 BotID
Definition Onepiece.h:56
static const FString BotName
Definition Onepiece.h:57
TArray< FScenarioTargetData > target_data
FString Kor