Pandemic_Pong/game_objects/pong_viewer.py

248 lines
13 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
pong_viewer.py
Pandemic Pong view client only
"""
import sys, time
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QPainter, QBrush, QPen, QFont, QFontMetrics
from playsound import playsound
import game_objects.pong_constants as pgc # Pong global constants
import game_objects.pong_globalvars as pgv # Pong global variables
from game_objects.pong_player import PongPlayer
from game_objects.pong_object import PongObject
from game_objects.pong_game import PongGame
import game_objects.pong_viewserver as vs # Pong TCP view server (threads)
def draw_buttonstate(x, y, player, painter, scale_x, scale_y):
"""View function: Indicate last button pressed (not strictly necessary ...)"""
if (player.state & pgc.BTN_TRIGGER) == pgc.BTN_TRIGGER:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
painter.drawEllipse(int(x * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y)) # Blue button
if (player.state & pgc.BTN_TOP) == pgc.BTN_TOP:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Green
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern))
painter.drawEllipse(int((x + 30) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB) == pgc.BTN_THUMB:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Red
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
painter.drawEllipse(int((x + 60) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB2) == pgc.BTN_THUMB2:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Yellow
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern))
painter.drawEllipse(int((x + 90) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
class pongWindow(QMainWindow):
"""Qt5 base class"""
def __init__(self, debug_x, debug_y, viewserver, game, ball, player1, player2):
super().__init__()
self.title = "Pandemic Pong"
if pgv.bIsLocal:
self.title = "Pandemic Pong (Local)"
if pgv.bIsViewer:
self.title = "Pandemic Pong (Viewer)"
self.top = 0
self.left = 0
self.win_width = 1920
self.win_height = 960
self.viewserver = viewserver
self.game = game
self.ball = ball
self.player1 = player1
self.player2 = player2
self.InitWindow(debug_x, debug_y) # s.b. !
def InitWindow(self, debug_x, debug_y):
"""Prepare window for later drawing"""
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.win_width, self.win_height)
if pgv.bSizeable:
if pgc.DEVLOCAL == True: # Small windows for convenience ...
self.resize(int(self.win_width * 2 / 3 / 3), int(self.win_height * 2 / 2.8 / 3))
#self.move(int(self.win_width / 2.5), int(self.win_height / 16))
if pgc.DEVLOCAL == True:
self.move(debug_x, debug_y)
else:
self.resize(int(self.win_width * 2 / 3), int(self.win_height * 2 / 2.8))
self.move(int(self.win_width / 6), int(self.win_height / 8))
else:
self.showFullScreen() # Regular full screen play by default
self.pic = QPixmap("pictures/pong_background.png")
self.show()
# Don't annoy developers (except for local play - to test game mechanics !) ...
if (pgc.DEVLOCAL == False) and (pgv.bIsServer == False):
# Enforce caching ...
playsound(pgc.sounds[pgc.PLAYERCONTACTSOUND])
playsound(pgc.sounds[pgc.PLAYERMISSSOUND])
playsound(pgc.sounds[pgc.WALLCONTACTSOUND])
playsound(pgc.sounds[pgc.GAMEEXITSOUND])
time.sleep(0.5)
playsound(pgc.sounds[pgc.GAMESPLASHSOUND])
self.timer = QTimer() # Start processing 'loop'
self.timer.setInterval(25) # 25ms
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.on_timer)
self.timer.start()
def paintEvent(self, event):
"""Called whenever drawing is necessary ..."""
scale_x = self.width() / pgc.GAMEAREA_MAX_X
scale_y = self.height() / (pgc.GAMEAREA_MAX_Y + 20) # Compensate header
painter = QPainter(self) # Painter need's to be freshly picked ...
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
painter.drawPixmap(self.rect(), self.pic) # Game background
# Font metrics assume font is monospaced!
fntLarge = QFont("Monospace", int(120 * min(scale_x, scale_y)))
fntLarge.setKerning(False)
fntLarge.setFixedPitch(True)
fm = QFontMetrics(fntLarge)
fntMedium = QFont("Monospace", int(40 * min(scale_x, scale_y)))
fntMedium.setKerning(False)
fntMedium.setFixedPitch(True)
if self.game.state == pgc.STATE_WELCOME: # Welcome screen
painter.drawPixmap(self.rect(), QPixmap("pictures/pandemic_pong.png"))
painter.setFont(fntLarge)
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "PANDEMIC")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 300) * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "PONG")
elif self.game.state == pgc.STATE_START: # Wait for start button pressed
painter.setFont(fntMedium)
painter.drawText(int(150 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "Press [Start] ...")
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "Press [Select] to abort")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * scale_y), "Press [color] button")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 2 * scale_y), "to infect player,")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 3 * scale_y), "view will pickup virus!")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 4 * scale_y), "Match color to deflect!")
elif self.game.state == pgc.STATE_PLAY: # Actual play 'til score reaches 10 points
# Ball gives NO indication ...
painter.drawRect(int(self.ball.x * scale_x), int(self.ball.y * scale_y), int(self.ball.w * scale_x), int(self.ball.h * scale_y)) # Ball
if self.player1.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(self.player1.x * scale_x), int(self.player1.y * scale_y), int(self.player1.w * scale_x), int(self.player1.h * scale_y)) # Player left
if self.player2.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(self.player2.x * scale_x), int(self.player2.y * scale_y), int(self.player2.w * scale_x), int(self.player2.h * scale_y)) # Player right
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Reset standard colour
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
# Game scores
painter.setFont(fntLarge)
# Score player #1
msg = str(self.player1.score)
msg_width = fm.width(msg)
painter.drawText(int((800 - msg_width) * scale_x), int(154 * scale_y), "{}".format(msg))
# Score player #2
painter.drawText(int((pgc.GAMEAREA_MAX_X - 840) * scale_x), int(154 * scale_y), "{}".format(self.player2.score))
# Indicate Button states
draw_buttonstate(60, 50, self.player1, painter, scale_x, scale_y)
draw_buttonstate(pgc.GAMEAREA_MAX_X - 180, 50, self.player2, painter, scale_x, scale_y)
elif self.game.state == pgc.STATE_GAMERESULTS: # Display winner of this game
painter.setFont(fntMedium)
if self.player1.score > 9:
msg = "Game player #1 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
else:
msg = "Game player #2 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif self.game.state == pgc.STATE_FINALRESULTS: # Display set winner
painter.setFont(fntMedium)
if self.game.p1_game > 2:
msg = "Match player #1 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
else:
msg = "Match player #2 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif self.game.state == pgc.STATE_EXIT: # Indicate good bye ...
painter.setFont(fntMedium)
painter.drawText(int(50 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_x), "Bye!")
# Force font memory free (indication GC)
del fm
del fntMedium
del fntLarge
@pyqtSlot()
def on_timer(self):
"""Qt5 timer event runs the game ..."""
self.timer.stop() # Block overrun
if (pgv.bIsServer == False) and (pgv.bIsLocal == False): # Player 1 or 2?
rc = self.viewserver.receive_data() # Retrieve server stati
if rc < 0: # Server down, abort game
self.player2.exit()
self.player1.exit()
sys.exit(0)
#if rc > 0: # May be useful later ...
# bChanged = True
if self.game.pong_game(self.ball, self.player1, self.player2) == False:
# Quit gracefully
self.player2.exit()
self.player1.exit()
sys.exit(0)
self.update() # Enforce redraw (explicitely)
self.timer.start() # Re-enable timer event
def run_Qt5_viewer(debug_x, debug_y, viewserver, game, ball, player1, player2):
"""Qt5 viewer"""
app = QApplication(sys.argv) # Eval command line args
window = pongWindow(debug_x, debug_y, viewserver, game, ball, player1, player2) # Create Qt5 GUI
return app.exec_() # & run logic