File size: 28,378 Bytes
b7d75f3
 
 
 
 
 
 
 
 
 
c62158d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7d75f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66414a3
b7d75f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66414a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7d75f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
"""

캐릭터 생성 및 관리를 위한 유틸리티 모듈

"""
import random
import streamlit as st
import re
from modules.ai_service import generate_gemini_text
from modules.item_manager import initialize_inventory
from config.constants import PROFESSION_KEY_STATS, BACKGROUND_TAGS, ABILITY_NAMES

# 직업별 아이콘 맵핑
PROFESSION_ICONS = {
    '마법사': '🧙',
    '전사': '⚔️',
    '도적': '🗡️',
    '성직자': '📿',
    '음유시인': '🎭',
    '연금술사': '⚗️',
    '우주 파일럿': '🚀',
    '사이버 해커': '💻',
    '생체공학자': '🧬',
    '보안 요원': '🛡️',
    '외계종족 전문가': '👽',
    '기계공학자': '⚙️',
    '정보 브로커': '📡',
    '밀수업자': '📦',
    '저항군 요원': '🕵️',
    '엘리트 경비원': '💂',
    '스카운터': '🔭',
    '의료 기술자': '💉'
}

# 종족별 아이콘 맵핑
RACE_ICONS = {
    '인간': '👤',
    '엘프': '🧝',
    '드워프': '🧔',
    '하플링': '🧒',
    '오크': '👹',
    '고블린': '👺',
    '드라코니안': '🐲',
    '안드로이드': '🤖',
    '외계인 하이브리드': '👽',
    '변형 인류': '🧟',
    '네뷸런': '👾',
    '크로노스피어': '⏱️',
    '우주 유목민': '🌠',
    '변이체': '🧬',
    '강화인류': '🦾',
    '생체기계': '🦿',
    '숙주': '🕸️',
    '정신감응자': '🔮',
    '저항자': '✊'
}

# 종족별 능력치 보너스
RACE_BONUSES = {
    '인간': {'모든 능력치': '+1'},
    '엘프': {'DEX': '+2', 'INT': '+1'},
    '드워프': {'CON': '+2', 'STR': '+1'},
    '하플링': {'DEX': '+2', 'CHA': '+1'},
    '오크': {'STR': '+2', 'CON': '+1'},
    '고블린': {'DEX': '+2', 'INT': '+1'},
    '드라코니안': {'STR': '+2', 'CHA': '+1'},
    '안드로이드': {'INT': '+2', 'STR': '+1'},
    '외계인 하이브리드': {'WIS': '+2', 'CHA': '+1'},
    '변형 인류': {'DEX': '+2', 'CON': '+1'},
    '네뷸런': {'INT': '+2', 'WIS': '+1'},
    '크로노스피어': {'INT': '+2', 'DEX': '+1'},
    '우주 유목민': {'CON': '+2', 'WIS': '+1'},
    '변이체': {'CON': '+2', 'STR': '+1'},
    '강화인류': {'STR': '+2', 'DEX': '+1'},
    '생체기계': {'STR': '+1', 'INT': '+2'},
    '숙주': {'CON': '+2', 'WIS': '+1'},
    '정신감응자': {'WIS': '+2', 'CHA': '+1'},
    '저항자': {'DEX': '+1', 'CON': '+2'}
}

# 종족별 특수 능력
RACE_ABILITIES = {
    '인간': '적응력: 하루에 한 번 주사위를 다시 굴릴 수 있습니다.',
    '엘프': '암시야: 어두운 곳에서도 잘 볼 수 있습니다.',
    '드워프': '내구력: 독성에 대한 저항력이 있습니다.',
    '하플링': '행운: 주사위 결과가 1이 나오면 다시 굴릴 수 있습니다.',
    '오크': '끈질김: 체력이 0이 되어도 1턴 더 활동할 수 있습니다.',
    '고블린': '약삭빠름: 은신 판정에 +2 보너스를 받습니다.',
    '드라코니안': '용의 숨결: 불을 내뿜을 수 있습니다.',
    '안드로이드': '계산 능력: 수학적 판정에 +3 보너스를 받습니다.',
    '외계인 하이브리드': '텔레파시: 타인의 마음을 읽을 수 있습니다.',
    '변형 인류': '적응력: 유해한 환경에 저항할 수 있습니다.',
    '네뷸런': '에너지 흡수: 에너지 공격을 흡수할 수 있습니다.',
    '크로노스피어': '시간 감각: 시간 흐름을 느리게 할 수 있습니다.',
    '우주 유목민': '우주 생존: 진공에서도 짧은 시간 살 수 있습니다.',
    '변이체': '재생 능력: 턴마다 체력 1점을 회복합니다.',
    '강화인류': '싸이버 강화: 특정 행동에 보너스를 받습니다.',
    '생체기계': '에너지 저장: 에너지를 저장하고 방출할 수 있습니다.',
    '숙주': '공생: 기생체와 함께 강화된 능력을 사용할 수 있습니다.',
    '정신감응자': '원격 감지: 근처의 생명체를 감지할 수 있습니다.',
    '저항자': '반역: 정신 지배에 대한 저항력이 있습니다.'
}

