""" 세계관 설명 페이지 모듈 """ import streamlit as st from datetime import datetime from config.constants import SUGGESTED_WORLD_QUESTIONS from modules.world_generator import ( master_answer_question, generate_world_expansion ) def world_description_page(): """세계관 설명 및 질문 페이지 구현""" st.header("2️⃣ 세계관 설명") # 마스터 메시지 표시 st.markdown(f"
{st.session_state.master_message}
", unsafe_allow_html=True) # 세계관 설명 표시 - 단락 구분 개선 world_desc_paragraphs = st.session_state.world_description.split("\n\n") formatted_desc = "" for para in world_desc_paragraphs: formatted_desc += f"

{para}

\n" st.markdown(f"
{formatted_desc}
", unsafe_allow_html=True) # "다른 세계 탐험하기" 버튼 추가 if st.button("🌍 다른 세계 탐험하기", key="explore_other_world", use_container_width=True): # 세션 상태 초기화 (일부만) for key in ['theme', 'world_description', 'world_generated', 'world_accepted', 'question_answers', 'question_count', 'current_location']: if key in st.session_state: del st.session_state[key] # 테마 선택 화면으로 돌아가기 st.session_state.stage = 'theme_selection' st.session_state.master_message = "새로운 세계를 탐험해보세요!" st.rerun() # 탭 기반 UI로 변경 tabs = st.tabs(["세계관 확장", "질문하기", "탐험 시작"]) # 세계관 확장 탭 with tabs[0]: world_expansion_tab() # 질문하기 탭 with tabs[1]: world_question_tab() # 탐험 시작 탭 with tabs[2]: exploration_start_tab() def world_expansion_tab(): """세계관 확장 탭 내용""" st.subheader("세계관 이어서 작성") # 설명 추가 st.markdown("""

세계관을 더 풍부하게 만들어보세요. AI 마스터에게 특정 부분을 확장해달라고 요청하거나, 직접 내용을 추가할 수 있습니다.

추가된 내용은 기존 세계관과 자연스럽게 통합되어 더 깊이 있는 세계를 만들어갑니다.

""", unsafe_allow_html=True) # 직접 입력 옵션 추가 expand_method = st.radio( "확장 방법 선택:", ["AI 마스터에게 맡기기", "직접 작성하기"], horizontal=True ) # AI 확장 선택 시 if expand_method == "AI 마스터에게 맡기기": handle_ai_expansion() # 직접 작성 선택 시 else: handle_manual_expansion() def handle_ai_expansion(): """AI가 세계관을 확장하는 기능 처리""" # 확장할 주제 선택 (더 구체적인 세계관 생성 유도) expansion_topics = { "역사와 전설": "세계의 역사적 사건, 신화, 전설적 영웅 등에 대한 이야기를 확장합니다.", "마법/기술 체계": "세계의 마법 시스템이나 기술 체계의 작동 방식과 한계를 자세히 설명합니다.", "종족과 문화": "세계에 존재하는 다양한 종족들과 그들의 문화, 관습, 생활 방식을 확장합니다.", "정치 체계와 세력": "권력 구조, 주요 세력 간의 관계, 정치적 갈등 등을 더 자세히 설명합니다.", "지리와 환경": "세계의 지리적 특성, 주요 지역, 기후, 자연환경에 대해 확장합니다.", "현재 갈등과 위기": "세계에서 진행 중인 갈등, 위기, 중요한 문제에 대해 자세히 설명합니다." } topic_options = list(expansion_topics.keys()) topic_descriptions = list(expansion_topics.values()) # 설명과 함께 확장 주제 선택 expansion_topic_idx = st.selectbox( "확장할 세계관 요소를 선택하세요:", range(len(topic_options)), format_func=lambda i: topic_options[i] ) expansion_topic = topic_options[expansion_topic_idx] # 선택한 주제에 대한 설명 표시 st.markdown(f"""

{topic_descriptions[expansion_topic_idx]}

