2019-12-02 08:45:03 -05:00
#!/bin/python
# -*- coding: utf-8 -*-
""" Standard initializations and functions shared by all games. """
2019-12-08 14:55:41 -05:00
from sys import exit
2019-12-02 08:45:03 -05:00
import configparser
import os
from os import listdir
from os . path import isfile , join
from inspect import isfunction
from xdg import BaseDirectory
2019-12-11 08:04:56 -05:00
from setproctitle import setproctitle
2019-12-02 08:45:03 -05:00
import pygame
import pyperclip
import random
2019-12-04 13:39:16 -05:00
import re
2019-12-02 08:45:03 -05:00
import requests
2019-12-08 08:08:20 -05:00
import webbrowser
2019-12-05 10:56:58 -05:00
# Global variable for speech provider
try :
import speechd
spd = speechd . Client ( )
speechProvider = " speechd "
2019-12-06 18:20:45 -05:00
except ImportError :
import accessible_output2 . outputs . auto
s = accessible_output2 . outputs . auto . Auto ( )
speechProvider = " accessible_output2 "
except ImportError :
print ( " No other speech providers found. " )
exit ( )
2019-12-02 08:45:03 -05:00
import time
localConfig = configparser . ConfigParser ( )
globalConfig = configparser . ConfigParser ( )
2019-12-10 14:12:15 -05:00
class scoreboard ( ) :
' Handles scores and top 10 '
2019-12-11 09:32:50 -05:00
def __init__ ( self , startingScore = 0 ) :
2019-12-11 21:45:52 -05:00
read_config ( )
2019-12-11 09:32:50 -05:00
try :
localConfig . add_section ( " scoreboard " )
except :
2020-08-30 01:23:32 -04:00
speak ( " Could not create scoreboard. " )
2019-12-11 09:32:50 -05:00
self . score = startingScore
2019-12-10 14:12:15 -05:00
self . oldScores = [ ]
2019-12-11 09:32:50 -05:00
for i in range ( 1 , 11 ) :
2019-12-10 14:12:15 -05:00
try :
2019-12-11 23:09:31 -05:00
self . oldScores . insert ( i - 1 , localConfig . getint ( " scoreboard " , i ) )
2019-12-10 14:12:15 -05:00
except :
2019-12-11 09:32:50 -05:00
pass
2019-12-11 21:45:52 -05:00
self . oldScores . insert ( i - 1 , 0 )
2019-12-11 09:32:50 -05:00
for i in range ( 1 , 11 ) :
if self . oldScores [ i - 1 ] == None :
self . oldScores [ i - 1 ] = 0
def __del__ ( self ) :
self . Update_Scores ( )
2019-12-11 21:45:52 -05:00
try :
write_config ( )
except :
2020-08-30 01:23:32 -04:00
speak ( " Could not update configuration file. "
2019-12-11 09:32:50 -05:00
def Decrease_Score ( self , points = 1 ) :
self . score - = points
def Get_High_Score ( self , position = 1 ) :
return self . oldScores [ position - 1 ]
def Get_Score ( self ) :
return self . score
def Increase_Score ( self , points = 1 ) :
self . score + = points
2019-12-11 23:31:19 -05:00
def New_High_Score ( self ) :
for i , j in enumerate ( self . oldScores ) :
if self . score > j : return i + 1
return None
2019-12-11 09:32:50 -05:00
def Update_Scores ( self ) :
2019-12-11 21:45:52 -05:00
# Update the scores
for i , j in enumerate ( self . oldScores ) :
if self . score > j :
2020-08-29 22:11:06 -04:00
# Move the old high score down the list
2020-08-30 01:23:32 -04:00
if i < 9 :
2020-08-29 22:11:06 -04:00
self . oldScores [ i + 1 ] = j
2019-12-11 21:45:52 -05:00
self . oldScores [ i ] = self . score
break
# Update the scoreboard section of the games config file.
for i , j in enumerate ( self . oldScores ) :
localConfig . set ( " scoreboard " , str ( i + 1 ) , str ( j ) )
2019-12-10 14:12:15 -05:00
2019-12-02 08:45:03 -05:00
def write_config ( writeGlobal = False ) :
if writeGlobal == False :
2019-12-11 21:45:52 -05:00
with open ( gamePath + " /config.ini " , ' w ' ) as configfile :
2019-12-02 08:45:03 -05:00
localConfig . write ( configfile )
else :
2019-12-11 21:45:52 -05:00
with open ( globalPath + " /config.ini " , ' w ' ) as configfile :
2019-12-02 08:45:03 -05:00
globalConfig . write ( configfile )
2019-12-11 21:45:52 -05:00
def read_config ( readGlobal = False ) :
2019-12-02 08:45:03 -05:00
if readGlobal == False :
2019-12-11 09:32:50 -05:00
try :
2019-12-11 21:45:52 -05:00
with open ( gamePath + " /config.ini " , ' r ' ) as configfile :
localConfig . read ( configfile )
2019-12-11 09:32:50 -05:00
except :
pass
2019-12-02 08:45:03 -05:00
else :
2019-12-11 09:32:50 -05:00
try :
2019-12-11 21:45:52 -05:00
with open ( globalPath + " /config.ini " , ' r ' ) as configfile :
globalConfig . read ( configfile )
2019-12-11 09:32:50 -05:00
except :
pass
2019-12-02 08:45:03 -05:00
def speak ( text , interupt = True ) :
2019-12-05 10:56:58 -05:00
if speechProvider == " speechd " :
if interupt == True : spd . cancel ( )
spd . say ( text )
2019-12-06 18:20:45 -05:00
else :
if speechProvider == " accessible_output2 " :
s . speak ( text , interrupt = True )
2019-12-02 08:45:03 -05:00
def exit_game ( ) :
2019-12-06 18:20:45 -05:00
if speechProvider == " speechd " : spd . close ( )
2019-12-02 08:45:03 -05:00
pygame . mixer . music . stop ( )
pygame . quit ( )
exit ( )
def initialize_gui ( gameTitle ) :
# Check for, and possibly create, storm-games path
global globalPath
global gamePath
globalPath = BaseDirectory . xdg_config_home + " /storm-games "
2019-12-10 18:36:29 -05:00
gamePath = globalPath + " / " + str . lower ( str . replace ( gameTitle , " " , " - " ) )
2019-12-02 08:45:03 -05:00
if not os . path . exists ( gamePath ) : os . makedirs ( gamePath )
# Seed the random generator to the clock
random . seed ( )
# Set game's name
global gameName
gameName = gameTitle
2019-12-11 09:37:58 -05:00
setproctitle ( str . lower ( str . replace ( gameTitle , " " , " " ) ) )
2019-12-02 08:45:03 -05:00
# start pygame
pygame . init ( )
# start the display (required by the event loop)
pygame . display . set_mode ( ( 320 , 200 ) )
pygame . display . set_caption ( gameTitle )
2019-12-05 12:33:54 -05:00
# Set 32 channels for sound by default
pygame . mixer . init ( )
pygame . mixer . set_num_channels ( 32 )
2019-12-05 10:30:44 -05:00
# Reserve the cut scene channel
pygame . mixer . set_reserved ( 0 )
2019-12-02 08:45:03 -05:00
# Load sounds from the sound directory and creates a list like that {'bottle': 'bottle.ogg'}
soundFiles = [ f for f in listdir ( " sounds/ " ) if isfile ( join ( " sounds/ " , f ) ) and ( f . split ( ' . ' ) [ 1 ] . lower ( ) in [ " ogg " , " wav " ] ) ]
#lets make a dict with pygame.mixer.Sound() objects {'bottle':<soundobject>}
soundData = { }
for f in soundFiles :
soundData [ f . split ( ' . ' ) [ 0 ] ] = pygame . mixer . Sound ( " sounds/ " + f )
soundData [ ' game-intro ' ] . play ( )
time . sleep ( soundData [ ' game-intro ' ] . get_length ( ) )
return soundData
2019-12-05 10:30:44 -05:00
def cut_scene ( sounds , soundName ) :
pygame . event . clear ( )
pygame . mixer . stop ( )
c = pygame . mixer . Channel ( 0 )
c . play ( sounds [ soundName ] )
while pygame . mixer . get_busy ( ) :
2019-12-05 12:33:54 -05:00
event = pygame . event . poll ( )
2019-12-06 09:40:36 -05:00
if event . type == pygame . KEYDOWN and event . key in [ pygame . K_ESCAPE , pygame . K_RETURN , pygame . K_SPACE ] :
2019-12-05 10:30:44 -05:00
pygame . mixer . stop ( )
pygame . event . pump ( )
2019-12-05 12:33:54 -05:00
def play_random ( sounds , soundName , pause = False , interrupt = False ) :
2019-12-04 18:06:03 -05:00
key = [ ]
for i in sounds . keys ( ) :
if re . match ( " ^ " + soundName + " .* " , i ) :
key . append ( i )
randomKey = random . choice ( key )
2019-12-05 12:33:54 -05:00
if interrupt == False :
sounds [ randomKey ] . play ( )
else :
cut_scene ( sounds , randomKey )
# Cut scenes override the pause option
return
2019-12-04 18:06:03 -05:00
if pause == True :
time . sleep ( sounds [ randomKey ] . get_length ( ) )
2019-12-04 13:39:16 -05:00
2019-12-02 08:45:03 -05:00
def instructions ( ) :
# Read in the instructions file
try :
with open ( ' files/instructions.txt ' , ' r ' ) as f :
info = f . readlines ( )
except :
info = [ " Instructions file is missing. " ]
display_text ( info )
def credits ( ) :
2019-12-08 16:25:11 -05:00
# Read in the credits file.
try :
with open ( ' files/credits.txt ' , ' r ' ) as f :
info = f . readlines ( )
# Add the header
info . insert ( 0 , gameName + " : brought to you by Storm Dragon " )
except :
info = [ " Credits file is missing. " ]
2019-12-02 08:45:03 -05:00
display_text ( info )
def display_text ( text ) :
i = 0
text . insert ( 0 , " Press space to read the whole text. Use up and down arrows to navigate the text line by line. Press c to copy the current line to the clipboard or t to copy the entire text. Press enter or escape when you are done reading. " )
text . append ( " End of text. " )
speak ( text [ i ] )
while True :
event = pygame . event . wait ( )
if event . type == pygame . KEYDOWN :
if event . key == pygame . K_ESCAPE or event . key == pygame . K_RETURN : return
if event . key == pygame . K_DOWN and i < len ( text ) - 1 : i = i + 1
if event . key == pygame . K_UP and i > 0 : i = i - 1
if event . key == pygame . K_SPACE :
2019-12-05 10:30:44 -05:00
speak ( ' ' . join ( text [ 1 : ] ) )
2019-12-02 08:45:03 -05:00
else :
speak ( text [ i ] )
if event . key == pygame . K_c :
try :
pyperclip . copy ( text [ i ] )
speak ( " Copied " + text [ i ] + " to the clipboard. " )
except :
speak ( " Failed to copy the text to the clipboard. " )
if event . key == pygame . K_t :
try :
2019-12-11 09:32:50 -05:00
pyperclip . copy ( ' ' . join ( text [ 1 : - 1 ] ) )
2019-12-02 08:45:03 -05:00
speak ( " Copied entire message to the clipboard. " )
except :
speak ( " Failed to copy the text to the clipboard. " )
event = pygame . event . clear ( )
time . sleep ( 0.001 )
def learn_sounds ( sounds ) :
loop = True
pygame . mixer . music . pause ( )
i = 0
soundFiles = [ f for f in listdir ( " sounds/ " ) if isfile ( join ( " sounds/ " , f ) ) and ( f . split ( ' . ' ) [ 1 ] . lower ( ) in [ " ogg " , " wav " ] ) and ( f . split ( ' . ' ) [ 0 ] . lower ( ) not in [ " game-intro " , " music_menu " ] ) ]
# j keeps track of last spoken index so it isn't voiced on key up.
j = - 1
while loop == True :
if i != j :
speak ( soundFiles [ i ] [ : - 4 ] )
j = i
event = pygame . event . wait ( )
if event . type == pygame . KEYDOWN :
if event . key == pygame . K_ESCAPE : return " menu "
if event . key == pygame . K_DOWN and i < len ( soundFiles ) - 1 :
2019-12-10 14:12:15 -05:00
pygame . mixer . stop ( )
2019-12-02 08:45:03 -05:00
i = i + 1
if event . key == pygame . K_UP and i > 0 :
2019-12-10 14:12:15 -05:00
pygame . mixer . stop ( )
2019-12-02 08:45:03 -05:00
i = i - 1
if event . key == pygame . K_RETURN :
try :
soundName = soundFiles [ i ] [ : - 4 ]
2019-12-10 14:12:15 -05:00
pygame . mixer . stop ( )
2019-12-02 08:45:03 -05:00
sounds [ soundName ] . play ( )
continue
except :
j = - 1
speak ( " Could not play sound. " )
continue
event = pygame . event . clear ( )
time . sleep ( 0.001 )
2019-12-10 14:12:15 -05:00
def game_menu ( sounds , * options ) :
2019-12-02 08:45:03 -05:00
loop = True
if pygame . mixer . music . get_busy ( ) :
pygame . mixer . music . unpause ( )
else :
pygame . mixer . music . load ( " sounds/music_menu.ogg " )
pygame . mixer . music . set_volume ( 0.75 )
pygame . mixer . music . play ( - 1 )
i = 0
# j keeps track of last spoken index so it isn't voiced on key up.
j = - 1
while loop == True :
if i != j :
speak ( options [ i ] )
j = i
event = pygame . event . wait ( )
if event . type == pygame . KEYDOWN :
if event . key == pygame . K_ESCAPE : exit_game ( )
if event . key == pygame . K_DOWN and i < len ( options ) - 1 :
i = i + 1
2019-12-10 14:12:15 -05:00
try :
sounds [ ' menu-move ' ] . play ( )
except :
pass
2019-12-02 08:45:03 -05:00
if options [ i ] != " donate " : pygame . mixer . music . unpause ( )
if event . key == pygame . K_UP and i > 0 :
i = i - 1
2019-12-10 14:12:15 -05:00
try :
sounds [ ' menu-move ' ] . play ( )
except :
pass
2019-12-02 08:45:03 -05:00
if options [ i ] != " donate " : pygame . mixer . music . unpause ( )
if event . key == pygame . K_RETURN :
try :
j = - 1
2019-12-10 14:12:15 -05:00
try :
sounds [ ' menu-select ' ] . play ( )
time . sleep ( sounds [ ' menu-select ' ] . get_length ( ) )
except :
pass
2019-12-02 08:45:03 -05:00
eval ( options [ i ] + " () " )
continue
except :
j = - 1
return options [ i ]
continue
event = pygame . event . clear ( )
time . sleep ( 0.001 )
def donate ( ) :
pygame . mixer . music . pause ( )
2019-12-08 16:09:46 -05:00
webbrowser . open ( ' https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=stormdragon2976@gmail.com&lc=US&item_name=Donation+to+Storm+Games&no_note=0&cn=¤cy_code=USD&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHosted ' )