Skip to content
This repository was archived by the owner on Feb 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# CHANGELOG of PBL-Game

## v0.43.0 (2026-02-10)
- クイズシステムの追加
- テキスト入力形式(今回は半角数字のみに限定)のクイズを追加
- `talk.py` のクイズ処理を三択問題とテキスト入力形式で分岐するように
- "quiz_type" を "text"(テキスト入力問題), "choice"(選択問題)で区別
- 入力は10文字まで、backspace で削除、z で決定
- `utils.py` に (0,1,2,3,4,5,6,7,8,9,backspace,period) を追加
- `dialogue.json` に "npc_text_quiz"(テキスト入力問題確認用のnpc)を追加

## v0.42.3 (2026-02-10)
- ゲームタイトルから「Demo ver」を消去し、タイトルを修正

Expand Down
32 changes: 32 additions & 0 deletions assets/dialogues/dialogues.json
Original file line number Diff line number Diff line change
Expand Up @@ -376,5 +376,37 @@
"speed": 0.0,
"max_offset": 0
}
},
"npc_text_quiz": {
"position": [
220,
105
],
"map_id": "world",
"image": "character/npc1.png",
"lines": [
"テキスト入力問題のテストだ。",
"簡単な計算問題を出すぞ。"
],
"movement_x": {
"enabled": false,
"speed": 0.0,
"max_offset": 0
},
"quiz": [
{
"type": "text",
"question": "1+1.1は?半角数字で入力してね。zキーで決定だ。",
"answer": "2.1"
},
{
"type": "text",
"question": "3×5は?半角数字で入力してね。zキーで決定だ。",
"answer": "15"
}
],
"reward": [
"test_item"
]
}
}
109 changes: 103 additions & 6 deletions src/core/talk.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(self, app):
self.current_quiz = None
self.quiz_choice = 0
self.quiz_index = 0 # 複数クイズのインデックス
self.quiz_text_input = "" # テキスト入力モード時の入力内容
self.wait_frames = 0

def is_active(self):
Expand Down Expand Up @@ -152,6 +153,15 @@ def _handle_quiz(self, keys):
if not self.current_quiz:
return

quiz_type = self.current_quiz.get("type", "choice")

if quiz_type == "text":
self._handle_text_quiz(keys)
else: # デフォルトは choice
self._handle_choice_quiz(keys)

def _handle_choice_quiz(self, keys):
"""3択問題の処理"""
choices = self.current_quiz.get("choices", [])

# 選択肢の移動
Expand All @@ -164,6 +174,26 @@ def _handle_quiz(self, keys):
elif keys.get("z"):
self._evaluate_quiz_answer()

def _handle_text_quiz(self, keys):
"""テキスト入力問題の処理"""
# 半角数字およびピリオドの入力
for ch in "0123456789.":
if keys.get(ch):
# ピリオドは1つまで
if ch == "." and "." in self.quiz_text_input:
continue
# 入力文字数は10文字までに制限
if len(self.quiz_text_input) < 10:
self.quiz_text_input += ch

# BackSpaceキーで入力削除
if keys.get("backspace"):
self.quiz_text_input = self.quiz_text_input[:-1]

# 決定(Zキー)
elif keys.get("z"):
self._evaluate_text_quiz_answer()

def _evaluate_quiz_answer(self):
"""クイズの正誤判定"""
q = self.current_quiz
Expand Down Expand Up @@ -214,6 +244,60 @@ def _evaluate_quiz_answer(self):
self.quiz_result_mode = True
self.wait_frames = 15

def _evaluate_text_quiz_answer(self):
"""テキスト入力クイズの正誤判定"""
q = self.current_quiz
npc_data = self.dialogues.get(self.active, {})
quiz = npc_data.get("quiz")

# ユーザーの入力値と正解を比較
user_answer = self.quiz_text_input.strip()
correct_answer = str(q.get("answer", ""))

result_lines = []
if user_answer == correct_answer:
if isinstance(quiz, list):
self.quiz_index += 1
if self.quiz_index >= len(quiz):
# すべて正解
result_lines.append("全問正解だ!素晴らしい。")
reward = npc_data.get("reward")
if reward:
for item_id in reward:
result_lines.append(f"【{item_id}】を手に入れた。")
self.app.items.extend(reward)
npc_data["quiz_done"] = True
else:
# 次のクイズへ
result_lines.append("正解!次の問題だ。")
self.current_quiz = quiz[self.quiz_index]
self.quiz_text_input = ""
self.window_lines = []
self.wait_frames = 15
return # 結果表示せずに次のクイズへ
else:
result_lines.append("正解だ!素晴らしい。")
reward = q.get("reward")
if reward:
for item_id in reward:
result_lines.append(f"【{item_id}】を手に入れた。")
self.app.items.extend(reward)
npc_data["quiz_done"] = True
else:
result_lines.append("残念、不正解だ。")
if isinstance(quiz, list):
result_lines.append("最初の問題からやり直しだ。")
self.quiz_index = 0
self.current_quiz = quiz[self.quiz_index]
self.quiz_text_input = ""
else:
result_lines.append("もう一度挑戦してくれたまえ。")

self.window_lines = result_lines
self.line_index = 0
self.quiz_result_mode = True
self.wait_frames = 15

def try_talk(self):
"""プレイヤーの周囲にNPCがいるか確認し、会話を開始する"""
if self.is_active() or self.wait_frames > 0:
Expand All @@ -236,6 +320,7 @@ def _open_dialog(self, data):
self.quiz_result_mode = False
self.current_quiz = None
self.quiz_index = 0
self.quiz_text_input = ""
self.wait_frames = 15

def _close_dialog(self):
Expand All @@ -246,6 +331,7 @@ def _close_dialog(self):
self.quiz_mode = False
self.quiz_result_mode = False
self.quiz_index = 0
self.quiz_text_input = ""
self.wait_frames = 20

def draw(self, screen, font):
Expand All @@ -259,14 +345,25 @@ def draw(self, screen, font):

if self.quiz_mode and not self.quiz_result_mode:
q = self.current_quiz
display_lines = [q.get("question", "")]
quiz_type = q.get("type", "choice")

if quiz_type == "text":
# テキスト入力クイズの描画
display_lines = [
q.get("question", ""),
f"入力: {self.quiz_text_input}_",
]
draw_window(screen, font, display_lines, rect)
else:
# 3択クイズの描画
display_lines = [q.get("question", "")]

# 選択肢の構築
for i, choice in enumerate(q.get("choices", [])):
cursor = ">" if i == self.quiz_choice else " "
display_lines.append(f"{cursor} {i + 1}. {choice}")
# 選択肢の構築
for i, choice in enumerate(q.get("choices", [])):
cursor = ">" if i == self.quiz_choice else " "
display_lines.append(f"{cursor} {i + 1}. {choice}")

draw_window(screen, font, display_lines, rect)
draw_window(screen, font, display_lines, rect)

elif self.window_lines:
# 1行ずつ表示するための修正
Expand Down
12 changes: 12 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ class KeyTracker:
"s": pygame.K_s,
"i": pygame.K_i,
"m": pygame.K_m,
"0": pygame.K_0,
"1": pygame.K_1,
"2": pygame.K_2,
"3": pygame.K_3,
"4": pygame.K_4,
"5": pygame.K_5,
"6": pygame.K_6,
"7": pygame.K_7,
"8": pygame.K_8,
"9": pygame.K_9,
"backspace": pygame.K_BACKSPACE,
".": pygame.K_PERIOD,
}

def __init__(self):
Expand Down