# 종족별 설명
RACE_DESCRIPTIONS = {
    '인간': '적응력과 다재다능함으로 모든 환경에서 성공할 수 있습니다.',
    '엘프': '우아하고 장수하는 종족으로 예술과 마법에 뛰어납니다.',
    '드워프': '강인하고 끈질긴 종족으로 대장장이 기술과 광산 작업에 능숙합니다.',
    '하플링': '작지만 용감한 종족으로 행운과 민첩성이 뛰어납니다.',
    '오크': '강력하고 야만적인 종족으로 전투에 뛰어납니다.',
    '고블린': '교활하고 약삭빠른 종족으로 생존 능력이 뛰어납니다.',
    '드라코니안': '드래곤의 피를 물려받은 종족으로 원소 저항력이 있습니다.',
    '안드로이드': '인공 지능을 가진 기계 생명체로 논리적 사고에 뛰어납니다.',
    '외계인 하이브리드': '인간과 외계종의 혼혈로 독특한 능력을 가집니다.',
    '변형 인류': '유전자 조작을 통해 진화한 인류로 특수 능력을 가집니다.',
    '네뷸런': '에너지 기반 생명체로 물리적 형태를 변형할 수 있습니다.',
    '크로노스피어': '시간의 흐름을 조작할 수 있는 능력을 가진 존재입니다.',
    '우주 유목민': '우주 공간에서 세대를 거쳐 살아온 인류의 변종입니다.',
    '변이체': '방사능이나 화학물질에 의해 변이된 인간입니다.',
    '강화인류': '기계 장치를 통해 강화된 인간입니다.',
    '생체기계': '생물학적 요소와 기계가 융합된 존재입니다.',
    '숙주': '외계 생물체와 공생 관계를 맺은 인간입니다.',
    '정신감응자': '정신적 능력이 발달한 인간의 진화 형태입니다.',
    '저항자': '억압에 저항하며 생존한 강인한 인류입니다.'
}

# 직업별 주요 능력치
PROFESSION_STATS = {
    '마법사': ['INT', 'WIS'],
    '전사': ['STR', 'CON'],
    '도적': ['DEX', 'CHA'],
    '성직자': ['WIS', 'CHA'],
    '음유시인': ['CHA', 'DEX'],
    '연금술사': ['INT', 'DEX'],
    '우주 파일럿': ['DEX', 'INT'],
    '사이버 해커': ['INT', 'DEX'],
    '생체공학자': ['INT', 'WIS'],
    '보안 요원': ['STR', 'CON'],
    '외계종족 전문가': ['WIS', 'CHA'],
    '기계공학자': ['INT', 'DEX'],
    '정보 브로커': ['INT', 'CHA'],
    '밀수업자': ['DEX', 'CHA'],
    '저항군 요원': ['DEX', 'CON'],
    '엘리트 경비원': ['STR', 'DEX'],
    '스카운터': ['DEX', 'WIS'],
    '의료 기술자': ['INT', 'WIS']
}

