"""
ステータス表示コンポーネント
好感度ゲージと関係ステージの表示を担当する
"""
import streamlit as st
import logging
from typing import Dict, Tuple
logger = logging.getLogger(__name__)
class StatusDisplay:
"""ステータス表示を管理するクラス"""
def __init__(self):
"""ステータス表示の初期化"""
self.stage_colors = {
"敵対": {"color": "#ff4757", "emoji": "🔴", "bg_color": "rgba(255, 71, 87, 0.1)"},
"警戒": {"color": "#ff6348", "emoji": "🟠", "bg_color": "rgba(255, 99, 72, 0.1)"},
"中立": {"color": "#ffa502", "emoji": "🟡", "bg_color": "rgba(255, 165, 2, 0.1)"},
"好意": {"color": "#2ed573", "emoji": "🟢", "bg_color": "rgba(46, 213, 115, 0.1)"},
"親密": {"color": "#a55eea", "emoji": "💜", "bg_color": "rgba(165, 94, 234, 0.1)"}
}
def get_affection_color(self, affection: int) -> str:
"""
好感度に基づいて色を取得する
Args:
affection: 好感度値 (0-100)
Returns:
色のHEXコード
"""
if affection < 20:
return "#ff4757" # 赤
elif affection < 40:
return "#ff6348" # オレンジ
elif affection < 60:
return "#ffa502" # 黄色
elif affection < 80:
return "#2ed573" # 緑
else:
return "#a55eea" # 紫
def get_relationship_stage_info(self, affection: int) -> Dict[str, str]:
"""
好感度から関係性ステージの情報を取得する
Args:
affection: 好感度値 (0-100)
Returns:
ステージ情報の辞書
"""
if affection < 20:
stage = "敵対"
elif affection < 40:
stage = "警戒"
elif affection < 60:
stage = "中立"
elif affection < 80:
stage = "好意"
else:
stage = "親密"
# 古いキー形式との互換性を保つため、新しいキーで検索し、見つからない場合は中立を返す
stage_info = None
for key, value in self.stage_colors.items():
if stage in key:
stage_info = value
break
return stage_info or self.stage_colors.get("中立", {"color": "#ffa502", "emoji": "🟡", "bg_color": "rgba(255, 165, 2, 0.1)"})
def render_affection_gauge(self, affection: int) -> None:
"""
好感度ゲージを表示する
Args:
affection: 好感度値 (0-100)
"""
try:
# 好感度の値を0-100の範囲に制限
affection = max(0, min(100, affection))
# 好感度メトリック表示
col1, col2 = st.columns([2, 1])
with col1:
st.metric("好感度", f"{affection}/100")
with col2:
# 好感度の変化を表示(前回の値と比較)
prev_affection = st.session_state.get('prev_affection', affection)
delta = affection - prev_affection
if delta != 0:
st.metric("変化", f"{delta:+d}")
st.session_state.prev_affection = affection
# プログレスバー
progress_value = affection / 100.0
affection_color = self.get_affection_color(affection)
# カスタムプログレスバーのCSS
progress_css = f"""
"""
st.markdown(progress_css, unsafe_allow_html=True)
# プログレスバーのHTML
progress_html = f"""
"""
st.markdown(progress_html, unsafe_allow_html=True)
# Streamlitの標準プログレスバーも表示(フォールバック)
st.progress(progress_value)
except Exception as e:
logger.error(f"好感度ゲージ表示エラー: {e}")
# フォールバック表示
st.metric("好感度", f"{affection}/100")
st.progress(affection / 100.0)
def render_relationship_stage(self, affection: int) -> None:
"""
関係性ステージを表示する
Args:
affection: 好感度値 (0-100)
"""
try:
stage_info = self.get_relationship_stage_info(affection)
# ステージ名を取得
if affection < 20:
stage_name = "ステージ1:敵対"
stage_description = "麻理はあなたを敵視している"
elif affection < 40:
stage_name = "ステージ2:警戒"
stage_description = "麻理はあなたを警戒している"
elif affection < 60:
stage_name = "ステージ3:中立"
stage_description = "麻理はあなたに対して中立的"
elif affection < 80:
stage_name = "ステージ4:好意"
stage_description = "麻理はあなたに好意を持っている"
else:
stage_name = "ステージ5:親密"
stage_description = "麻理はあなたと親密な関係"
# ステージ表示のCSS
stage_css = f"""
"""
st.markdown(stage_css, unsafe_allow_html=True)
# ステージ表示のHTML
stage_html = f"""
{stage_info['emoji']}
{stage_name}
{stage_description}
"""
st.markdown(stage_html, unsafe_allow_html=True)
# フォールバック表示
st.write(f"{stage_info['emoji']} **関係性**: {stage_name}")
except Exception as e:
logger.error(f"関係性ステージ表示エラー: {e}")
# フォールバック表示
if affection < 20:
st.write("🔴 **関係性**: ステージ1:敵対")
elif affection < 40:
st.write("🟠 **関係性**: ステージ2:中立")
elif affection < 60:
st.write("🟡 **関係性**: ステージ3:好意")
elif affection < 80:
st.write("🟢 **関係性**: ステージ4:親密")
else:
st.write("💜 **関係性**: ステージ5:最接近")
def render_affection_history(self, max_history: int = 10) -> None:
"""
好感度の履歴を表示する(デバッグモード用)
Args:
max_history: 表示する履歴の最大数
"""
try:
if not st.session_state.get('debug_mode', False):
return
# 好感度履歴を取得
affection_history = st.session_state.get('affection_history', [])
if not affection_history:
st.write("好感度の履歴がありません")
return
# 最新の履歴を表示
recent_history = affection_history[-max_history:]
st.subheader("📈 好感度履歴")
for i, entry in enumerate(reversed(recent_history)):
timestamp = entry.get('timestamp', 'Unknown')
affection = entry.get('affection', 0)
change = entry.get('change', 0)
message = entry.get('message', '')
change_str = f"({change:+d})" if change != 0 else ""
st.write(f"{i+1}. {affection}/100 {change_str} - {timestamp[:19]}")
if message:
st.caption(f"メッセージ: {message[:50]}...")
except Exception as e:
logger.error(f"好感度履歴表示エラー: {e}")
def update_affection_history(self, old_affection: int, new_affection: int,
message: str = "") -> None:
"""
好感度履歴を更新する
Args:
old_affection: 変更前の好感度
new_affection: 変更後の好感度
message: 関連するメッセージ
"""
try:
if 'affection_history' not in st.session_state:
st.session_state.affection_history = []
# 履歴エントリを作成
history_entry = {
'timestamp': st.session_state.get('current_timestamp', ''),
'affection': new_affection,
'change': new_affection - old_affection,
'message': message[:100] if message else '' # メッセージを100文字に制限
}
st.session_state.affection_history.append(history_entry)
# 履歴の長さを制限(最大50エントリ)
if len(st.session_state.affection_history) > 50:
st.session_state.affection_history = st.session_state.affection_history[-50:]
except Exception as e:
logger.error(f"好感度履歴更新エラー: {e}")
def get_affection_statistics(self) -> Dict[str, float]:
"""
好感度の統計情報を取得する
Returns:
統計情報の辞書
"""
try:
affection_history = st.session_state.get('affection_history', [])
if not affection_history:
return {
'current': st.session_state.get('affection', 30),
'average': 30.0,
'max': 30,
'min': 30,
'total_changes': 0
}
affections = [entry['affection'] for entry in affection_history]
changes = [entry['change'] for entry in affection_history if entry['change'] != 0]
return {
'current': st.session_state.get('affection', 30),
'average': sum(affections) / len(affections),
'max': max(affections),
'min': min(affections),
'total_changes': len(changes),
'positive_changes': len([c for c in changes if c > 0]),
'negative_changes': len([c for c in changes if c < 0])
}
except Exception as e:
logger.error(f"好感度統計取得エラー: {e}")
return {
'current': st.session_state.get('affection', 30),
'average': 30.0,
'max': 30,
'min': 30,
'total_changes': 0
}
def apply_status_styles(self) -> None:
"""
ステータス表示用のカスタムスタイルを適用する
"""
try:
status_css = """
"""
st.markdown(status_css, unsafe_allow_html=True)
logger.debug("ステータス表示用スタイルを適用しました")
except Exception as e:
logger.error(f"ステータススタイル適用エラー: {e}")
def render_enhanced_status_display(self, affection: int) -> None:
"""
拡張されたステータス表示を描画する
Args:
affection: 現在の好感度
"""
try:
# カスタムスタイルを適用
self.apply_status_styles()
# ステータスコンテナの開始
st.markdown('', unsafe_allow_html=True)
# 好感度ゲージ
self.render_affection_gauge(affection)
# 関係性ステージ
self.render_relationship_stage(affection)
# ステータスコンテナの終了
st.markdown('
', unsafe_allow_html=True)
except Exception as e:
logger.error(f"拡張ステータス表示エラー: {e}")
# フォールバック:通常の表示
self.render_affection_gauge(affection)
self.render_relationship_stage(affection)
def show_affection_change_notification(self, old_affection: int,
new_affection: int, reason: str = "") -> None:
"""
好感度変化の通知を表示する
Args:
old_affection: 変更前の好感度
new_affection: 変更後の好感度
reason: 変化の理由
"""
try:
change = new_affection - old_affection
if change == 0:
return
# 変化の方向に応じてスタイルを決定
if change > 0:
icon = "📈"
color = "#2ed573"
change_text = f"+{change}"
css_class = "affection-change-positive"
else:
icon = "📉"
color = "#ff4757"
change_text = str(change)
css_class = "affection-change-negative"
# 通知メッセージを作成
notification_html = f"""
{icon} 好感度が変化しました: {change_text}
{f'
{reason}' if reason else ''}
"""
st.markdown(notification_html, unsafe_allow_html=True)
# 自動で消える通知(JavaScript)
auto_hide_js = """
"""
st.markdown(auto_hide_js, unsafe_allow_html=True)
except Exception as e:
logger.error(f"好感度変化通知エラー: {e}")
def get_status_display_config(self) -> Dict[str, any]:
"""
ステータス表示の設定情報を取得する
Returns:
設定情報の辞書
"""
try:
current_affection = st.session_state.get('affection', 30)
stage_info = self.get_relationship_stage_info(current_affection)
return {
"current_affection": current_affection,
"affection_color": self.get_affection_color(current_affection),
"stage_info": stage_info,
"history_count": len(st.session_state.get('affection_history', [])),
"statistics": self.get_affection_statistics(),
"styles_applied": True
}
except Exception as e:
logger.error(f"ステータス表示設定取得エラー: {e}")
return {
"current_affection": 30,
"affection_color": "#ffa502",
"stage_info": self.stage_colors["中立"],
"history_count": 0,
"statistics": {},
"styles_applied": False
}