No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

game.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. # Licensed under GPLv3
  2. # Written by Sebastian Lohff (seba@someserver.de)
  3. # http://seba-geek.de/projects/seopardy/
  4. from __future__ import print_function
  5. from PySide import QtCore, QtGui
  6. from player import Player, nobodyColor
  7. from windows import QuestionWindow, EditAnswersWindow, PlayerStartWindow, VictoryWindow, DoubleJeopardyWindow
  8. from gamestate import QuestionAnswers
  9. class SeopardyGame(QtGui.QWidget):
  10. def __init__(self, questions, gamestate, inputs, parent=None):
  11. super(SeopardyGame, self).__init__(parent)
  12. self.questions = questions
  13. self.gamestate = gamestate
  14. self.inputs = inputs
  15. #self.players = [Player.gen_player(i, parent=self) for i in range(1, 4)]
  16. self.players = self.gamestate.get_players()
  17. if len(self.players) == 0:
  18. for i in range(3):
  19. self.add_player()
  20. self.currentPlayer = None
  21. self._inOtherWindow = False
  22. self._createGui()
  23. self.setWindowTitle("Seopardy - Board")
  24. self.showFullScreen()
  25. # initiate starting the game!
  26. if self.gamestate.get_state() == "start":
  27. QtCore.QCoreApplication.postEvent(self, GameStartEvent())
  28. def add_player(self, addToGui=False):
  29. self.players.append(Player.gen_player(len(self.players)+1, self))
  30. if addToGui:
  31. if len(self.players) > 1:
  32. self.playerBar.addStretch()
  33. self.playerBar.addWidget(self.players[-1])
  34. def _createGui(self):
  35. """ Create the board from questions. """
  36. layout = QtGui.QVBoxLayout()
  37. headerLayout = QtGui.QHBoxLayout()
  38. self.header = QtGui.QLabel(self.questions.get_title())
  39. self.header.setStyleSheet("QLabel { font-size: 20px; }")
  40. headerLayout.addWidget(self.header)
  41. headerLayout.setSizeConstraint(QtGui.QLayout.SetMinimumSize)
  42. layout.addWidget(self.header, alignment=QtCore.Qt.AlignCenter)
  43. # create board
  44. #board = QtGui.QGridLayout(6, len(self.questions.get_sections()))
  45. self.board = QtGui.QGridLayout()
  46. self.board.setSizeConstraint(QtGui.QLayout.SetMaximumSize)
  47. for i, sec in enumerate(self.questions.get_sections()):
  48. seclabel = QtGui.QLabel(sec, alignment=QtCore.Qt.AlignCenter)
  49. seclabel.setStyleSheet("QLabel { font-size: 30px; }")
  50. self.board.addWidget(seclabel, 0, i)
  51. for j in range(5):
  52. b = QtGui.QPushButton(str((j+1)*100))
  53. b.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
  54. b.setAutoDefault(False)
  55. #b.setStyleSheet("QPushButton { font-size: 60px; }")
  56. b.installEventFilter(FocusKeyGrabber(i+1, j+1, self))
  57. b.clicked.connect(lambda sec=sec, j=j: self.go_to_question(sec, j+1))
  58. self.board.addWidget(b, j+1, i)
  59. self._restyle_button(sec, j+1, self.gamestate.get_answers(sec, j+1))
  60. layout.addLayout(self.board)
  61. # create player bar
  62. self.playerBar = QtGui.QHBoxLayout()
  63. for i, player in enumerate(self.players):
  64. self.playerBar.addWidget(player)
  65. if i != len(self.players)-1:
  66. self.playerBar.addStretch()
  67. layout.addLayout(self.playerBar)
  68. self.setLayout(layout)
  69. def setFocusToQuestion(self, secno, number):
  70. if secno < 1 or secno > len(self.questions.get_sections()) or number > 5 or number < 1:
  71. return
  72. self.board.itemAtPosition(number, secno-1).widget().setFocus()
  73. def event(self, e):
  74. if e.type() == GameStartEvent.eventType:
  75. # start window
  76. if not self._inOtherWindow:
  77. self._inOtherWindow = True
  78. x = PlayerStartWindow(self.players, self)
  79. for inp in self.inputs:
  80. inp.buttonEvent.connect(x.playerButtonPress)
  81. x.buzzersOpen.connect(inp.buzzersOpen)
  82. x.playerGotQuestion.connect(inp.playerGotQuestion)
  83. x.exec_()
  84. for inp in self.inputs:
  85. inp.buttonEvent.disconnect(x.playerButtonPress)
  86. x.buzzersOpen.disconnect(inp.buzzersOpen)
  87. x.playerGotQuestion.disconnect(inp.playerGotQuestion)
  88. self._inOtherWindow = False
  89. self.gamestate.set_state("playing")
  90. self.gamestate.save()
  91. return True
  92. else:
  93. return super(SeopardyGame, self).event(e)
  94. def closeEvent(self, event):
  95. if not self._inOtherWindow:
  96. event.accept()
  97. else:
  98. event.ignore()
  99. def keyPressEvent(self, e):
  100. if e.key() == QtCore.Qt.Key_Escape:
  101. self.close()
  102. elif e.key() == QtCore.Qt.Key_R:
  103. x = PlayerStartWindow(self.players, self)
  104. self._inOtherWindow = True
  105. x.exec_()
  106. self._inOtherWindow = False
  107. elif e.key() == QtCore.Qt.Key_V:
  108. self.show_victory_window()
  109. elif e.key() == QtCore.Qt.Key_E:
  110. if self._inOtherWindow:
  111. return
  112. # find current question
  113. section = None
  114. qno = None
  115. found = False
  116. for secno, section in enumerate(self.questions.get_sections(), 0):
  117. for qno in range(1, 6):
  118. btn = self.board.itemAtPosition(qno, secno).widget()
  119. if btn == self.focusWidget():
  120. found = True
  121. break
  122. if found:
  123. break
  124. if not found:
  125. return
  126. self.edit_question_answers(section, qno)
  127. def edit_question_answers(self, section, number):
  128. self._inOtherWindow = True
  129. answers = self.gamestate.get_answers(section, number)
  130. if answers is None:
  131. answers = QuestionAnswers(section, number)
  132. editAnswersWin = EditAnswersWindow(self.players, answers, self)
  133. editAnswersWin.exec_()
  134. self._inOtherWindow = False
  135. oldAnswers = self.gamestate.get_answers(section, number)
  136. newAnswers = editAnswersWin.get_new_answers()
  137. if oldAnswers:
  138. self._set_player_points(oldAnswers, rollback=True)
  139. self._set_player_points(newAnswers)
  140. self.gamestate.set_answers(section, number, newAnswers)
  141. self._restyle_button(section, number, newAnswers)
  142. self.gamestate.save()
  143. def show_victory_window(self):
  144. self._inOtherWindow = True
  145. victoryWindow = VictoryWindow(self.players, self)
  146. victoryWindow.exec_()
  147. self._inOtherWindow = False
  148. def go_to_question(self, section, number):
  149. if self._inOtherWindow:
  150. return
  151. self._inOtherWindow = True
  152. answers = self.gamestate.get_answers(section, number)
  153. wasAnswered = (answers is not None and answers.is_answered())
  154. question = self.questions.get_question(section, number)
  155. dj = None
  156. if not wasAnswered and question["Double-Jeopardy"]:
  157. dwin = DoubleJeopardyWindow(self.players, number*100, self.currentPlayer, parent=self)
  158. dwin.exec_()
  159. dj = (dwin.get_player(), dwin.get_chosen_points())
  160. qwin = QuestionWindow(self.players, section, number, question, answers, dj, self)
  161. for inp in self.inputs:
  162. inp.buttonEvent.connect(qwin.playerButtonPress)
  163. qwin.buzzersOpen.connect(inp.buzzersOpen)
  164. qwin.playerGotQuestion.connect(inp.playerGotQuestion)
  165. qwin.showFullScreen()
  166. qwin.exec_()
  167. for inp in self.inputs:
  168. inp.buttonEvent.disconnect(qwin.playerButtonPress)
  169. qwin.buzzersOpen.disconnect(inp.buzzersOpen)
  170. qwin.playerGotQuestion.disconnect(inp.playerGotQuestion)
  171. self._inOtherWindow = False
  172. # add points to players
  173. if not wasAnswered:
  174. answers = qwin.get_answers()
  175. self.gamestate.set_answers(section, number, answers)
  176. self._set_player_points(answers)
  177. if answers.get_winner():
  178. self.currentPlayer = answers.get_winner()
  179. # restyle the button
  180. self._restyle_button(section, number, answers)
  181. # safe current gamestate
  182. self.gamestate.save()
  183. # check if we are done
  184. foundUnanswered = False
  185. for sec in self.questions.get_sections():
  186. for j in range(5):
  187. boardAnswers = self.gamestate.get_answers(sec, j+1)
  188. if boardAnswers is None or not answers.is_answered():
  189. foundUnanswered = True
  190. if foundUnanswered:
  191. break
  192. if not foundUnanswered:
  193. self.show_victory_window()
  194. def _set_player_points(self, answers, rollback=False):
  195. for i, (player, correct) in enumerate(answers.get_tries()):
  196. prefix = 1
  197. if rollback:
  198. prefix *= -1
  199. if not correct:
  200. prefix *= -1
  201. points = 0
  202. if i == 0 and answers.get_dj_points() is not None:
  203. points = answers.get_dj_points()
  204. else:
  205. points = answers.points()
  206. player.add_points(prefix*points)
  207. def _restyle_button(self, sec, qno, answers):
  208. secno = self.questions.get_number_from_section(sec)
  209. btn = self.board.itemAtPosition(qno, secno-1).widget()
  210. btnstr = ""
  211. btncolor = None
  212. if answers is None or not answers.is_answered():
  213. btn.setText(str(qno*100))
  214. btn.setStyleSheet("QPushButton { font-size: 60px; }")
  215. return
  216. # restrict number of tries shown
  217. numTries = 4 if answers.got_answered() else 3
  218. tries = answers.get_tries()
  219. if len(tries) > numTries:
  220. btnstr = "...\n"
  221. first = True
  222. for player, correct in answers.get_tries()[-numTries:]:
  223. if correct:
  224. btncolor = player.color
  225. prefix = "+"
  226. else:
  227. prefix = "-"
  228. dj = " (D)" if first and answers.get_dj_points() is not None else ""
  229. btnstr += "%s%s%s\n" % (prefix, player.name, dj)
  230. first = False
  231. if not answers.got_answered():
  232. btnstr += "+nobody"
  233. btncolor = nobodyColor
  234. btn.setStyleSheet("QPushButton { background-color: %s; color: white; font-size: 20px; border: none; }" % (btncolor.name(),))
  235. btn.setText(btnstr.strip())
  236. class FocusKeyGrabber(QtCore.QObject):
  237. def __init__(self, secno, number, parent, *args, **kwargs):
  238. super(FocusKeyGrabber, self).__init__(parent, *args, **kwargs)
  239. self._secno = secno
  240. self._number = number
  241. self._parent = parent
  242. def eventFilter(self, obj, e):
  243. if e.type() == QtCore.QEvent.KeyPress:
  244. if e.key() == QtCore.Qt.Key.Key_Left:
  245. self._parent.setFocusToQuestion(self._secno-1, self._number)
  246. return True
  247. elif e.key() == QtCore.Qt.Key.Key_Right:
  248. self._parent.setFocusToQuestion(self._secno+1, self._number)
  249. return True
  250. elif e.key() == QtCore.Qt.Key.Key_Up:
  251. self._parent.setFocusToQuestion(self._secno, self._number-1)
  252. return True
  253. elif e.key() == QtCore.Qt.Key.Key_Down:
  254. self._parent.setFocusToQuestion(self._secno, self._number+1)
  255. return True
  256. return QtCore.QObject.eventFilter(self, obj, e)
  257. class GameStartEvent(QtCore.QEvent):
  258. eventType = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
  259. def __init__(self):
  260. super(GameStartEvent, self).__init__(self.eventType)