# 직업별 시작 장비
PROFESSION_EQUIPMENT = {
    '마법사': ['마법서', '지팡이', '로브', '마법 재료 파우치', '양초 3개'],
    '전사': ['장검', '방패', '체인 메일', '배낭', '모험가 키트'],
    '도적': ['단검 2개', '가죽 갑옷', '도둑 도구 세트', '후드 망토', '물약 2개'],
    '성직자': ['메이스', '신성한 상징', '갑옷', '치유 키트', '기도문'],
    '음유시인': ['류트', '가죽 갑옷', '단검', '여행 의상 세트', '매력 도구'],
    '연금술사': ['연금술 키트', '로브', '약초 주머니', '실험 노트', '물약 3개'],
    '우주 파일럿': ['레이저 건', '우주복', '통신 장치', '내비게이션 도구', '응급 키트'],
    '사이버 해커': ['휴대용 컴퓨터', '임플란트 도구', '전자 장비 세트', '스텔스 장비', '데이터 칩'],
    '생체공학자': ['의료 키트', '연구 도구', '생체 샘플 세트', '데이터 패드', '실험 장비'],
    '보안 요원': ['에너지 소총', '방탄 조끼', '보안 키트', '통신 장치', '감시 장비'],
    '외계종족 전문가': ['번역기', '외계 유물', '연구 노트', '통신 장치', '생명 지원 시스템'],
    '기계공학자': ['공구 세트', '청사진', '부품 키트', '용접 장비', '분석 장치'],
    '정보 브로커': ['암호화된 데이터 패드', '은밀한 통신 장치', '위장 도구', '정보 칩', '비상금'],
    '밀수업자': ['블래스터 권총', '숨겨진 주머니 의류', '잠금해제 도구', '위조 신분증', '비상 탈출 키트'],
    '저항군 요원': ['숨겨진 무기', '암호화 통신 장치', '위장 키트', '임시 폭발물', '생존 장비'],
    '엘리트 경비원': ['충격봉', '방탄 유니폼', '감시 장치', '통신 이어피스', '신원 확인 장치'],
    '스카운터': ['망원경', '생존 키트', '지도 제작 도구', '위치 추적기', '휴대용 쉘터'],
    '의료 기술자': ['고급 의료 키트', '진단 스캐너', '응급 치료 약품', '수술 도구', '생체 모니터']
}

# 직업별 특수 기술
PROFESSION_SKILLS = {
    '마법사': '마법 시전: 다양한 마법 주문을 시전할 수 있으며, 스크롤에서 주문을 배우는 능력이 있습니다.',
    '전사': '전투 전문가: 모든 무기와 갑옷을 능숙하게 다루며, 전투 중 특별한 기동을 사용할 수 있습니다.',
    '도적': '교묘한 행동: 민첩성을 활용한 은밀한 공격과 함정 해제, 자물쇠 따기에 능숙합니다.',
    '성직자': '신성한 힘: 신성한 마법을 사용하여 치유하고 보호하며, 언데드를 물리칠 수 있습니다.',
    '음유시인': '바드의 영감: 음악을 통해 동료를 격려하고 적을 혼란시키는 마법적 효과를 만들어냅니다.',
    '연금술사': '물약 제조: 다양한 효과의 물약과 폭탄을 제조할 수 있는 지식이 있습니다.',
    '우주 파일럿': '우주선 조종: 어떤 종류의 우주선이든 능숙하게 조종하고 위험한 상황에서 탈출할 수 있습니다.',
    '사이버 해커': '시스템 침입: 컴퓨터 시스템과 보안 네트워크를 침투하고 조작할 수 있습니다.',
    '생체공학자': '생체 분석: 생명체의 생물학적 특성을 분석하고 수정할 수 있는 지식이 있습니다.',
    '보안 요원': '경계 태세: 항상 위험에 대비하여 경계 판정에 +2 보너스를 받습니다.',
    '외계종족 전문가': '외계어 이해: 다양한 외계 언어를 해석하고 의사소통할 수 있습니다.',
    '기계공학자': '장치 수리: 복잡한 기계와 장치를 수리하고 개선할 수 있는 지식이 있습니다.',
    '정보 브로커': '정보망: 유용한 정보를 찾거나 거래하는데 탁월한 능력이 있습니다.',
    '밀수업자': '은밀한 운송: 물건을 숨기고 감시를 피해 운반하는 기술이 있습니다.',
    '저항군 요원': '게릴라 전술: 열세한 상황에서도 효과적으로 전투하고 은신할 수 있습니다.',
    '엘리트 경비원': '경계 태세: 보안 시스템을 이해하고 침입자를 감지하는 능력이 뛰어납니다.',
    '스카운터': '지형 탐색: 위험한 지형을 안전하게 탐색하고 자원을 찾아낼 수 있습니다.',
    '의료 기술자': '응급 처치: 위급한 상황에서 부상을 치료하고 생명을 구할 수 있습니다.'
}