""", unsafe_allow_html=True) # 확장 버튼 누르기 전과 후의 상태 관리 if 'continuation_generated' not in st.session_state: st.session_state.continuation_generated = False if not st.session_state.continuation_generated: if st.button("세계관 확장하기", key="expand_world"): with st.spinner("이어질 내용을 생성 중..."): try: # 확장 내용 생성 st.session_state.continuation_text = generate_world_expansion( st.session_state.world_description, st.session_state.theme, expansion_topic ) st.session_state.continuation_generated = True except Exception as e: st.error(f"내용 생성 중 오류 발생: {e}") # 오류 발생 시 백업 응답 st.session_state.continuation_text = "이 세계는 더 많은 비밀과 모험으로 가득 차 있습니다. 숨겨진 장소와 만날 수 있는 흥미로운 캐릭터들이 여러분을 기다리고 있습니다." st.session_state.continuation_generated = True st.rerun() # 생성된 내용이 있으면 표시 if st.session_state.continuation_generated: # 생성된 내용과 어떻게 반영되는지 시각적으로 표시 st.subheader("확장된 세계관 내용:") st.info("다음 내용이 세계관에 추가됩니다. '이 내용으로 적용하기'를 클릭하면 세계관에 반영됩니다.") # 단락 나누기 - 가독성 개선 continuation_paragraphs = st.session_state.continuation_text.split("\n\n") formatted_continuation = "" for para in continuation_paragraphs: formatted_continuation += f"

{para}

\n" st.markdown(f"
{formatted_continuation}
", unsafe_allow_html=True) # 적용 버튼과 다시 생성 버튼 병렬 배치 col1, col2 = st.columns(2) with col1: if st.button("이 내용으로 적용하기", key="apply_expansion"): # 세계 설명에 추가 st.session_state.world_description += "\n\n## " + expansion_topic + "\n" + st.session_state.continuation_text # 상태 초기화 st.session_state.continuation_generated = False if "continuation_text" in st.session_state: del st.session_state.continuation_text st.session_state.master_message = "세계관이 더욱 풍부해졌습니다! 이 세계에 대해 더 궁금한 점이 있으신가요?" st.success("세계관이 성공적으로 확장되었습니다!") st.rerun() with col2: if st.button("다시 생성하기", key="regenerate_expansion"): # 내용 다시 생성하도록 상태 초기화 st.session_state.continuation_generated = False if "continuation_text" in st.session_state: del st.session_state.continuation_text st.rerun() def handle_manual_expansion(): """사용자가 직접 세계관을 확장하는 기능 처리""" st.write("세계관에 추가하고 싶은 내용을 직접 작성해보세요:") user_continuation = st.text_area("세계관 추가 내용:", height=200) # 사용성 개선: 무한 추가 방지를 위한 확인 메시지 if user_continuation and st.button("내용 추가하기", key="add_user_content"): # 미리보기 표시 st.subheader("추가될 내용:") st.info("다음 내용이 세계관에 추가됩니다. 내용이 올바른지 확인하세요.") # 단락 나누기 - 가독성 개선 user_paragraphs = user_continuation.split("\n\n") formatted_user_content = "" for para in user_paragraphs: formatted_user_content += f"

{para}

\n" st.markdown(f"
{formatted_user_content}
", unsafe_allow_html=True) # 확인 후 추가 confirm = st.checkbox("위 내용을 세계관에 추가하시겠습니까?", key="confirm_add_content") if confirm and st.button("확인 후 추가하기", key="confirm_add_user_content"): # 작성한 내용 추가 st.session_state.world_description += "\n\n## 직접 추가한 세계관 내용\n" + user_continuation st.session_state.master_message = "직접 작성하신 내용이 세계관에 추가되었습니다! 이 세계가 더욱 풍부해졌습니다." st.success("세계관에 내용이 성공적으로 추가되었습니다!") st.rerun() def world_question_tab(): """세계관 질문 탭 내용""" st.subheader("세계관에 대한 질문") # 설명 추가 st.markdown("""

세계에 대해 궁금한 점을 마스터에게 질문해보세요. 세계의 역사, 문화, 종족, 마법/기술 체계 등에 대한 질문을 할 수 있습니다.

마스터의 답변은 세계관에 추가되어 더 풍부한 배경을 만들어갑니다.

