2019-09-13 08:18:54 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#coding=utf-8
|
|
|
|
|
|
|
|
############################################
|
|
|
|
########### GENERAL INFORMATION ############
|
|
|
|
############################################
|
|
|
|
|
|
|
|
"""
|
|
|
|
This is the software for the project heisser_draht from the bfi itlabs st.stefan
|
|
|
|
|
|
|
|
The idea of the game is to follow a wire with a ring, without touching it.
|
|
|
|
The ones with the fastest times to reach the end (and with the least errors)
|
|
|
|
are written onto the highscore table. Said table is temporary, and not saved on exit!
|
|
|
|
|
2019-09-17 08:19:24 +02:00
|
|
|
The main game loop is running in 4 states which are changed depending on what inputs the raspberry receives.
|
|
|
|
In each game state, a different part of the main game loop is run repeatedly, checking for signals and taking
|
|
|
|
corresponding actions.
|
|
|
|
If the right signals are detected, the state will change accordingly.
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
* The game is not running and not ending (State 0)
|
|
|
|
This state is the default "starting" page of the game.
|
|
|
|
The screen shows the highscore table, and the game waits for a start signal.
|
2019-09-17 08:19:24 +02:00
|
|
|
If the raspberry detects a signal on the start pin, the game state changes to State 1
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
* The game is running and not ending (State 1)
|
|
|
|
This is the game running. A timer is displayed on screen.
|
|
|
|
Errors are shown on screen aswell.
|
2019-09-17 08:19:24 +02:00
|
|
|
The game is waiting for error signals and stop signals.
|
|
|
|
If the raspberry detects a signal on the error pin, errors are added to the time.
|
|
|
|
If the raspberry detects a signal on the stop pin, the game state changes to State 2
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
* The game is not running and ending (State 2)
|
|
|
|
Once the stop signal has been reached in the running game, this state is reached.
|
|
|
|
The game checks for highscores and asks for a name, should a highscore have been made.
|
|
|
|
This state automatically ends after a few seconds and returns the game to State 0.
|
|
|
|
|
|
|
|
* The game is neither running nor ending (State 3)
|
2019-09-17 08:19:24 +02:00
|
|
|
This state is currently invalid and does nothing. It should never be reached.
|
|
|
|
There's no functionality assigned to this, and this state is never run, but included
|
2019-09-13 08:18:54 +02:00
|
|
|
for completeness sake.
|
|
|
|
|
2019-09-17 08:19:24 +02:00
|
|
|
If during any of these states the shutdown pin is detected, the raspberry will shutdown entirely.
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
The game also features a (not entirely interactive) LED lighting control. The LEDs are supposed
|
|
|
|
to be driven by a small MOSFET driver circuit. DO NOT ATTACH LEDS DIRECTLY!
|
|
|
|
The correct type of LEDs are the ones that have a common 12V rail per segment, and are switched
|
|
|
|
by clamping the ground. Those ground clamps are driven by the Raspberry via a PWM signal.
|
|
|
|
|
|
|
|
Pin explanation:
|
|
|
|
|
|
|
|
pin_blue, pin_green, pin_red
|
|
|
|
|
|
|
|
Type: PWM Output
|
2019-09-17 08:19:24 +02:00
|
|
|
Use: These pins are for controlling the lighting. They are to be connected to a MOSFET driver stage
|
2019-09-13 08:18:54 +02:00
|
|
|
that clamps the ground.
|
|
|
|
|
|
|
|
pin_start
|
|
|
|
|
2019-09-19 08:58:21 +02:00
|
|
|
Type: Input, Pull Down
|
2019-09-13 08:18:54 +02:00
|
|
|
Use: Start pin. If this is active, the game starts.
|
|
|
|
|
|
|
|
pin_stop
|
|
|
|
|
2019-09-19 08:58:21 +02:00
|
|
|
Type: Input, Pull Down
|
2019-09-13 08:18:54 +02:00
|
|
|
Use: Stop pin. If this is active, the game ends
|
|
|
|
|
|
|
|
pin_error
|
|
|
|
|
2019-09-19 08:58:21 +02:00
|
|
|
Type: Input, Pull Down
|
2019-09-13 08:18:54 +02:00
|
|
|
Use: This is to be attached to the game wire. If the wire is touched,
|
|
|
|
the pin is pulled up and an error is registered.
|
|
|
|
|
|
|
|
pin_shutdown
|
|
|
|
|
2019-09-19 08:58:21 +02:00
|
|
|
Type: Input, Pull Down
|
2019-09-13 08:18:54 +02:00
|
|
|
Use: Triggering this input causes a system shutdown.
|
|
|
|
|
|
|
|
GPIO Pinout for assembly:
|
|
|
|
|
|
|
|
3V3 (1) (2) 5V
|
|
|
|
- (3) (4) 5V
|
|
|
|
- (5) (6) GND
|
|
|
|
- (7) (8) -
|
|
|
|
GND (9) (10) -
|
|
|
|
- (11) (12) -
|
|
|
|
- (13) (14) GND
|
|
|
|
- (15) (16) pin_blue
|
|
|
|
3V3 (17) (18) pin_green
|
|
|
|
- (19) (20) GND
|
|
|
|
- (21) (22) pin_red
|
|
|
|
- (23) (24) -
|
|
|
|
GND (25) (26) -
|
|
|
|
- (27) (28) -
|
|
|
|
- (29) (30) GND
|
|
|
|
- (31) (32) pin_start
|
|
|
|
pin_error (33) (34) GND
|
|
|
|
- (35) (36) pin_shutdown
|
|
|
|
pin_stop (37) (38) -
|
|
|
|
GND (39) (40) -
|
|
|
|
|
|
|
|
Default state of pins:
|
|
|
|
|
2019-09-17 08:19:24 +02:00
|
|
|
P_32: pull up
|
|
|
|
P_33: pull up
|
|
|
|
P_36: pull up
|
|
|
|
P_37: pull up
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
Make sure leds are on pull-up default pins
|
|
|
|
so when their state is undefined, the led
|
|
|
|
strips are switched off!
|
|
|
|
|
2019-09-17 08:19:24 +02:00
|
|
|
P_22: pull up
|
|
|
|
P_18: pull up
|
|
|
|
P_16: pull up
|
|
|
|
|
|
|
|
Circuit diagrams for the contact and button electronics:
|
|
|
|
|
|
|
|
All buttons are close-contact.
|
|
|
|
|
2019-09-23 14:27:43 +02:00
|
|
|
+3.3V DC ---+-----------+-----------+-----------+-------->
|
2019-09-17 08:19:24 +02:00
|
|
|
| | | |
|
2019-09-23 14:27:43 +02:00
|
|
|
R2 R4 R6 R8
|
2019-09-19 08:58:21 +02:00
|
|
|
| | | |
|
|
|
|
+----+ +----+ +----+ +----+
|
|
|
|
| | | | | | | |
|
2019-10-29 13:33:47 +01:00
|
|
|
| P_32 | P_33 | P_36 | P_37
|
2019-09-19 08:58:21 +02:00
|
|
|
\ \ \ \
|
|
|
|
\ \ \ \
|
2019-09-17 08:19:24 +02:00
|
|
|
\ \ \ \
|
|
|
|
* * * *
|
|
|
|
| | | |
|
|
|
|
GND --------+-----------+-----------+-----------+-------->
|
|
|
|
|
|
|
|
LED Strip controller and driver:
|
|
|
|
|
|
|
|
A power MOSFET per channel is driven by the GPIO output of the raspberry to
|
|
|
|
control the flow from the LED Strip contact on the connector to the ground rail.
|
|
|
|
|
|
|
|
P_22 ---+ LED_STRIP_RED-----+
|
|
|
|
| |
|
|
|
|
R9 Q1 | |--+ D
|
|
|
|
| |
|
|
|
|
+----------------| |<-+
|
|
|
|
| G | |
|
|
|
|
R10 | |--+ S
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
GND ----+---------------------+----->
|
|
|
|
|
|
|
|
P_18 ---+ LED_STRIP_GRN-----+
|
|
|
|
| |
|
|
|
|
R11 Q2 | |--+ D
|
|
|
|
| |
|
|
|
|
+----------------| |<-+
|
|
|
|
| G | |
|
|
|
|
R12 | |--+ S
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
GND ----+---------------------+----->
|
|
|
|
|
|
|
|
P_16 ---+ LED_STRIP_BLU-----+
|
|
|
|
| |
|
|
|
|
R13 Q3 | |--+ D
|
|
|
|
| |
|
|
|
|
+----------------| |<-+
|
|
|
|
| G | |
|
|
|
|
R14 | |--+ S
|
|
|
|
| |
|
|
|
|
| |
|
|
|
|
GND ----+---------------------+----->
|
|
|
|
|
|
|
|
LED Strip:
|
|
|
|
|
|
|
|
The LED Strip has a 4 contact connector, one being the 12V supply rail
|
|
|
|
and the other 3 the ground connections for the corresponding LED colour.
|
|
|
|
|
|
|
|
+12V DC ----+---------------------------->
|
|
|
|
|
|
|
|
|
| a series of LEDs and
|
|
|
|
| resistors, as determined
|
|
|
|
| by the LED strip.
|
|
|
|
|
|
|
|
|
+--( ->|- -[|||]- ->|- )--+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LED_STRIP_RED/GRN/BLU
|
|
|
|
|
|
|
|
Part list:
|
|
|
|
|
|
|
|
Resistors:
|
|
|
|
R2...........10,000 Ohm
|
|
|
|
R4...........10,000 Ohm
|
|
|
|
R6...........10,000 Ohm
|
|
|
|
R8...........10,000 Ohm
|
|
|
|
R9............1,000 Ohm
|
|
|
|
R10..........10,000 Ohm
|
|
|
|
R11...........1,000 Ohm
|
|
|
|
R12..........10,000 Ohm
|
|
|
|
R13...........1,000 Ohm
|
|
|
|
R14..........10,000 Ohm
|
|
|
|
|
|
|
|
Transistors:
|
|
|
|
Q1...........IRLZ34N Enhancement Mode n-channel MOSFET
|
|
|
|
Q2...........IRLZ34N Enhancement Mode n-channel MOSFET
|
|
|
|
Q3...........IRLZ34N Enhancement Mode n-channel MOSFET
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
Credits:
|
|
|
|
Based on a script made by TODO: source, although it has been heavily altered
|
2019-09-17 08:19:24 +02:00
|
|
|
Changes made by: Clima Philip, Krajnc Moris, Cooke Thomas, Glantschnig Raphael
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
Hosted on:
|
2019-09-17 08:19:24 +02:00
|
|
|
https://git.wolfsberg.local/philipp.clima/heisser_draht
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
############################################
|
|
|
|
############# START OF IMPORTS #############
|
|
|
|
############################################
|
2019-09-17 08:19:24 +02:00
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
import os
|
2019-09-17 08:19:24 +02:00
|
|
|
import time
|
2019-09-13 08:18:54 +02:00
|
|
|
import pygame
|
|
|
|
from PIL import Image
|
|
|
|
from pygame.locals import *
|
|
|
|
import RPi.GPIO as GPIO
|
|
|
|
|
|
|
|
############################################
|
|
|
|
############## END OF IMPORTS ##############
|
|
|
|
############################################
|
|
|
|
|
|
|
|
############################################
|
|
|
|
########### START OF DEFINITIONS ###########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
# base pygame settings
|
|
|
|
pygame_fps = 30 #frames per second setting
|
|
|
|
pygame_clock = pygame.time.Clock()
|
|
|
|
|
2019-10-29 15:54:18 +01:00
|
|
|
# enable border drawing
|
|
|
|
cfg_borders_enabled = False
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
# GPIO for Buttons
|
|
|
|
pin_start = 32
|
|
|
|
pin_stop = 37
|
|
|
|
pin_error = 33
|
|
|
|
pin_shutdown = 36
|
|
|
|
|
|
|
|
# GPIO for LED
|
|
|
|
pin_red = 22
|
|
|
|
pin_green = 18
|
|
|
|
pin_blue = 16
|
|
|
|
|
|
|
|
# other constants
|
|
|
|
# every time the wire is touched, some time is added as penalty.
|
|
|
|
# number is in ms
|
|
|
|
time_per_error = 5000
|
|
|
|
|
|
|
|
# name length for highscores
|
|
|
|
max_name_length = 10
|
|
|
|
|
|
|
|
# preset highscores
|
|
|
|
hs1_name = "Fritz"
|
|
|
|
hs2_name = "Bernd"
|
|
|
|
hs3_name = "Max"
|
2019-09-13 12:15:30 +02:00
|
|
|
hs1_time = 100000
|
|
|
|
hs2_time = 200000
|
|
|
|
hs3_time = 300000
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
############################################
|
|
|
|
############ END OF DEFINITIONS ############
|
|
|
|
############################################
|
|
|
|
|
|
|
|
############################################
|
|
|
|
####### START OF PRE-INITIALISATION ########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
# here goes stuff that is requires by functions
|
|
|
|
# so they can be properly defined
|
|
|
|
|
|
|
|
# initialise the game
|
|
|
|
pygame.init()
|
|
|
|
|
2019-09-17 08:19:24 +02:00
|
|
|
# screen settings - autodetermined based on the initialised pygame video instance
|
|
|
|
screen_size_x = pygame.display.Info().current_w
|
|
|
|
screen_size_y = pygame.display.Info().current_h
|
|
|
|
|
|
|
|
# load sound effects
|
2019-10-29 15:54:18 +01:00
|
|
|
#error_sound = pygame.mixer.Sound('snd/buzz.wav')
|
|
|
|
error_sound = pygame.mixer.Sound('snd/nope.wav')
|
2019-09-17 08:19:24 +02:00
|
|
|
logoff_sound = pygame.mixer.Sound('snd/winxplogoff.wav')
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
# fonts
|
|
|
|
pygame_font_1 = pygame.font.Font('freesansbold.ttf', 90)
|
|
|
|
pygame_font_2 = pygame.font.Font('freesansbold.ttf', 65)
|
|
|
|
pygame_font_3 = pygame.font.Font('freesansbold.ttf', 45)
|
2020-07-16 10:58:01 +02:00
|
|
|
pygame_font_4 = pygame.font.Font('freesansbold.ttf', 16)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
# colors
|
|
|
|
pygame_color_green = pygame.Color(42, 217, 13)
|
|
|
|
pygame_color_black = pygame.Color(0, 0, 0)
|
|
|
|
pygame_color_white = pygame.Color(255, 255, 255)
|
|
|
|
pygame_color_yellow = pygame.Color(255, 215, 0)
|
|
|
|
pygame_color_grey = pygame.Color(196, 202, 206)
|
|
|
|
pygame_color_brown = pygame.Color(177, 86, 15)
|
|
|
|
|
|
|
|
# font colors
|
|
|
|
pygame_font_main_color = pygame_color_black
|
|
|
|
|
|
|
|
# initialise gpio
|
|
|
|
GPIO.setmode(GPIO.BOARD)
|
|
|
|
GPIO.setup(pin_start, GPIO.IN)
|
2019-09-13 12:15:30 +02:00
|
|
|
GPIO.setup(pin_stop, GPIO.IN)
|
2019-09-13 08:18:54 +02:00
|
|
|
GPIO.setup(pin_error, GPIO.IN)
|
|
|
|
GPIO.setup(pin_shutdown, GPIO.IN)
|
|
|
|
|
|
|
|
# predefinition of led variables, for use in functions
|
|
|
|
led_red = 0
|
|
|
|
led_green = 0
|
|
|
|
led_blue = 0
|
|
|
|
|
|
|
|
############################################
|
|
|
|
######## END OF PRE-INITIALISATION #########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
############################################
|
|
|
|
###### START OF FUNCTION DEFINITIONS #######
|
|
|
|
############################################
|
|
|
|
|
|
|
|
def get_image_width(filepath):
|
|
|
|
"""get the width of an image"""
|
|
|
|
return (Image.open(filepath).size)[0]
|
|
|
|
|
|
|
|
def get_image_height(filepath):
|
|
|
|
"""get the height of an image"""
|
|
|
|
return (Image.open(filepath).size)[1]
|
|
|
|
|
|
|
|
def signal_handler(sig, frame):
|
|
|
|
"""
|
|
|
|
signal handler, later called to interpret
|
|
|
|
ctrl+c as "quit" rather than "abort"
|
|
|
|
"""
|
|
|
|
print('Programm exiting')
|
|
|
|
exit_application()
|
|
|
|
|
|
|
|
def led_init():
|
|
|
|
"""
|
|
|
|
initialises the LED pins
|
|
|
|
mind that the pins for the LEDs are pullup by default,
|
|
|
|
which is on purpose, since this causes the LED strip to
|
|
|
|
be turned off when the program isnt running or their state
|
|
|
|
is undefined
|
|
|
|
|
|
|
|
set the led pins to be outputs
|
|
|
|
careful: these pins dont drive the
|
|
|
|
led strip themselves, but rather 3
|
|
|
|
n-channel mosfets, type IRLZ34N
|
|
|
|
"""
|
|
|
|
global led_red, led_green, led_blue
|
|
|
|
GPIO.setup(pin_red, GPIO.OUT)
|
|
|
|
GPIO.setup(pin_green, GPIO.OUT)
|
|
|
|
GPIO.setup(pin_blue, GPIO.OUT)
|
|
|
|
|
|
|
|
# set the pwm frequency to 100
|
|
|
|
# if you experience strobing, try changing
|
|
|
|
# the number
|
|
|
|
led_red = GPIO.PWM(pin_red, 100)
|
|
|
|
led_blue = GPIO.PWM(pin_blue, 100)
|
|
|
|
led_green = GPIO.PWM(pin_green, 100)
|
|
|
|
|
|
|
|
# start pwm generation with a duty cycle of 0
|
|
|
|
led_green.start(0)
|
|
|
|
led_blue.start(0)
|
|
|
|
led_red.start(0)
|
|
|
|
|
|
|
|
def change_red(amount):
|
|
|
|
"""
|
|
|
|
takes an input from 0 to 255 and converts it to Duty Cycle
|
|
|
|
valid duty cycle values are between 0 and 100
|
|
|
|
stores the new value in led_brightness_COLOUR
|
|
|
|
this value is later read by the led_handler to set the
|
|
|
|
corresponding duty cycles to allow fast interactive updating
|
|
|
|
to be used by change_led_colour
|
|
|
|
"""
|
|
|
|
|
|
|
|
global led_red
|
|
|
|
factor = 100/255
|
2019-10-29 15:54:18 +01:00
|
|
|
led_red.ChangeDutyCycle(amount*factor)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
def change_green(amount):
|
|
|
|
"""
|
|
|
|
takes an input from 0 to 255 and converts it to Duty Cycle
|
|
|
|
valid duty cycle values are between 0 and 100
|
|
|
|
stores the new value in led_brightness_COLOUR
|
|
|
|
this value is later read by the led_handler to set the
|
|
|
|
corresponding duty cycles to allow fast interactive updating
|
|
|
|
to be used by change_led_colour
|
|
|
|
"""
|
|
|
|
|
|
|
|
global led_green
|
|
|
|
factor = 100/255
|
|
|
|
led_green.ChangeDutyCycle(amount*factor)
|
|
|
|
|
|
|
|
def change_blue(amount):
|
|
|
|
"""
|
|
|
|
takes an input from 0 to 255 and converts it to Duty Cycle
|
|
|
|
valid duty cycle values are between 0 and 100
|
|
|
|
stores the new value in led_brightness_COLOUR
|
|
|
|
this value is later read by the led_handler to set the
|
|
|
|
corresponding duty cycles to allow fast interactive updating
|
|
|
|
to be used by change_led_colour
|
|
|
|
"""
|
|
|
|
|
|
|
|
global led_blue
|
|
|
|
factor = 100/255
|
|
|
|
led_blue.ChangeDutyCycle(amount*factor)
|
|
|
|
|
|
|
|
def change_led_colour(red_amount, green_amount, blue_amount):
|
|
|
|
"""
|
|
|
|
takes an rgb value as input
|
|
|
|
and converts it to duty cycles
|
|
|
|
and then applies that.
|
|
|
|
"""
|
|
|
|
global led_red, led_green, led_blue
|
|
|
|
# sets an RGB value for the led strip.
|
|
|
|
if red_amount < 0 or red_amount > 255:
|
|
|
|
# valid rgb values should be between 0 and 255
|
|
|
|
print('change_led_colour: invalid red value received: ' + str(red_amount))
|
|
|
|
return False
|
|
|
|
if green_amount < 0 or green_amount > 255:
|
|
|
|
# valid rgb values should be between 0 and 255
|
|
|
|
print('change_led_colour: invalid green value received: '+ str(green_amount))
|
|
|
|
return False
|
|
|
|
if blue_amount < 0 or blue_amount > 255:
|
|
|
|
# valid rgb values should be between 0 and 255
|
|
|
|
print('change_led_colour: invalid blue value received: ' + str(blue_amount))
|
|
|
|
return False
|
|
|
|
|
|
|
|
change_red(red_amount)
|
|
|
|
change_blue(blue_amount)
|
|
|
|
change_green(green_amount)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def clear_screen():
|
2019-10-29 10:08:07 +01:00
|
|
|
"""fills the screen with a colour and draws the background"""
|
2019-09-13 08:18:54 +02:00
|
|
|
screen.fill(pygame_color_white)
|
2019-10-29 10:08:07 +01:00
|
|
|
draw_background()
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
def handle_events():
|
|
|
|
"""looks for things to do every tick"""
|
|
|
|
for event in pygame.event.get():
|
|
|
|
if event.type == KEYDOWN and event.key == K_ESCAPE:
|
|
|
|
exit_application()
|
|
|
|
|
|
|
|
# shutdown raspberry if shutdown pin is detected
|
2019-09-19 08:58:21 +02:00
|
|
|
if not GPIO.input(pin_shutdown):
|
2019-09-13 08:18:54 +02:00
|
|
|
shutdown_raspberry()
|
|
|
|
|
|
|
|
def exit_application():
|
|
|
|
""" exit the application with proper cleanup"""
|
2019-09-17 08:19:24 +02:00
|
|
|
logoff_sound.play()
|
|
|
|
time.sleep(3)
|
2019-09-13 08:18:54 +02:00
|
|
|
pygame.quit()
|
|
|
|
GPIO.cleanup()
|
|
|
|
sys.exit()
|
|
|
|
|
|
|
|
def shutdown_raspberry():
|
|
|
|
"""shutdown the system with proper cleanup"""
|
2019-09-17 08:19:24 +02:00
|
|
|
logoff_sound.play()
|
|
|
|
time.sleep(3)
|
2019-09-13 08:18:54 +02:00
|
|
|
pygame.quit()
|
|
|
|
GPIO.cleanup()
|
|
|
|
os.system("sudo shutdown -h now")
|
|
|
|
|
|
|
|
def enter_name():
|
|
|
|
"""asks for a name on screen and then returns that name"""
|
|
|
|
clear_screen()
|
|
|
|
print('text entry started')
|
|
|
|
name = ''
|
|
|
|
while True:
|
|
|
|
clear_screen()
|
|
|
|
|
|
|
|
# add all needed surfaces
|
|
|
|
highscore_surface = pygame_font_1.render('High Score!', True, pygame_font_main_color)
|
|
|
|
highscore_rectangle = highscore_surface.get_rect()
|
2019-10-29 11:53:15 +01:00
|
|
|
highscore_rectangle.topleft = (700, 350)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
screen.blit(highscore_surface, highscore_rectangle)
|
2020-07-16 08:22:56 +02:00
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
textbox_text_surface = pygame_font_2.render('Enter Name:', True, pygame_font_main_color)
|
|
|
|
textbox_text_rectangle = textbox_text_surface.get_rect()
|
2019-10-29 11:53:15 +01:00
|
|
|
textbox_text_rectangle.topleft = (700, 500)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
textbox_surface = pygame_font_2.render(str(name), True, pygame_font_main_color)
|
|
|
|
textbox_rectangle = textbox_surface.get_rect()
|
2019-10-29 11:53:15 +01:00
|
|
|
textbox_rectangle.topleft = (800, 580)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
# draw everything
|
|
|
|
screen.blit(textbox_text_surface, textbox_text_rectangle)
|
|
|
|
screen.blit(textbox_surface, textbox_rectangle)
|
|
|
|
|
|
|
|
# add the border and logos
|
2019-10-29 15:54:18 +01:00
|
|
|
draw_border()
|
2019-09-13 08:18:54 +02:00
|
|
|
draw_logos()
|
|
|
|
|
|
|
|
# display everything
|
|
|
|
pygame.display.flip()
|
|
|
|
|
|
|
|
# handle key events
|
|
|
|
for event in pygame.event.get():
|
|
|
|
# read new keys for as long as the max length for the name hasnt been reached
|
|
|
|
if len(name) < max_name_length:
|
|
|
|
if event.type == KEYDOWN:
|
|
|
|
# if someone presses the erase key, erase 1 letter
|
|
|
|
if event.key == pygame.K_BACKSPACE:
|
|
|
|
name = name[:-1]
|
|
|
|
# if enter is pressed with at least 1 letter as the name,
|
|
|
|
# return the name
|
|
|
|
elif event.key == pygame.K_RETURN and len(name) > 0:
|
|
|
|
clear_screen()
|
|
|
|
return name
|
|
|
|
# if enter is pressed without any letters in the name,
|
|
|
|
# dont return a name. do nothing instead.
|
|
|
|
elif event.key == pygame.K_RETURN:
|
|
|
|
# do nothing
|
|
|
|
pass
|
|
|
|
# if escape is pressed, exit.
|
|
|
|
# this prevents lockups since the main game loop is not running inside this function.
|
|
|
|
elif event.key == pygame.K_ESCAPE:
|
|
|
|
exit_application()
|
|
|
|
# for any other key, add the letter to the name
|
|
|
|
# Caution: this includes all sorts of weird letters, control sequences, and other unprintable chars.
|
|
|
|
else:
|
|
|
|
name += event.unicode
|
|
|
|
# if someone presses the erase key, erase 1 letter
|
2019-09-13 12:15:30 +02:00
|
|
|
elif event.key == pygame.K_BACKSPACE:
|
|
|
|
name = name[:-1]
|
2019-09-13 08:18:54 +02:00
|
|
|
#
|
2019-09-13 12:15:30 +02:00
|
|
|
elif event.type == KEYDOWN and event.key == pygame.K_RETURN:
|
|
|
|
clear_screen()
|
|
|
|
return name
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
# this part of the code should be unreachable, and only here in case the event handling goes wrong horribly.
|
|
|
|
clear_screen()
|
|
|
|
name = 'Error'
|
|
|
|
return name
|
|
|
|
|
|
|
|
def check_highscores(time):
|
|
|
|
"""
|
|
|
|
check if a new highscore has been made
|
|
|
|
this is coded in a horrible way and adding more than 3 high scores with this sorta system would be too much work.
|
|
|
|
"""
|
|
|
|
global hs1_time, hs2_time, hs3_time, hs1_name, hs2_name, hs3_name
|
|
|
|
if time <= hs1_time:
|
|
|
|
print('new high score:'+str(time))
|
|
|
|
# make the second the third
|
|
|
|
hs3_time = hs2_time
|
|
|
|
hs3_name = hs2_name
|
|
|
|
|
|
|
|
# make the first the second
|
|
|
|
hs2_time = hs1_time
|
|
|
|
hs2_name = hs1_name
|
|
|
|
|
|
|
|
# new high score
|
|
|
|
hs1_time = time
|
|
|
|
hs1_name = enter_name()
|
|
|
|
return True
|
|
|
|
elif time <= hs2_time:
|
|
|
|
print('new second:'+str(time))
|
|
|
|
|
|
|
|
# make the second the third
|
|
|
|
hs3_time = hs2_time
|
|
|
|
hs3_name = hs2_name
|
|
|
|
|
|
|
|
# new second time
|
|
|
|
hs2_time = time
|
|
|
|
hs2_name = enter_name()
|
|
|
|
return True
|
|
|
|
elif time <= hs3_time:
|
|
|
|
print('new third:'+str(time))
|
|
|
|
hs3_time = time
|
|
|
|
hs3_name = enter_name()
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2019-10-29 10:08:07 +01:00
|
|
|
def draw_background():
|
|
|
|
"""draw background image"""
|
|
|
|
screen.blit(img_background_image, (0,0))
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
def draw_border():
|
|
|
|
"""draw rectangle border around everything"""
|
2019-10-29 15:54:18 +01:00
|
|
|
if cfg_borders_enabled:
|
|
|
|
pygame.draw.rect(screen, pygame_color_black, (500, 200, (screen_size_x-500*2), (screen_size_y-200*2)), 5)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
2019-09-19 13:51:58 +02:00
|
|
|
def draw_logos():
|
|
|
|
"""draw logos"""
|
|
|
|
screen.blit(img_itlablogo_image, (screen_size_x-img_itlablogo_imagex-20, screen_size_y-img_itlablogo_imagey-20))
|
|
|
|
screen.blit(img_metalliclogo_image, (20, screen_size_y-img_metalliclogo_imagey-20))
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
def show_debug():
|
|
|
|
"""show debug information"""
|
|
|
|
print('#############')
|
|
|
|
print('Game running: ' + str(game_running))
|
|
|
|
print('Game ending: ' + str(game_ending))
|
2019-09-17 08:19:24 +02:00
|
|
|
print('-------------')
|
2019-09-13 08:18:54 +02:00
|
|
|
print('Pin status: ')
|
2019-09-13 12:15:30 +02:00
|
|
|
print('Start pin: ' + str(GPIO.input(pin_start)))
|
2019-09-17 08:19:24 +02:00
|
|
|
print('Stop pin: ' + str(GPIO.input(pin_stop)))
|
2019-09-13 12:15:30 +02:00
|
|
|
print('Error pin: ' + str(GPIO.input(pin_error)))
|
2019-09-13 08:18:54 +02:00
|
|
|
print('Shutdown pin: ' + str(GPIO.input(pin_shutdown)))
|
2019-09-17 08:19:24 +02:00
|
|
|
print('-------------')
|
|
|
|
print('Tick: ' + str(pygame.time.get_ticks()))
|
2019-09-13 08:18:54 +02:00
|
|
|
print('#############')
|
|
|
|
|
|
|
|
############################################
|
|
|
|
####### END OF FUNCTION DEFINITIONS ########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
############################################
|
|
|
|
######### START OF INITIALISATION ##########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
# signal handing setup for controlled exit via ctrl+c
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
|
|
|
|
# hide mouse from screen
|
|
|
|
pygame.mouse.set_visible(False)
|
|
|
|
|
|
|
|
# start fullscreen mode with defined screen geometrics
|
|
|
|
screen = pygame.display.set_mode((screen_size_x, screen_size_y), pygame.FULLSCREEN)
|
|
|
|
#pygame.display.set_caption('Heisser Draht') # not required for fullscreen application
|
|
|
|
|
|
|
|
# currently unused because its not needed
|
|
|
|
#screen = toggle_fullscreen()
|
|
|
|
|
|
|
|
# define image variables
|
|
|
|
img_itlablogo = 'img/itlablogo.png'
|
2020-07-16 08:22:56 +02:00
|
|
|
img_itlablogo_image = pygame.image.load(img_itlablogo).convert_alpha()
|
2019-09-13 08:18:54 +02:00
|
|
|
img_itlablogo_imagex = get_image_width(img_itlablogo)
|
|
|
|
img_itlablogo_imagey = get_image_height(img_itlablogo)
|
|
|
|
|
2019-09-19 13:51:58 +02:00
|
|
|
img_metalliclogo = 'img/metalliclogo.png'
|
2020-07-16 08:22:56 +02:00
|
|
|
img_metalliclogo_image = pygame.image.load(img_metalliclogo).convert_alpha()
|
2019-09-19 13:51:58 +02:00
|
|
|
img_metalliclogo_imagex = get_image_width(img_metalliclogo)
|
|
|
|
img_metalliclogo_imagey = get_image_height(img_metalliclogo)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
2019-10-29 13:33:47 +01:00
|
|
|
img_background = 'img/bg.jpg'
|
2020-07-16 08:22:56 +02:00
|
|
|
img_background_image = pygame.image.load(img_background).convert()
|
2019-10-29 10:08:07 +01:00
|
|
|
img_background_imagex = get_image_width(img_background)
|
|
|
|
img_background_imagey = get_image_height(img_background)
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
# initialise led strip
|
|
|
|
led_init()
|
|
|
|
|
|
|
|
# set the start state
|
|
|
|
game_running = False
|
|
|
|
game_ending = False
|
|
|
|
|
|
|
|
# enable the one-shot for changing the LEDs
|
|
|
|
game_just_started = True
|
|
|
|
|
|
|
|
# set start inhibitor to false by default
|
|
|
|
pin_start_inhibit = False
|
|
|
|
|
|
|
|
############################################
|
|
|
|
########## END OF INITIALISATION ###########
|
|
|
|
############################################
|
|
|
|
|
|
|
|
############################################
|
|
|
|
######### START OF MAIN GAME LOOP ##########
|
|
|
|
############################################
|
|
|
|
while True:
|
|
|
|
# default actions to be done every cycle
|
|
|
|
clear_screen()
|
|
|
|
handle_events()
|
|
|
|
show_debug()
|
|
|
|
|
|
|
|
# State 0
|
|
|
|
|
|
|
|
if not game_running and not game_ending:
|
|
|
|
# one shot for changing the led colour
|
|
|
|
if game_just_started:
|
|
|
|
print('game just started, changed colour of leds to green')
|
2019-10-29 16:20:35 +01:00
|
|
|
change_led_colour(200, 0, 200)
|
2019-09-13 08:18:54 +02:00
|
|
|
game_just_started = False
|
|
|
|
|
|
|
|
# reset all the surfaces to be empty
|
|
|
|
time_surface = 0
|
|
|
|
errors_surface = 0
|
|
|
|
time_rectangle = 0
|
|
|
|
errors_rectangle = 0
|
|
|
|
|
|
|
|
# fill all surfaces again to contain the proper text
|
|
|
|
header_surface = pygame_font_1.render('Dr' + u'ü' + 'cken Sie Start!', True, pygame_font_main_color)
|
|
|
|
header_rectangle = header_surface.get_rect()
|
2019-10-29 13:33:47 +01:00
|
|
|
header_rectangle.topleft = (560, 270)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
highscore_header_surface = pygame_font_2.render('Highscores:', True, pygame_font_main_color)
|
|
|
|
highscore_header_rectangle = highscore_header_surface.get_rect()
|
|
|
|
highscore_header_rectangle = (770, 410)
|
|
|
|
|
|
|
|
score1_surface = pygame_font_3.render(hs1_name + ": " + str(hs1_time/1000) + 's', True, pygame_color_yellow)
|
|
|
|
score1_rectangle = score1_surface.get_rect()
|
|
|
|
score1_rectangle.topleft = (820, 540)
|
|
|
|
|
|
|
|
score2_surface = pygame_font_3.render(hs2_name + ": " + str(hs2_time/1000) + 's', True, pygame_color_grey)
|
|
|
|
score2_rectangle = score2_surface.get_rect()
|
|
|
|
score2_rectangle.topleft = (820, 630)
|
|
|
|
|
|
|
|
score3_surface = pygame_font_3.render(hs3_name + ": " + str(hs3_time/1000) + 's', True, pygame_color_brown)
|
|
|
|
score3_rectangle = score3_surface.get_rect()
|
|
|
|
score3_rectangle.topleft = (820, 720)
|
|
|
|
|
|
|
|
# draw headline, highscore headline, the 3 scores, as well as the logos to the screen
|
|
|
|
screen.blit(header_surface, header_rectangle)
|
|
|
|
screen.blit(highscore_header_surface, highscore_header_rectangle)
|
|
|
|
screen.blit(score1_surface, score1_rectangle)
|
|
|
|
screen.blit(score2_surface, score2_rectangle)
|
|
|
|
screen.blit(score3_surface, score3_rectangle)
|
2020-07-16 08:22:56 +02:00
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
# event handling for if the start button is pushed
|
|
|
|
#wait for the user to press start button
|
2019-09-19 08:58:21 +02:00
|
|
|
if not GPIO.input(pin_start) and not pin_start_inhibit:
|
2019-09-13 08:18:54 +02:00
|
|
|
# change game state to running and not ending
|
|
|
|
game_running = True
|
|
|
|
game_ending = False
|
|
|
|
|
|
|
|
# set an inhibitor bit to stop the contact from possibly vibrating and
|
|
|
|
# causing a stop of the game immediately after start
|
|
|
|
pin_start_inhibit = True
|
|
|
|
timer_pin_start_inhibit_starttime = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
# reset errors
|
|
|
|
errors = 0
|
|
|
|
error_added = False
|
|
|
|
error_cooldown_start = 0
|
|
|
|
|
|
|
|
# change the headline
|
|
|
|
header_surface = pygame_font_1.render('Spiel l' + u'ä' + 'uft!', True, pygame_font_main_color)
|
|
|
|
|
|
|
|
# start the timer
|
|
|
|
timer_game_running_start = pygame.time.get_ticks()
|
|
|
|
|
|
|
|
# change led colour to blue
|
|
|
|
change_led_colour(0, 0, 200)
|
|
|
|
|
|
|
|
# State 1
|
|
|
|
|
|
|
|
if game_running and not game_ending:
|
|
|
|
|
|
|
|
# reset the highscore check
|
|
|
|
highscore_checked = False
|
|
|
|
|
|
|
|
# if the start pin is inhibited and at least 3 seconds have passed,
|
|
|
|
# one shot: stop blocking the pin
|
|
|
|
if pin_start_inhibit:
|
|
|
|
if (pygame.time.get_ticks()-timer_pin_start_inhibit_starttime) > 3000:
|
|
|
|
pin_start_inhibit = False
|
|
|
|
|
|
|
|
# add errors if error pin is detected, only once per 500 ms
|
2019-09-19 08:58:21 +02:00
|
|
|
if not GPIO.input(pin_error):
|
2019-09-13 08:18:54 +02:00
|
|
|
if not error_added:
|
|
|
|
errors += 1
|
|
|
|
error_added = True
|
|
|
|
error_cooldown_start = pygame.time.get_ticks()
|
2019-09-17 08:19:24 +02:00
|
|
|
error_sound.play()
|
2019-09-13 08:18:54 +02:00
|
|
|
change_led_colour(200, 0, 0)
|
|
|
|
|
|
|
|
# if an error happened, error_added is set to true - which prohibits adding another error.
|
|
|
|
# after 500 ms error_added is set to false again.
|
|
|
|
# this provides a cooldown
|
|
|
|
if error_added and (pygame.time.get_ticks()-error_cooldown_start) > 500:
|
|
|
|
error_added = False
|
|
|
|
change_led_colour(0, 0, 200)
|
|
|
|
|
|
|
|
# calculate the current time
|
|
|
|
timer_game_running = (pygame.time.get_ticks() - timer_game_running_start) + (errors * time_per_error)
|
|
|
|
|
|
|
|
# fill the surface with the current time and errors
|
|
|
|
time_surface = pygame_font_2.render('Zeit: ' + str(timer_game_running/1000) + 's', True, pygame_font_main_color)
|
|
|
|
time_rectangle = time_surface.get_rect()
|
|
|
|
time_rectangle.topleft = (640, 480)
|
|
|
|
|
|
|
|
errors_surface = pygame_font_2.render('Fehler: ' + str(errors), True, pygame_font_main_color)
|
|
|
|
errors_rectangle = errors_surface.get_rect()
|
|
|
|
errors_rectangle.topleft = (640, 560)
|
|
|
|
|
|
|
|
clear_screen()
|
|
|
|
|
|
|
|
# draw errors, time and headline to the screen
|
|
|
|
screen.blit(errors_surface, errors_rectangle) #errors
|
|
|
|
screen.blit(time_surface, time_rectangle) #time
|
|
|
|
screen.blit(header_surface, header_rectangle) #header
|
2020-07-16 10:58:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
fps = pygame_font_4.render(str(int(pygame_clock.get_fps())), True, pygame.Color('white'))
|
|
|
|
screen.blit(fps, (50, 50))
|
|
|
|
|
2019-09-13 08:18:54 +02:00
|
|
|
# if another push of start is detected (i.e. the game is ending!)
|
2019-09-19 08:58:21 +02:00
|
|
|
if not GPIO.input(pin_stop):
|
2019-09-13 08:18:54 +02:00
|
|
|
# change led colour to red
|
2019-10-29 16:20:35 +01:00
|
|
|
change_led_colour(0, 200, 200)
|
2019-09-13 08:18:54 +02:00
|
|
|
|
|
|
|
# reset the one shot latch for the end of game timer
|
|
|
|
timer_game_ending_started = False
|
|
|
|
|
|
|
|
# change the state to not running and ending
|
|
|
|
game_running = False
|
|
|
|
game_ending = True
|
|
|
|
|
|
|
|
# State 2
|
|
|
|
|
|
|
|
if not game_running and game_ending:
|
|
|
|
# check highscores only once when game ended
|
|
|
|
if not highscore_checked:
|
|
|
|
check_highscores(timer_game_running)
|
|
|
|
highscore_checked = True
|
|
|
|
|
|
|
|
# timer for ending the game after 5 seconds
|
|
|
|
if not timer_game_ending_started:
|
|
|
|
timer_game_ending_timout = pygame.time.get_ticks()
|
|
|
|
timer_game_ending_started = True
|
|
|
|
|
|
|
|
clear_screen()
|
|
|
|
|
|
|
|
# change the headline to game over
|
|
|
|
header_surface = pygame_font_1.render('Game over!', True, pygame_font_main_color)
|
|
|
|
|
|
|
|
# draw errors, time, and new headline to the screen
|
|
|
|
screen.blit(errors_surface, errors_rectangle) #errors
|
|
|
|
screen.blit(time_surface, time_rectangle) #time
|
|
|
|
screen.blit(header_surface, header_rectangle) #header
|
|
|
|
|
|
|
|
# if 5 seconds have passed since the game state changed to ending (see: timer_game_ending_timeout)
|
|
|
|
if ((pygame.time.get_ticks()-timer_game_ending_timout)/1000) > 5:
|
|
|
|
# reset game_just_started flag to enable the one shot led change in the first state
|
|
|
|
game_just_started = True
|
|
|
|
|
|
|
|
# change state
|
|
|
|
game_running = False
|
|
|
|
game_ending = False
|
|
|
|
|
|
|
|
# reset highscore one shot
|
|
|
|
highscore_checked = False
|
|
|
|
|
|
|
|
# reset timer started one shot
|
|
|
|
timer_game_ending_started = False
|
|
|
|
|
|
|
|
# unblock the start pin unconditionally
|
|
|
|
pin_start_inhibit = False
|
|
|
|
|
|
|
|
# State 3
|
|
|
|
|
|
|
|
if game_running and game_ending:
|
|
|
|
# something went horribly wrong...
|
|
|
|
# this state is never supposed to be reached
|
|
|
|
pass
|
|
|
|
|
|
|
|
# overlaying structure - this is to be drawn no matter what's below
|
2019-10-29 15:54:18 +01:00
|
|
|
draw_border()
|
2019-09-13 08:18:54 +02:00
|
|
|
draw_logos()
|
|
|
|
|
|
|
|
# flip everything, update display, do a tick
|
|
|
|
pygame.display.flip()
|
|
|
|
pygame.display.update()
|
|
|
|
pygame_clock.tick(pygame_fps)
|
|
|
|
|
|
|
|
############################################
|
|
|
|
########## END OF MAIN GAME LOOP ###########
|
|
|
|
############################################
|