# 배경 태그 색상
BACKGROUND_TAGS_COLORS = {
    "영웅적": "#4CAF50",  # 녹색
    "비극적": "#F44336",  # 빨간색
    "신비로운": "#9C27B0",  # 보라색
    "학자": "#2196F3",  # 파란색
    "범죄자": "#FF9800",  # 주황색
    "전사": "#795548",  # 갈색
    "귀족": "#FFC107",  # 노란색
    "서민": "#607D8B",  # 회색
    "이방인": "#009688",  # 청록색
    "운명적": "#E91E63"   # 분홍색
}



def generate_professions(theme):
    """

    테마에 따른 직업 목록 반환

    

    Args:

        theme (str): 세계관 테마

        

    Returns:

        list: 직업 목록

    """
    professions = {
        'fantasy': ['마법사', '전사', '도적', '성직자', '음유시인', '연금술사'],
        'sci-fi': ['우주 파일럿', '사이버 해커', '생체공학자', '보안 요원', '외계종족 전문가', '기계공학자'],
        'dystopia': ['정보 브로커', '밀수업자', '저항군 요원', '엘리트 경비원', '스카운터', '의료 기술자']
    }
    return professions.get(theme, ['모험가', '전문가', '기술자'])

def generate_races(theme):
    """

    테마에 따른 종족 목록 반환

    

    Args:

        theme (str): 세계관 테마

        

    Returns:

        list: 종족 목록

    """
    races = {
        'fantasy': ['인간', '엘프', '드워프', '하플링', '오크', '고블린', '드라코니안'],
        'sci-fi': ['인간', '안드로이드', '외계인 하이브리드', '변형 인류', '네뷸런', '크로노스피어', '우주 유목민'],
        'dystopia': ['인간', '변이체', '강화인류', '생체기계', '숙주', '정신감응자', '저항자']
    }
    return races.get(theme, ['인간', '비인간', '신비종족'])

def generate_character_options(profession, theme):
    """

    직업과 테마에 기반한 캐릭터 배경 옵션 생성

    

    Args:

        profession (str): 선택한 직업

        theme (str): 세계관 테마

        

    Returns:

        list: 배경 스토리 옵션 목록

    """
    prompt = f"""

    당신은 TRPG 게임 마스터입니다. '{theme}' 테마의 세계에서 '{profession}' 직업을 가진 

    캐릭터의 3가지 다른 배경 스토리 옵션을 한국어로 제안해주세요. 



    각 옵션은 다음 요소를 포함해야 합니다:



    ## 삼위일체 구조

    1. **배경 서사**: 캐릭터가 겪은 결정적 사건 3개

    2. **도덕적 축**: 선택을 규정하는 2가지 원칙

    3. **정체성 기반**: 타인에게 설명하는 5초 자기소개



    ## 개성화를 위한 요소

    - 캐릭터만의 독특한 특성이나 버릇

    - 관계망 (가족, 멘토, 적대자 등)

    - 물리적 특징이나 외형적 특성



    ## 직업 연계성

    - 이 캐릭터가 해당 직업을 가지게 된 이유

    - 직업 관련 전문 기술이나 지식



    각 옵션을 120단어 내외로 작성해주세요.

    모든 문장은 완결된 형태로 작성하세요.

    

    다음 형식으로 반환해주세요:

    

    #옵션 1:

    (첫 번째 배경 스토리)

    

    #옵션 2:

    (두 번째 배경 스토리)

    

    #옵션 3:

    (세 번째 배경 스토리)

    """
    
    from src.modules.ai_service import generate_gemini_text
    response = generate_gemini_text(prompt, 800)
    
    # 옵션 분리
    options = []
    current_option = ""
    for line in response.split('\n'):
        if line.startswith('#옵션') or line.startswith('# 옵션') or line.startswith('옵션'):
            if current_option:
                options.append(current_option.strip())
            current_option = ""
        else:
            current_option += line + "\n"
    
    if current_option:
        options.append(current_option.strip())
    
    # 옵션이 3개 미만이면 백업 옵션 추가
    while len(options) < 3:
        options.append(f"당신은 {profession}으로, 험난한 세계에서 살아남기 위해 기술을 연마했습니다. 특별한 재능을 가지고 있으며, 자신의 운명을 개척하고자 합니다.")
    
    return options[:3]  # 최대 3개까지만 반환