""", unsafe_allow_html=True) # 질문 처리 상태 관리 if 'question_processing' not in st.session_state: st.session_state.question_processing = False if 'selected_suggested_question' not in st.session_state: st.session_state.selected_suggested_question = None if 'world_questions_history' not in st.session_state: st.session_state.world_questions_history = [] # 제안된 질문 표시 st.write("제안된 질문:") question_cols = st.columns(2) for i, q in enumerate(SUGGESTED_WORLD_QUESTIONS): with question_cols[i % 2]: # 토글 버튼으로 질문 선택 is_selected = st.checkbox(q, key=f"toggle_q_{i}", value=(st.session_state.selected_suggested_question == q)) if is_selected: st.session_state.selected_suggested_question = q elif st.session_state.selected_suggested_question == q: st.session_state.selected_suggested_question = None # 선택된 질문이 있으면 질문하기 버튼 표시 if st.session_state.selected_suggested_question: st.markdown("
", unsafe_allow_html=True) st.success(f"'{st.session_state.selected_suggested_question}' 질문이 선택되었습니다.") # 직접 질문 입력 섹션 st.markdown("
", unsafe_allow_html=True) st.write("### 직접 질문 입력") # 기본값 설정 (선택된 질문이 있으면 해당 질문 표시) default_question = st.session_state.get('custom_question_value', st.session_state.get('selected_suggested_question', '')) # 폼 사용으로 무한 생성 방지 with st.form(key="world_question_form"): custom_question = st.text_input("질문 내용:", value=default_question, key="custom_world_question") submit_question = st.form_submit_button("질문하기", use_container_width=True, disabled=st.session_state.question_processing) # 질문이 제출되었을 때 if submit_question and (custom_question or st.session_state.selected_suggested_question): process_world_question(custom_question or st.session_state.selected_suggested_question) # 이전 질문 및 답변 표시 if st.session_state.world_questions_history: st.markdown("
", unsafe_allow_html=True) st.write("### 이전 질문 및 답변") for i, qa in enumerate(reversed(st.session_state.world_questions_history)): with st.expander(f"Q: {qa['question']} ({qa['timestamp']})"): st.markdown(qa['answer']) def process_world_question(question): """세계관 질문 처리 함수""" # 이미 처리 중이 아닐 때만 실행 if not st.session_state.question_processing: st.session_state.question_processing = True # 응답 표시할 플레이스홀더 생성 response_placeholder = st.empty() response_placeholder.info("마스터가 답변을 작성 중입니다... 잠시만 기다려주세요.") # 질문 처리 및 답변 생성 try: answer = master_answer_question( question, st.session_state.world_description, st.session_state.theme ) # 질문과 답변을 세션 상태에 저장 qa_pair = { "question": question, "answer": answer, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } st.session_state.world_questions_history.append(qa_pair) # 세계관에 질문과 답변 추가 st.session_state.world_description += f"\n\n## 질문: {question}\n{answer}" # 단락 구분 적용 answer_paragraphs = answer.split("\n\n") formatted_answer = "" for para in answer_paragraphs: formatted_answer += f"

{para}

\n" # 응답 표시 response_placeholder.markdown(f"""
질문: {question}
{formatted_answer}
""", unsafe_allow_html=True) # 상태 초기화 st.session_state.master_message = "질문에 답변했습니다. 더 궁금한 점이 있으신가요?" except Exception as e: st.error(f"응답 생성 중 오류가 발생했습니다: {e}") response_placeholder.error("질문 처리 중 오류가 발생했습니다. 다시 시도해주세요.") finally: # 처리 완료 상태로 변경 st.session_state.question_processing = False st.session_state.selected_suggested_question = None st.session_state.custom_question_value = '' def exploration_start_tab(): """탐험 시작 탭 내용""" st.subheader("탐험 시작하기") # 설명 추가 st.markdown("""

모험을 시작할 지역을 선택하고 캐릭터 생성으로 진행하세요.

선택한 지역은 캐릭터가 모험을 시작하는 첫 장소가 됩니다.

""", unsafe_allow_html=True) # 시작 지점 선택 if 'available_locations' in st.session_state and st.session_state.available_locations: st.write("#### 시작 지점 선택") st.write("모험을 시작할 위치를 선택하세요:") # 사용성 개선: 선택된 위치를 표시 selected_location = st.session_state.get('current_location', '') # 시작 지점 그리드 표시 location_cols = st.columns(3) for i, location in enumerate(st.session_state.available_locations): with location_cols[i % 3]: # 현재 선택된 위치인 경우 다른 스타일로 표시 if location == selected_location: st.markdown(f"""
✓ {location} (선택됨)
""", unsafe_allow_html=True) # 선택 취소 버튼 if st.button("선택 취소", key=f"unselect_loc_{i}"): st.session_state.current_location = "" st.rerun() else: if st.button(location, key=f"start_loc_{i}", use_container_width=True): st.session_state.current_location = location st.session_state.master_message = f"{location}에서 모험을 시작합니다. 이제 캐릭터를 생성할 차례입니다." st.rerun() # 캐릭터 생성으로 이동 버튼 st.write("#### 캐릭터 생성") st.write("세계를 충분히 탐색했다면, 이제 당신의 캐릭터를 만들어 모험을 시작할 수 있습니다.") # 선택된 시작 위치 없으면 경고 if not st.session_state.get('current_location'): st.warning("캐릭터 생성으로 진행하기 전에 시작 지점을 선택해주세요!") proceed_button = st.button("캐릭터 생성으로 진행", key="to_character_creation", use_container_width=True, disabled=True) else: proceed_button = st.button("캐릭터 생성으로 진행", key="to_character_creation", use_container_width=True) if proceed_button: st.session_state.stage = 'character_creation' st.session_state.master_message = "이제 이 세계에서 모험을 떠날 당신의 캐릭터를 만들어 볼까요?" st.rerun()