# 감정-행동 연동 매핑 및 행동 생성 # portfolio/npc_social_network/npc/npc_behavior.py from .emotion_config import EMOTION_CATEGORY_MAP class BehaviorManager: def __init__(self): self.mapping = { # Core (기본 감정) "joy": "jump", # 기쁨 : 점프 뛰다 "sadness": "sit_down", # 슬픔 : 주저 앉다 "anger": "shout", # 분노 : 소리치다 "fear": "run_away", # 공포 : 도망치다 "disgust": "avoid", # 혐오 : 피하다 "surprise": "gasp", # 놀람 : 감탄하며 숨을 들이쉼 "neutral": "idle", # 중립 : 아무것도 하지 않음 # Social (사회적 감정) "gratitude": "thank", # 감사 : 감사하다 "shame": "hide_face", # 수치심 : 얼굴을 감추다 "guilt": "apologize", # 죄책감 : 미안하다고 말함 "pride": "show_off", # 자부심 : 자랑하다 "jealousy": "glare", # 질투 : 노려보다 "compassion": "console", # 연민/동정 : 위로하다 "love": "embrace", # 사랑/애정 : 포옹하다 "admiration": "cheer", # 존경/감탄 : 환호하다 "empathy": "listen_carefully", # 공감 : 집중하다 "awe": "gasp", # 경외심 : 감탄하며 숨을 들이쉼 "attachment": "hold_hands", # 애착 : 소유하다 "comfort": "pat", # 위로 추구 : 토닥거리다 # Cognitive (인지적 상태) "anticipation": "prepare", # 기대 : 준비하다 "curiosity": "inspect", # 호기심 : 살펴보다 "confusion": "scratch_head", # 혼란 : 머리를 긁다 "interest": "lean_forward", # 흥미 : 집중하다 "engagement": "focus", # 몰입 : 집중하다 "boredom": "idle", # 지루함 : 아무것도 하지 않음 "relief": "exhale", # 안도 : 숨을 내쉬다 "anxiety": "fidget", # 불안/긴장 : 안절부절 못하다 "calm": "relax", # 평온 : 진정하다 "skepticism": "raise_eyebrow", # 회의 : 눈썹을 올리다 # Complex (복합 감정) "nostalgia": "stare_into_distance", # 향수 "bittersweet": "smile_then_look_down", # 달콤쌉싸름함 "schadenfreude": "smirk", # 남의 불행에서 느끼는 기쁨 "hope": "brighten_up", # 희망 "resentment": "scowl", # 분개 "anticipatory_joy": "rub_hands", # 기대 속의 기쁨 "regret": "look_down", # 후회 "rumination": "mutter_to_self", # 반추 "groundedness": "steady_posture", # 안정감 } # 행동에 따른 감정 (역매핑) self.action_to_emotion = {v: k for k, v in self.mapping.items()} # Layer 우선 순위 / weight 설정 (튜닝 가능) self.layer_priority = ["core", "social", "cognitive", "complex"] self.layer_weights = { "core" : 1.0, "social" : 0.8, "cognitive" : 0.6, "complex" : 0.4, } def get_layer_dominant_emotions(self, buffer): """ layer별 dominant emotion 계산 """ layer_emotions = {layer: [] for layer in self.layer_priority} for emo, val in buffer.items(): if val > 0: layer = EMOTION_CATEGORY_MAP.get(emo) if layer: layer_emotions[layer].append((emo, val)) # 각 layer에서 가장 강한 감정 추출 layer_dominant = {} for layer, emos in layer_emotions.items(): if emos: dominant_emo = max(emos, key=lambda x: x[1]) layer_dominant[layer] = dominant_emo return layer_dominant def decide_layered_sequence(self, layer_dominant_emotions): """ layer priority + weight 기반 시퀀스 생성 """ sequence = [] for layer in self.layer_priority: if layer in layer_dominant_emotions: emo, score = layer_dominant_emotions[layer] weighted_score = round(score * self.layer_weights[layer], 2) action = self.mapping.get(emo) if action: sequence.append((action, weighted_score)) return sequence def perform_sequence(self, name, job, emotion_buffer, return_trace=False): """ Layer-aware Behavior 시퀀스 구성 + Behavior Trace 반환 """ layer_dominant_emotions = self.get_layer_dominant_emotions(emotion_buffer) sequence = self.decide_layered_sequence(layer_dominant_emotions) if not sequence: behavior_output = f"{name}은(는) 특별한 행동을 하지 않습니다." if return_trace: return behavior_output, [] else: return behavior_output # 행동 텍스트 템플릿 behavior_lines = { # Core "jump": "기뻐서 깡충 뜁니다.", "sit_down": "슬퍼서 주저앉습니다....", "shout": "화가 나서 소리를 지릅니다!", "run_away": "무서워 도망갑니다!", "avoid": "뒤로 물러서 피하려 합니다.", "gasp": "깜짝 놀랍니다.", "idle": "가만히 있습니다.", # Social "thank": "감사한 마음을 표현합니다.", "hide_face": "수치심에 얼굴을 가립니다.", "apologize": "미안하다고 말합니다.", "show_off": "자랑스러운 모습을 보여줍니다.", "glare": "상대를 노려봅니다.", "console": "상대방을 따뜻하게 위로합니다.", "embrace": "상대를 포옹합니다.", "cheer": "감탄하며 응원합니다.", "listen_carefully": "상대의 말을 집중해서 듣습니다.", "hold_hands": "손을 잡습니다.", "pat": "가볍게 등을 두드려 위로합니다.", # Cognitive "prepare": "무언가를 준비합니다.", "inspect": "유심히 관찰합니다.", "scratch_head": "머리를 긁적입니다.", "lean_forward": "앞으로 숙이며 집중합니다.", "focus": "몰입하여 집중합니다.", "exhale": "안도하며 숨을 쉽니다.", "fidget": "불안하게 몸을 움직입니다.", "relax": "평온하게 휴식합니다.", "raise_eyebrow": "의심스럽게 눈썹을 치켜뜹니다.", # Complex "stare_into_distance": "먼 곳을 바라보며 회상에 잠깁니다.", "smile_then_look_down": "미소를 짓다가 고개를 숙입니다.", "smirk": "비웃는 표정을 짓습니다.", "brighten_up": "기대감에 눈빛이 빛납니다.", "scowl": "인상을 찌푸립니다.", "rub_hands": "들뜬 마음에 손을 비빕니다.", "look_down": "고개를 숙이며 후회합니다.", "mutter_to_self": "혼잣말을 중얼거립니다.", "steady_posture": "안정된 자세를 유지합니다.", } # 행동 문장 생성 lines = [] trace_pairs = [] for action, score in sequence: msg = behavior_lines.get(action, f"{action} 행동을 합니다.") lines.append(f"({round(score,1)}) {msg}") trace_pairs.append((action, round(score, 1))) # 직업 라인 추가 job_line = { "farmer": "밭일도 해야 하겠군요.", "blacksmith": "대장간 불도 지펴야죠.", }.get(job, "오늘도 바쁜 하루네요.") behavior_output = f"{name} 행동 시퀀스: \n" + "\n".join(lines) + f"\n{job_line}" # 최종 출력 문장 if return_trace: return behavior_output, trace_pairs else: return behavior_output