def extract_background_tags(background_text):
    """

    배경 텍스트에서 태그를 추출하는 함수

    

    Args:

        background_text (str): 배경 스토리 텍스트

        

    Returns:

        list: 추출된 태그 목록

    """
    tags = []
    keyword_map = {
        "영웅": "영웅적", "구원": "영웅적", "정의": "영웅적", 
        "비극": "비극적", "상실": "비극적", "슬픔": "비극적", "고통": "비극적",
        "신비": "신비로운", "마법": "신비로운", "초자연": "신비로운", 
        "학자": "학자", "연구": "학자", "지식": "학자", "서적": "학자",
        "범죄": "범죄자", "도둑": "범죄자", "불법": "범죄자", "암흑가": "범죄자",
        "전사": "전사", "전투": "전사", "군인": "전사", "검술": "전사",
        "귀족": "귀족", "왕족": "귀족", "부유": "귀족", "상류층": "귀족",
        "서민": "서민", "평민": "서민", "일반인": "서민", "농부": "서민",
        "이방인": "이방인", "외지인": "이방인", "여행자": "이방인", "이주민": "이방인",
        "운명": "운명적", "예언": "운명적", "선택받은": "운명적"
    }
    
    for keyword, tag in keyword_map.items():
        if keyword.lower() in background_text.lower() and tag not in tags:
            tags.append(tag)
    
    # 최대 3개 태그 제한
    return tags[:3] if tags else ["신비로운"]  # 기본 태그 추가

def extract_background_tags(background_text):
    """

    배경 텍스트에서 태그를 추출하는 함수

    

    Args:

        background_text (str): 배경 스토리 텍스트

        

    Returns:

        list: 추출된 태그 목록

    """
    tags = []
    keyword_map = {
        "영웅": "영웅적", "구원": "영웅적", "정의": "영웅적", 
        "비극": "비극적", "상실": "비극적", "슬픔": "비극적", "고통": "비극적",
        "신비": "신비로운", "마법": "신비로운", "초자연": "신비로운", 
        "학자": "학자", "연구": "학자", "지식": "학자", "서적": "학자",
        "범죄": "범죄자", "도둑": "범죄자", "불법": "범죄자", "암흑가": "범죄자",
        "전사": "전사", "전투": "전사", "군인": "전사", "검술": "전사",
        "귀족": "귀족", "왕족": "귀족", "부유": "귀족", "상류층": "귀족",
        "서민": "서민", "평민": "서민", "일반인": "서민", "농부": "서민",
        "이방인": "이방인", "외지인": "이방인", "여행자": "이방인", "이주민": "이방인",
        "운명": "운명적", "예언": "운명적", "선택받은": "운명적"
    }
    
    for keyword, tag in keyword_map.items():
        if keyword.lower() in background_text.lower() and tag not in tags:
            tags.append(tag)
    
    # 최대 3개 태그 제한
    return tags[:3] if tags else ["신비로운"]  # 기본 태그 추가

def get_stat_info(stat, value, profession):
    """

    스탯별 색상 및 설명 제공

    

    Args:

        stat (str): 능력치 코드

        value (int): 능력치 값

        profession (str): 직업

        

    Returns:

        tuple: (색상 코드, 설명 텍스트)

    """
    # 스탯별 색상 설정 (낮음 - 중간 - 높음)
    if value < 8:
        color = "#F44336"  # 빨강 (낮음)
        level = "낮음"
    elif value < 12:
        color = "#FFC107"  # 노랑 (보통)
        level = "보통"
    elif value < 16:
        color = "#4CAF50"  # 초록 (높음)
        level = "높음"
    else:
        color = "#3F51B5"  # 파랑 (매우 높음)
        level = "매우 높음"
    
    # 직업별 스탯 적합성 설명
    if profession in PROFESSION_KEY_STATS and stat in PROFESSION_KEY_STATS[profession]:
        match = "핵심" if PROFESSION_KEY_STATS[profession][0] == stat else "중요"
        description = f"{level} - {match} 스탯"
    else:
        description = f"{level}"
    
    return color, description

def display_character_panel(character, location):
    """

    캐릭터 정보를 왼쪽 패널에 표시

    

    Args:

        character (dict): 캐릭터 정보

        location (str): 현재 위치

    """
    from modules.item_manager import display_inventory
    
    st.markdown("<div class='character-panel'>", unsafe_allow_html=True)
    st.write(f"## {character['profession']}")
    
    # 능력치 표시
    st.write("### 능력치")
    for stat, value in character['stats'].items():
        # 직업 정보 가져오기
        prof = character['profession']
        color, description = get_stat_info(stat, value, prof)
        
        st.markdown(f"""

        <div class='stat-box' style="border-left: 4px solid {color};">

            <span class='stat-name'>{stat}</span>

            <span class='stat-value'>{value}</span>

            <div style="font-size: 0.8rem; color: #aaaaaa; margin-top: 2px;">{description}</div>

        </div>

        """, unsafe_allow_html=True)
    
    # 인벤토리 표시
    st.write("### 인벤토리")
    display_inventory(character['inventory'])
    
    st.markdown("</div>", unsafe_allow_html=True)
    
    # 위치 정보
    st.markdown(f"""

    <div class='location-box' style='margin-bottom: 15px; padding: 12px; background-color: #2d3748; border-radius: 5px; text-align: center;'>

        <h3 style='margin: 0; color: #e0e0ff;'>현재 위치</h3>

        <div style='font-size: 1.2rem; font-weight: bold; margin-top: 8px;'>{location}</div>

    </div>

    """, unsafe_allow_html=True)

def initialize_character(profession, backstory, stats, theme):
    """

    캐릭터 초기화 및 인벤토리 설정

    

    Args:

        profession (str): 직업

        backstory (str): 배경 스토리

        stats (dict): 능력치

        theme (str): 게임 테마

        

    Returns:

        dict: 초기화된 캐릭터 정보

    """
    # 아이템 객체 리스트로 인벤토리 초기화
    inventory = initialize_inventory(theme)
    
    character = {
        'profession': profession,
        'backstory': backstory,
        'stats': stats,
        'inventory': inventory,
        'special_trait': None
    }
    
    return character

def ability_roll_section(placeholder):
    """

    능력치 주사위 굴리기 기능

    

    Args:

        placeholder (st.empty): 결과를 표시할 플레이스홀더

    """
    # 주사위 굴리기 관련 상태 초기화
    if 'dice_rolled' not in st.session_state:
        st.session_state.dice_rolled = False
    
    if 'reroll_used' not in st.session_state:
        st.session_state.reroll_used = False
        
    # 주사위 굴리기 설명 추가
    placeholder.markdown("""

    <div style='background-color: #2a3549; padding: 10px; border-radius: 5px; margin-bottom: 15px;'>

        <p>능력치는 각각 3D6(6면체 주사위 3개) 방식으로 결정됩니다.</p>

        <p>각 능력치는 3~18 사이의 값을 가지며, 평균값은 10-11입니다.</p>

        <p>14 이상은 뛰어난 능력, 16 이상은 탁월한 능력입니다.</p>

        <p><strong>다시 굴리기는 1번만 가능합니다.</strong></p>

    </div>

    """, unsafe_allow_html=True)
    
    # 주사위 굴리기 버튼
    if not st.session_state.dice_rolled and placeholder.button("주사위 굴리기", use_container_width=True, key="roll_ability_dice"):
        st.session_state.dice_rolled = True
        
        # 능력치 목록
        ability_names = ['STR', 'INT', 'DEX', 'CON', 'WIS', 'CHA']
        rolled_abilities = {}
        
        # 각 능력치별 주사위 굴리기 결과 애니메이션으로 표시
        ability_placeholders = {}
        for ability in ability_names:
            ability_placeholders[ability] = placeholder.empty()
        
        # 순차적으로 각 능력치 굴리기
        for ability in ability_names:
            # 3D6 주사위 결과 계산
            dice_rolls = [random.randint(1, 6) for _ in range(3)]
            total = sum(dice_rolls)
            
            # 결과 표시
            ability_placeholders[ability].markdown(f"""

            <div style='background-color: #1e2636; padding: 10px; border-radius: 5px; margin-bottom: 5px;'>

                <div style='display: flex; justify-content: space-between;'>

                    <span><strong>{ability}</strong></span>

                    <span>🎲 {dice_rolls[0]} + {dice_rolls[1]} + {dice_rolls[2]} = <strong>{total}</strong></span>

                </div>

            </div>

            """, unsafe_allow_html=True)
            rolled_abilities[ability] = total
        
        # 세션에 저장
        st.session_state.rolled_abilities = rolled_abilities
        st.rerun()

def generate_special_trait(theme, background_tags):
    """

    캐릭터의 특별한 특성 생성

    

    Args:

        theme (str): 게임 테마

        background_tags (list): 배경 태그 목록

        

    Returns:

        str: 생성된 특별한 특성

    """
    # 테마별 특성 목록
    fantasy_traits = [
        "마법에 대한 직관: 마법 관련 판정에 +1 보너스",
        "언어 재능: 하나의 추가 언어를 이해할 수 있음",
        "생존 본능: 위험 감지 판정에 +2 보너스",
        "전투 감각: 선제력 판정에 +1 보너스",
        "비밀 감지: 숨겨진 문이나 함정 찾기에 +2 보너스"
    ]
    
    scifi_traits = [
        "기계 친화력: 장치 조작 판정에 +1 보너스",
        "우주 적응: 저중력 환경 적응에 +2 보너스",
        "전술적 사고: 전투 전략 판정에 +1 보너스",
        "네트워크 감각: 정보 검색에 +2 보너스",
        "생체 회복: 휴식 시 추가 체력 회복"
    ]
    
    dystopia_traits = [
        "생존자 본능: 위험한 상황 탈출에 +1 보너스",
        "자원 절약: 소비품 사용 효율 +25%",
        "야간 시력: 어두운 곳에서 시각 판정에 불이익 없음",
        "불굴의 의지: 정신적 충격 저항에 +2 보너스",
        "전술적 직감: 교전 시 선제 행동 확률 +15%"
    ]
    
    # 태그에 따른 특성 선택 확률 조정
    has_hero = "영웅적" in background_tags
    has_scholarly = "학자" in background_tags
    has_tragic = "비극적" in background_tags
    has_criminal = "범죄자" in background_tags
    has_mysterious = "신비로운" in background_tags
    
    if theme == "fantasy":
        traits = fantasy_traits
        if has_hero:
            traits.append("운명의 보호: 하루에 한 번 치명적 공격을 일반 공격으로 낮출 수 있음")
        if has_scholarly:
            traits.append("비전학자: 마법 관련 지식 판정에 +2 보너스")
        if has_tragic:
            traits.append("고통의 힘: 체력이 절반 이하일 때 공격력 +1")
        if has_criminal:
            traits.append("그림자 걷기: 은신 판정에 +2 보너스")
        if has_mysterious:
            traits.append("신비한 직감: 하루에 한 번 주사위를 다시 굴릴 수 있음")
    elif theme == "sci-fi":
        traits = scifi_traits
        if has_hero:
            traits.append("영웅적 리더십: 아군 NPC 의사 결정에 영향력 +25%")
        if has_scholarly:
            traits.append("데이터 분석: 기술 장치 판독에 +2 보너스")
        if has_tragic:
            traits.append("역경의 경험: 위기 상황에서 판단력 +1")
        if has_criminal:
            traits.append("시스템 침투: 보안 해제 시도에 +2 보너스")
        if has_mysterious:
            traits.append("양자 직감: 확률적 사건 예측에 +15% 정확도")
    else:  # dystopia
        traits = dystopia_traits
        if has_hero:
            traits.append("불굴의 영웅: 동료를 보호하는 행동에 +2 보너스")
        if has_scholarly:
            traits.append("생존 지식: 자원 활용 효율 +20%")
        if has_tragic:
            traits.append("상실의 분노: 개인적 원한에 관련된 행동에 +2 보너스")
        if has_criminal:
            traits.append("암시장 연결망: 희귀 물품 거래 시 15% 할인")
        if has_mysterious:
            traits.append("통제 면역: 정신 조작 시도에 대한 저항 +25%")
    
    # 무작위 특성 선택
    return random.choice(traits)