Compare commits
	
		
			3 Commits
		
	
	
		
			e7d5b03e55
			...
			testing
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e1451f69b0 | ||
| 
						 | 
					401b2a8527 | ||
| 
						 | 
					639198e8de | 
							
								
								
									
										509
									
								
								__init__.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										509
									
								
								__init__.py
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@@ -1,511 +1,4 @@
 | 
			
		||||
#!/bin/python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
"""Standard initializations and functions shared by all games."""
 | 
			
		||||
 | 
			
		||||
from sys import exit
 | 
			
		||||
import configparser
 | 
			
		||||
import os
 | 
			
		||||
from os import listdir
 | 
			
		||||
from os.path import isfile, join
 | 
			
		||||
from inspect import isfunction
 | 
			
		||||
from xdg import BaseDirectory
 | 
			
		||||
from setproctitle import setproctitle
 | 
			
		||||
import pygame
 | 
			
		||||
import pyperclip
 | 
			
		||||
import random
 | 
			
		||||
import re
 | 
			
		||||
import requests
 | 
			
		||||
import textwrap
 | 
			
		||||
import webbrowser
 | 
			
		||||
# Global variable for speech provider
 | 
			
		||||
try:
 | 
			
		||||
    import speechd
 | 
			
		||||
    spd = speechd.Client()
 | 
			
		||||
    speechProvider = "speechd"
 | 
			
		||||
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()
 | 
			
		||||
import math
 | 
			
		||||
import numpy as np
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
localConfig = configparser.ConfigParser()
 | 
			
		||||
globalConfig = configparser.ConfigParser()
 | 
			
		||||
 | 
			
		||||
class scoreboard():
 | 
			
		||||
    'Handles scores and top 10'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, startingScore = 0):
 | 
			
		||||
        read_config()
 | 
			
		||||
        try:
 | 
			
		||||
            localConfig.add_section("scoreboard")
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
        self.score = startingScore
 | 
			
		||||
        self.oldScores = []
 | 
			
		||||
        for i in range(1, 11):
 | 
			
		||||
            try:
 | 
			
		||||
                self.oldScores.insert(i - 1, localConfig.getint("scoreboard", str(i)))
 | 
			
		||||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
                self.oldScores.insert(i - 1, 0)
 | 
			
		||||
        for i in range(1, 11):
 | 
			
		||||
            if self.oldScores[i - 1] == None:
 | 
			
		||||
                self.oldScores[i - 1] = 0
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        self.Update_Scores()
 | 
			
		||||
        try:
 | 
			
		||||
            write_config()
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    def New_High_Score(self):
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
            if self.score > j: return i + 1
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def Update_Scores(self):
 | 
			
		||||
        # Update the scores
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
            if self.score > j:
 | 
			
		||||
                self.oldScores.insert(i, self.score)
 | 
			
		||||
                break
 | 
			
		||||
        # Only keep the top 10 scores.
 | 
			
		||||
        self.oldScores = self.oldScores[:10]
 | 
			
		||||
        # Update the scoreboard section of the games config file.
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
                localConfig.set("scoreboard", str(i + 1), str(j))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_config(writeGlobal = False):
 | 
			
		||||
    if writeGlobal == False:
 | 
			
		||||
        with open(gamePath + "/config.ini", 'w') as configfile:
 | 
			
		||||
            localConfig.write(configfile)
 | 
			
		||||
    else:
 | 
			
		||||
        with open(globalPath + "/config.ini", 'w') as configfile:
 | 
			
		||||
            globalConfig.write(configfile)
 | 
			
		||||
 | 
			
		||||
def read_config(readGlobal = False):
 | 
			
		||||
    if readGlobal == False:
 | 
			
		||||
        try:
 | 
			
		||||
            with open(gamePath + "/config.ini", 'r') as configfile:
 | 
			
		||||
                localConfig.read_file(configfile)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            with open(globalPath + "/config.ini", 'r') as configfile:
 | 
			
		||||
                globalConfig.read_file(configfile)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
def speak(text, interupt=True):
 | 
			
		||||
    if speechProvider == "speechd":
 | 
			
		||||
        if interupt: spd.cancel()
 | 
			
		||||
        spd.say(text)
 | 
			
		||||
    else:
 | 
			
		||||
        if speechProvider == "accessible_output2":
 | 
			
		||||
            s.speak(text, interrupt=True)
 | 
			
		||||
    # Display the text on screen
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    font = pygame.font.Font(None, 36)
 | 
			
		||||
    # Wrap the text
 | 
			
		||||
    maxWidth = screen.get_width() - 40  # Leave a 20-pixel margin on each side
 | 
			
		||||
    wrappedText = textwrap.wrap(text, width=maxWidth // font.size('A')[0])
 | 
			
		||||
    # Render each line
 | 
			
		||||
    textSurfaces = [font.render(line, True, (255, 255, 255)) for line in wrappedText]
 | 
			
		||||
    screen.fill((0, 0, 0))  # Clear screen with black
 | 
			
		||||
    # Calculate total height of text block
 | 
			
		||||
    totalHeight = sum(surface.get_height() for surface in textSurfaces)
 | 
			
		||||
    # Start y-position (centered vertically)
 | 
			
		||||
    currentY = (screen.get_height() - totalHeight) // 2
 | 
			
		||||
    # Blit each line of text
 | 
			
		||||
    for surface in textSurfaces:
 | 
			
		||||
        textRect = surface.get_rect(center=(screen.get_width() // 2, currentY + surface.get_height() // 2))
 | 
			
		||||
        screen.blit(surface, textRect)
 | 
			
		||||
        currentY += surface.get_height()
 | 
			
		||||
    pygame.display.flip()
 | 
			
		||||
 | 
			
		||||
def check_for_exit():
 | 
			
		||||
    for event in pygame.event.get():
 | 
			
		||||
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
    pygame.event.pump()
 | 
			
		||||
 | 
			
		||||
def exit_game():
 | 
			
		||||
    if speechProvider == "speechd": spd.close()
 | 
			
		||||
    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"
 | 
			
		||||
    gamePath = globalPath + "/" + str.lower(str.replace(gameTitle, " ", "-"))
 | 
			
		||||
    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
 | 
			
		||||
    setproctitle(str.lower(str.replace(gameTitle, " ", "")))
 | 
			
		||||
    # start pygame
 | 
			
		||||
    pygame.init()
 | 
			
		||||
    # start the display (required by the event loop)
 | 
			
		||||
    pygame.display.set_mode((800, 600))
 | 
			
		||||
    pygame.display.set_caption(gameTitle)
 | 
			
		||||
    # Set 32 channels for sound by default
 | 
			
		||||
    pygame.mixer.pre_init(44100, -16, 2, 1024)
 | 
			
		||||
    pygame.mixer.init()
 | 
			
		||||
    pygame.mixer.set_num_channels(32)
 | 
			
		||||
    # Reserve the cut scene channel
 | 
			
		||||
    pygame.mixer.set_reserved(0)
 | 
			
		||||
    # Load sounds from the sound directory and creates a list like {'bottle': 'bottle.ogg'}
 | 
			
		||||
    try:
 | 
			
		||||
        soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"])]
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print("No sounds found.")
 | 
			
		||||
        speak("No sounds found.", False)
 | 
			
		||||
    #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
 | 
			
		||||
 | 
			
		||||
def generate_tone(frequency, duration=0.1, sample_rate=44100, volume = 0.2):
 | 
			
		||||
    t = np.linspace(0, duration, int(sample_rate * duration), False)
 | 
			
		||||
    tone = np.sin(2 * np.pi * frequency * t)
 | 
			
		||||
    stereo_tone = np.vstack((tone, tone)).T  # Create a 2D array for stereo
 | 
			
		||||
    stereo_tone = (stereo_tone * 32767).astype(np.int16)
 | 
			
		||||
    stereo_tone = (stereo_tone * 32767 * volume).astype(np.int16)  # Apply volume
 | 
			
		||||
    stereo_tone = np.ascontiguousarray(stereo_tone)  # Ensure C-contiguous array
 | 
			
		||||
    return pygame.sndarray.make_sound(stereo_tone)
 | 
			
		||||
 | 
			
		||||
def x_powerbar():
 | 
			
		||||
    clock = pygame.time.Clock()
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    position = -50  # Start from the leftmost position
 | 
			
		||||
    direction = 1   # Move right initially
 | 
			
		||||
    barHeight = 20
 | 
			
		||||
    while True:
 | 
			
		||||
        frequency = 440  # A4 note
 | 
			
		||||
        leftVolume = (50 - position) / 100
 | 
			
		||||
        rightVolume = (position + 50) / 100
 | 
			
		||||
        tone = generate_tone(frequency)
 | 
			
		||||
        channel = tone.play()
 | 
			
		||||
        channel.set_volume(leftVolume, rightVolume)
 | 
			
		||||
        # Visual representation
 | 
			
		||||
        screen.fill((0, 0, 0))
 | 
			
		||||
        barWidth = screen.get_width() - 40  # Leave 20px margin on each side
 | 
			
		||||
        pygame.draw.rect(screen, (100, 100, 100), (20, screen.get_height() // 2 - barHeight // 2, barWidth, barHeight))
 | 
			
		||||
        markerPos = int(20 + (position + 50) / 100 * barWidth)
 | 
			
		||||
        pygame.draw.rect(screen, (255, 0, 0), (markerPos - 5, screen.get_height() // 2 - barHeight, 10, barHeight * 2))
 | 
			
		||||
        pygame.display.flip()
 | 
			
		||||
        for event in pygame.event.get():
 | 
			
		||||
            check_for_exit()
 | 
			
		||||
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
 | 
			
		||||
                channel.stop()
 | 
			
		||||
                return position  # This will return a value between -50 and 50
 | 
			
		||||
        position += direction
 | 
			
		||||
        if position > 50:
 | 
			
		||||
            position = 50
 | 
			
		||||
            direction = -1
 | 
			
		||||
        elif position < -50:
 | 
			
		||||
            position = -50
 | 
			
		||||
            direction = 1
 | 
			
		||||
        clock.tick(40) # Speed of bar
 | 
			
		||||
 | 
			
		||||
def y_powerbar():
 | 
			
		||||
    clock = pygame.time.Clock()
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    power = 0
 | 
			
		||||
    direction = 1  # 1 for increasing, -1 for decreasing
 | 
			
		||||
    barWidth = 20
 | 
			
		||||
    while True:
 | 
			
		||||
        frequency = 220 + (power * 5)  # Adjust these values to change the pitch range
 | 
			
		||||
        tone = generate_tone(frequency)
 | 
			
		||||
        channel = tone.play()
 | 
			
		||||
        # Visual representation
 | 
			
		||||
        screen.fill((0, 0, 0))
 | 
			
		||||
        barHeight = screen.get_height() - 40  # Leave 20px margin on top and bottom
 | 
			
		||||
        pygame.draw.rect(screen, (100, 100, 100), (screen.get_width() // 2 - barWidth // 2, 20, barWidth, barHeight))
 | 
			
		||||
        markerPos = int(20 + (100 - power) / 100 * barHeight)
 | 
			
		||||
        pygame.draw.rect(screen, (255, 0, 0), (screen.get_width() // 2 - barWidth, markerPos - 5, barWidth * 2, 10))
 | 
			
		||||
        pygame.display.flip()
 | 
			
		||||
        for event in pygame.event.get():
 | 
			
		||||
            check_for_exit()
 | 
			
		||||
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
 | 
			
		||||
                channel.stop()
 | 
			
		||||
                return power
 | 
			
		||||
        power += direction
 | 
			
		||||
        if power >= 100 or power <= 0:
 | 
			
		||||
            direction *= -1  # Reverse direction at limits
 | 
			
		||||
        clock.tick(40)
 | 
			
		||||
 | 
			
		||||
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():
 | 
			
		||||
        event = pygame.event.poll()
 | 
			
		||||
        if event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_RETURN, pygame.K_SPACE]:
 | 
			
		||||
            pygame.mixer.stop()
 | 
			
		||||
        pygame.event.pump()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def calculate_volume_and_pan(player_pos, obj_pos):
 | 
			
		||||
    distance = abs(player_pos - obj_pos)
 | 
			
		||||
    max_distance = 12  # Maximum audible distance
 | 
			
		||||
    if distance > max_distance:
 | 
			
		||||
        return 0, 0, 0  # No sound if out of range
 | 
			
		||||
    # Calculate volume (non-linear scaling for more noticeable changes)
 | 
			
		||||
    volume = ((max_distance - distance) / max_distance) ** 1.5
 | 
			
		||||
    # Determine left/right based on relative position
 | 
			
		||||
    if player_pos < obj_pos:
 | 
			
		||||
        # Object is to the right
 | 
			
		||||
        left = max(0, 1 - (obj_pos - player_pos) / max_distance)
 | 
			
		||||
        right = 1
 | 
			
		||||
    elif player_pos > obj_pos:
 | 
			
		||||
        # Object is to the left
 | 
			
		||||
        left = 1
 | 
			
		||||
        right = max(0, 1 - (player_pos - obj_pos) / max_distance)
 | 
			
		||||
    else:
 | 
			
		||||
        # Player is on the object
 | 
			
		||||
        left = right = 1
 | 
			
		||||
    return volume, left, right
 | 
			
		||||
 | 
			
		||||
def obj_play(sounds, soundName, player_pos, obj_pos):
 | 
			
		||||
    volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
 | 
			
		||||
    if volume == 0:
 | 
			
		||||
        return None  # Don't play if out of range
 | 
			
		||||
    # Play the sound on a new channel
 | 
			
		||||
    x = sounds[soundName].play(-1)
 | 
			
		||||
    # Apply the volume and pan
 | 
			
		||||
    x.set_volume(volume * left, volume * right)
 | 
			
		||||
    return x
 | 
			
		||||
def obj_update(x, player_pos, obj_pos):
 | 
			
		||||
    if x is None:
 | 
			
		||||
        return None
 | 
			
		||||
    volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
 | 
			
		||||
    if volume == 0:
 | 
			
		||||
        x.stop()
 | 
			
		||||
        return None
 | 
			
		||||
    # Apply the volume and pan
 | 
			
		||||
    x.set_volume(volume * left, volume * right)
 | 
			
		||||
    return x
 | 
			
		||||
    
 | 
			
		||||
def obj_stop(x):
 | 
			
		||||
    # Tries to stop a playing object channel
 | 
			
		||||
    try:
 | 
			
		||||
        x.stop()
 | 
			
		||||
        return None
 | 
			
		||||
    except:
 | 
			
		||||
        return x
 | 
			
		||||
 | 
			
		||||
def play_ambiance(sounds, soundNames, probability, randomLocation = False):
 | 
			
		||||
    # Check if any of the sounds in the list is already playing
 | 
			
		||||
    for soundName in soundNames:
 | 
			
		||||
        if pygame.mixer.find_channel(True) and pygame.mixer.find_channel(True).get_busy():
 | 
			
		||||
            return
 | 
			
		||||
    if random.randint(1, 100) > probability:
 | 
			
		||||
        return
 | 
			
		||||
    # Choose a random sound from the list
 | 
			
		||||
    ambianceSound = random.choice(soundNames)
 | 
			
		||||
    channel = sounds[ambianceSound].play()
 | 
			
		||||
    if randomLocation and channel:
 | 
			
		||||
        left_volume = random.random()
 | 
			
		||||
        right_volume = random.random()
 | 
			
		||||
        channel.set_volume(left_volume, right_volume)
 | 
			
		||||
    return channel  # Return the channel object for potential further manipulation
 | 
			
		||||
 | 
			
		||||
def play_random(sounds, soundName, pause = False, interrupt = False):
 | 
			
		||||
    key = []
 | 
			
		||||
    for i in sounds.keys():
 | 
			
		||||
        if re.match("^" + soundName + ".*", i):
 | 
			
		||||
            key.append(i)
 | 
			
		||||
    randomKey = random.choice(key)
 | 
			
		||||
    if interrupt == False:
 | 
			
		||||
        sounds[randomKey].play()
 | 
			
		||||
    else:
 | 
			
		||||
        cut_scene(sounds, randomKey)
 | 
			
		||||
        # Cut scenes override the pause option
 | 
			
		||||
        return
 | 
			
		||||
    if pause == True:
 | 
			
		||||
        time.sleep(sounds[randomKey].get_length())
 | 
			
		||||
    
 | 
			
		||||
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():
 | 
			
		||||
    # 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."]
 | 
			
		||||
    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:
 | 
			
		||||
                speak(' '.join(text[1:]))
 | 
			
		||||
            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:
 | 
			
		||||
                    pyperclip.copy(' '.join(text[1:-1]))
 | 
			
		||||
                    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"]) and (not f.lower().startswith("_"))]
 | 
			
		||||
    # 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:
 | 
			
		||||
                pygame.mixer.stop()
 | 
			
		||||
                i = i + 1
 | 
			
		||||
            if event.key == pygame.K_UP and i > 0:
 | 
			
		||||
                pygame.mixer.stop()
 | 
			
		||||
                i = i - 1
 | 
			
		||||
            if event.key == pygame.K_RETURN:
 | 
			
		||||
                try:
 | 
			
		||||
                    soundName = soundFiles[i][:-4]
 | 
			
		||||
                    pygame.mixer.stop()
 | 
			
		||||
                    sounds[soundName].play()
 | 
			
		||||
                    continue
 | 
			
		||||
                except:
 | 
			
		||||
                    j = -1
 | 
			
		||||
                    speak("Could not play sound.")
 | 
			
		||||
                    continue
 | 
			
		||||
        event = pygame.event.clear()
 | 
			
		||||
        time.sleep(0.001)
 | 
			
		||||
 | 
			
		||||
def game_menu(sounds, *options):
 | 
			
		||||
    loop = True
 | 
			
		||||
    pygame.mixer.stop()
 | 
			
		||||
    if pygame.mixer.music.get_busy():
 | 
			
		||||
        pygame.mixer.music.unpause()
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            pygame.mixer.music.load("sounds/music_menu.ogg")
 | 
			
		||||
            pygame.mixer.music.set_volume(0.75)
 | 
			
		||||
            pygame.mixer.music.play(-1)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
    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
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_UP and i > 0:
 | 
			
		||||
                i = i - 1
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_HOME and i != 0:
 | 
			
		||||
                i = 0
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_END and i != len(options) - 1:
 | 
			
		||||
                i = len(options) -1
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_RETURN:
 | 
			
		||||
                try:
 | 
			
		||||
                    j = -1
 | 
			
		||||
                    try:
 | 
			
		||||
                        sounds['menu-select'].play()
 | 
			
		||||
                        time.sleep(sounds['menu-select'].get_length())
 | 
			
		||||
                    except:
 | 
			
		||||
                        pass
 | 
			
		||||
                    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()
 | 
			
		||||
    webbrowser.open('https://ko-fi.com/stormux')
 | 
			
		||||
from .libstormgames import *
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										511
									
								
								libstormgames.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										511
									
								
								libstormgames.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,511 @@
 | 
			
		||||
#!/bin/python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
"""Standard initializations and functions shared by all games."""
 | 
			
		||||
 | 
			
		||||
from sys import exit
 | 
			
		||||
import configparser
 | 
			
		||||
import os
 | 
			
		||||
from os import listdir
 | 
			
		||||
from os.path import isfile, join
 | 
			
		||||
from inspect import isfunction
 | 
			
		||||
from xdg import BaseDirectory
 | 
			
		||||
from setproctitle import setproctitle
 | 
			
		||||
import pygame
 | 
			
		||||
import pyperclip
 | 
			
		||||
import random
 | 
			
		||||
import re
 | 
			
		||||
import requests
 | 
			
		||||
import textwrap
 | 
			
		||||
import webbrowser
 | 
			
		||||
# Global variable for speech provider
 | 
			
		||||
try:
 | 
			
		||||
    import speechd
 | 
			
		||||
    spd = speechd.Client()
 | 
			
		||||
    speechProvider = "speechd"
 | 
			
		||||
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()
 | 
			
		||||
import math
 | 
			
		||||
import numpy as np
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
localConfig = configparser.ConfigParser()
 | 
			
		||||
globalConfig = configparser.ConfigParser()
 | 
			
		||||
 | 
			
		||||
class scoreboard():
 | 
			
		||||
    'Handles scores and top 10'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, startingScore = 0):
 | 
			
		||||
        read_config()
 | 
			
		||||
        try:
 | 
			
		||||
            localConfig.add_section("scoreboard")
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
        self.score = startingScore
 | 
			
		||||
        self.oldScores = []
 | 
			
		||||
        for i in range(1, 11):
 | 
			
		||||
            try:
 | 
			
		||||
                self.oldScores.insert(i - 1, localConfig.getint("scoreboard", str(i)))
 | 
			
		||||
            except:
 | 
			
		||||
                pass
 | 
			
		||||
                self.oldScores.insert(i - 1, 0)
 | 
			
		||||
        for i in range(1, 11):
 | 
			
		||||
            if self.oldScores[i - 1] == None:
 | 
			
		||||
                self.oldScores[i - 1] = 0
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
        self.Update_Scores()
 | 
			
		||||
        try:
 | 
			
		||||
            write_config()
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    def New_High_Score(self):
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
            if self.score > j: return i + 1
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def Update_Scores(self):
 | 
			
		||||
        # Update the scores
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
            if self.score > j:
 | 
			
		||||
                self.oldScores.insert(i, self.score)
 | 
			
		||||
                break
 | 
			
		||||
        # Only keep the top 10 scores.
 | 
			
		||||
        self.oldScores = self.oldScores[:10]
 | 
			
		||||
        # Update the scoreboard section of the games config file.
 | 
			
		||||
        for i, j in enumerate(self.oldScores):
 | 
			
		||||
                localConfig.set("scoreboard", str(i + 1), str(j))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_config(writeGlobal = False):
 | 
			
		||||
    if writeGlobal == False:
 | 
			
		||||
        with open(gamePath + "/config.ini", 'w') as configfile:
 | 
			
		||||
            localConfig.write(configfile)
 | 
			
		||||
    else:
 | 
			
		||||
        with open(globalPath + "/config.ini", 'w') as configfile:
 | 
			
		||||
            globalConfig.write(configfile)
 | 
			
		||||
 | 
			
		||||
def read_config(readGlobal = False):
 | 
			
		||||
    if readGlobal == False:
 | 
			
		||||
        try:
 | 
			
		||||
            with open(gamePath + "/config.ini", 'r') as configfile:
 | 
			
		||||
                localConfig.read_file(configfile)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            with open(globalPath + "/config.ini", 'r') as configfile:
 | 
			
		||||
                globalConfig.read_file(configfile)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
def speak(text, interupt=True):
 | 
			
		||||
    if speechProvider == "speechd":
 | 
			
		||||
        if interupt: spd.cancel()
 | 
			
		||||
        spd.say(text)
 | 
			
		||||
    else:
 | 
			
		||||
        if speechProvider == "accessible_output2":
 | 
			
		||||
            s.speak(text, interrupt=True)
 | 
			
		||||
    # Display the text on screen
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    font = pygame.font.Font(None, 36)
 | 
			
		||||
    # Wrap the text
 | 
			
		||||
    maxWidth = screen.get_width() - 40  # Leave a 20-pixel margin on each side
 | 
			
		||||
    wrappedText = textwrap.wrap(text, width=maxWidth // font.size('A')[0])
 | 
			
		||||
    # Render each line
 | 
			
		||||
    textSurfaces = [font.render(line, True, (255, 255, 255)) for line in wrappedText]
 | 
			
		||||
    screen.fill((0, 0, 0))  # Clear screen with black
 | 
			
		||||
    # Calculate total height of text block
 | 
			
		||||
    totalHeight = sum(surface.get_height() for surface in textSurfaces)
 | 
			
		||||
    # Start y-position (centered vertically)
 | 
			
		||||
    currentY = (screen.get_height() - totalHeight) // 2
 | 
			
		||||
    # Blit each line of text
 | 
			
		||||
    for surface in textSurfaces:
 | 
			
		||||
        textRect = surface.get_rect(center=(screen.get_width() // 2, currentY + surface.get_height() // 2))
 | 
			
		||||
        screen.blit(surface, textRect)
 | 
			
		||||
        currentY += surface.get_height()
 | 
			
		||||
    pygame.display.flip()
 | 
			
		||||
 | 
			
		||||
def check_for_exit():
 | 
			
		||||
    for event in pygame.event.get():
 | 
			
		||||
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
    pygame.event.pump()
 | 
			
		||||
 | 
			
		||||
def exit_game():
 | 
			
		||||
    if speechProvider == "speechd": spd.close()
 | 
			
		||||
    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"
 | 
			
		||||
    gamePath = globalPath + "/" + str.lower(str.replace(gameTitle, " ", "-"))
 | 
			
		||||
    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
 | 
			
		||||
    setproctitle(str.lower(str.replace(gameTitle, " ", "")))
 | 
			
		||||
    # start pygame
 | 
			
		||||
    pygame.init()
 | 
			
		||||
    # start the display (required by the event loop)
 | 
			
		||||
    pygame.display.set_mode((800, 600))
 | 
			
		||||
    pygame.display.set_caption(gameTitle)
 | 
			
		||||
    # Set 32 channels for sound by default
 | 
			
		||||
    pygame.mixer.pre_init(44100, -16, 2, 1024)
 | 
			
		||||
    pygame.mixer.init()
 | 
			
		||||
    pygame.mixer.set_num_channels(32)
 | 
			
		||||
    # Reserve the cut scene channel
 | 
			
		||||
    pygame.mixer.set_reserved(0)
 | 
			
		||||
    # Load sounds from the sound directory and creates a list like {'bottle': 'bottle.ogg'}
 | 
			
		||||
    try:
 | 
			
		||||
        soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"])]
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        print("No sounds found.")
 | 
			
		||||
        speak("No sounds found.", False)
 | 
			
		||||
    #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
 | 
			
		||||
 | 
			
		||||
def generate_tone(frequency, duration=0.1, sample_rate=44100, volume = 0.2):
 | 
			
		||||
    t = np.linspace(0, duration, int(sample_rate * duration), False)
 | 
			
		||||
    tone = np.sin(2 * np.pi * frequency * t)
 | 
			
		||||
    stereo_tone = np.vstack((tone, tone)).T  # Create a 2D array for stereo
 | 
			
		||||
    stereo_tone = (stereo_tone * 32767).astype(np.int16)
 | 
			
		||||
    stereo_tone = (stereo_tone * 32767 * volume).astype(np.int16)  # Apply volume
 | 
			
		||||
    stereo_tone = np.ascontiguousarray(stereo_tone)  # Ensure C-contiguous array
 | 
			
		||||
    return pygame.sndarray.make_sound(stereo_tone)
 | 
			
		||||
 | 
			
		||||
def x_powerbar():
 | 
			
		||||
    clock = pygame.time.Clock()
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    position = -50  # Start from the leftmost position
 | 
			
		||||
    direction = 1   # Move right initially
 | 
			
		||||
    barHeight = 20
 | 
			
		||||
    while True:
 | 
			
		||||
        frequency = 440  # A4 note
 | 
			
		||||
        leftVolume = (50 - position) / 100
 | 
			
		||||
        rightVolume = (position + 50) / 100
 | 
			
		||||
        tone = generate_tone(frequency)
 | 
			
		||||
        channel = tone.play()
 | 
			
		||||
        channel.set_volume(leftVolume, rightVolume)
 | 
			
		||||
        # Visual representation
 | 
			
		||||
        screen.fill((0, 0, 0))
 | 
			
		||||
        barWidth = screen.get_width() - 40  # Leave 20px margin on each side
 | 
			
		||||
        pygame.draw.rect(screen, (100, 100, 100), (20, screen.get_height() // 2 - barHeight // 2, barWidth, barHeight))
 | 
			
		||||
        markerPos = int(20 + (position + 50) / 100 * barWidth)
 | 
			
		||||
        pygame.draw.rect(screen, (255, 0, 0), (markerPos - 5, screen.get_height() // 2 - barHeight, 10, barHeight * 2))
 | 
			
		||||
        pygame.display.flip()
 | 
			
		||||
        for event in pygame.event.get():
 | 
			
		||||
            check_for_exit()
 | 
			
		||||
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
 | 
			
		||||
                channel.stop()
 | 
			
		||||
                return position  # This will return a value between -50 and 50
 | 
			
		||||
        position += direction
 | 
			
		||||
        if position > 50:
 | 
			
		||||
            position = 50
 | 
			
		||||
            direction = -1
 | 
			
		||||
        elif position < -50:
 | 
			
		||||
            position = -50
 | 
			
		||||
            direction = 1
 | 
			
		||||
        clock.tick(40) # Speed of bar
 | 
			
		||||
 | 
			
		||||
def y_powerbar():
 | 
			
		||||
    clock = pygame.time.Clock()
 | 
			
		||||
    screen = pygame.display.get_surface()
 | 
			
		||||
    power = 0
 | 
			
		||||
    direction = 1  # 1 for increasing, -1 for decreasing
 | 
			
		||||
    barWidth = 20
 | 
			
		||||
    while True:
 | 
			
		||||
        frequency = 220 + (power * 5)  # Adjust these values to change the pitch range
 | 
			
		||||
        tone = generate_tone(frequency)
 | 
			
		||||
        channel = tone.play()
 | 
			
		||||
        # Visual representation
 | 
			
		||||
        screen.fill((0, 0, 0))
 | 
			
		||||
        barHeight = screen.get_height() - 40  # Leave 20px margin on top and bottom
 | 
			
		||||
        pygame.draw.rect(screen, (100, 100, 100), (screen.get_width() // 2 - barWidth // 2, 20, barWidth, barHeight))
 | 
			
		||||
        markerPos = int(20 + (100 - power) / 100 * barHeight)
 | 
			
		||||
        pygame.draw.rect(screen, (255, 0, 0), (screen.get_width() // 2 - barWidth, markerPos - 5, barWidth * 2, 10))
 | 
			
		||||
        pygame.display.flip()
 | 
			
		||||
        for event in pygame.event.get():
 | 
			
		||||
            check_for_exit()
 | 
			
		||||
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
 | 
			
		||||
                channel.stop()
 | 
			
		||||
                return power
 | 
			
		||||
        power += direction
 | 
			
		||||
        if power >= 100 or power <= 0:
 | 
			
		||||
            direction *= -1  # Reverse direction at limits
 | 
			
		||||
        clock.tick(40)
 | 
			
		||||
 | 
			
		||||
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():
 | 
			
		||||
        event = pygame.event.poll()
 | 
			
		||||
        if event.type == pygame.KEYDOWN and event.key in [pygame.K_ESCAPE, pygame.K_RETURN, pygame.K_SPACE]:
 | 
			
		||||
            pygame.mixer.stop()
 | 
			
		||||
        pygame.event.pump()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def calculate_volume_and_pan(player_pos, obj_pos):
 | 
			
		||||
    distance = abs(player_pos - obj_pos)
 | 
			
		||||
    max_distance = 12  # Maximum audible distance
 | 
			
		||||
    if distance > max_distance:
 | 
			
		||||
        return 0, 0, 0  # No sound if out of range
 | 
			
		||||
    # Calculate volume (non-linear scaling for more noticeable changes)
 | 
			
		||||
    volume = ((max_distance - distance) / max_distance) ** 1.5
 | 
			
		||||
    # Determine left/right based on relative position
 | 
			
		||||
    if player_pos < obj_pos:
 | 
			
		||||
        # Object is to the right
 | 
			
		||||
        left = max(0, 1 - (obj_pos - player_pos) / max_distance)
 | 
			
		||||
        right = 1
 | 
			
		||||
    elif player_pos > obj_pos:
 | 
			
		||||
        # Object is to the left
 | 
			
		||||
        left = 1
 | 
			
		||||
        right = max(0, 1 - (player_pos - obj_pos) / max_distance)
 | 
			
		||||
    else:
 | 
			
		||||
        # Player is on the object
 | 
			
		||||
        left = right = 1
 | 
			
		||||
    return volume, left, right
 | 
			
		||||
 | 
			
		||||
def obj_play(sounds, soundName, player_pos, obj_pos):
 | 
			
		||||
    volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
 | 
			
		||||
    if volume == 0:
 | 
			
		||||
        return None  # Don't play if out of range
 | 
			
		||||
    # Play the sound on a new channel
 | 
			
		||||
    x = sounds[soundName].play(-1)
 | 
			
		||||
    # Apply the volume and pan
 | 
			
		||||
    x.set_volume(volume * left, volume * right)
 | 
			
		||||
    return x
 | 
			
		||||
def obj_update(x, player_pos, obj_pos):
 | 
			
		||||
    if x is None:
 | 
			
		||||
        return None
 | 
			
		||||
    volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
 | 
			
		||||
    if volume == 0:
 | 
			
		||||
        x.stop()
 | 
			
		||||
        return None
 | 
			
		||||
    # Apply the volume and pan
 | 
			
		||||
    x.set_volume(volume * left, volume * right)
 | 
			
		||||
    return x
 | 
			
		||||
    
 | 
			
		||||
def obj_stop(x):
 | 
			
		||||
    # Tries to stop a playing object channel
 | 
			
		||||
    try:
 | 
			
		||||
        x.stop()
 | 
			
		||||
        return None
 | 
			
		||||
    except:
 | 
			
		||||
        return x
 | 
			
		||||
 | 
			
		||||
def play_ambiance(sounds, soundNames, probability, randomLocation = False):
 | 
			
		||||
    # Check if any of the sounds in the list is already playing
 | 
			
		||||
    for soundName in soundNames:
 | 
			
		||||
        if pygame.mixer.find_channel(True) and pygame.mixer.find_channel(True).get_busy():
 | 
			
		||||
            return
 | 
			
		||||
    if random.randint(1, 100) > probability:
 | 
			
		||||
        return
 | 
			
		||||
    # Choose a random sound from the list
 | 
			
		||||
    ambianceSound = random.choice(soundNames)
 | 
			
		||||
    channel = sounds[ambianceSound].play()
 | 
			
		||||
    if randomLocation and channel:
 | 
			
		||||
        left_volume = random.random()
 | 
			
		||||
        right_volume = random.random()
 | 
			
		||||
        channel.set_volume(left_volume, right_volume)
 | 
			
		||||
    return channel  # Return the channel object for potential further manipulation
 | 
			
		||||
 | 
			
		||||
def play_random(sounds, soundName, pause = False, interrupt = False):
 | 
			
		||||
    key = []
 | 
			
		||||
    for i in sounds.keys():
 | 
			
		||||
        if re.match("^" + soundName + ".*", i):
 | 
			
		||||
            key.append(i)
 | 
			
		||||
    randomKey = random.choice(key)
 | 
			
		||||
    if interrupt == False:
 | 
			
		||||
        sounds[randomKey].play()
 | 
			
		||||
    else:
 | 
			
		||||
        cut_scene(sounds, randomKey)
 | 
			
		||||
        # Cut scenes override the pause option
 | 
			
		||||
        return
 | 
			
		||||
    if pause == True:
 | 
			
		||||
        time.sleep(sounds[randomKey].get_length())
 | 
			
		||||
    
 | 
			
		||||
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():
 | 
			
		||||
    # 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."]
 | 
			
		||||
    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:
 | 
			
		||||
                speak(' '.join(text[1:]))
 | 
			
		||||
            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:
 | 
			
		||||
                    pyperclip.copy(' '.join(text[1:-1]))
 | 
			
		||||
                    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"]) and (not f.lower().startswith("_"))]
 | 
			
		||||
    # 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:
 | 
			
		||||
                pygame.mixer.stop()
 | 
			
		||||
                i = i + 1
 | 
			
		||||
            if event.key == pygame.K_UP and i > 0:
 | 
			
		||||
                pygame.mixer.stop()
 | 
			
		||||
                i = i - 1
 | 
			
		||||
            if event.key == pygame.K_RETURN:
 | 
			
		||||
                try:
 | 
			
		||||
                    soundName = soundFiles[i][:-4]
 | 
			
		||||
                    pygame.mixer.stop()
 | 
			
		||||
                    sounds[soundName].play()
 | 
			
		||||
                    continue
 | 
			
		||||
                except:
 | 
			
		||||
                    j = -1
 | 
			
		||||
                    speak("Could not play sound.")
 | 
			
		||||
                    continue
 | 
			
		||||
        event = pygame.event.clear()
 | 
			
		||||
        time.sleep(0.001)
 | 
			
		||||
 | 
			
		||||
def game_menu(sounds, *options):
 | 
			
		||||
    loop = True
 | 
			
		||||
    pygame.mixer.stop()
 | 
			
		||||
    if pygame.mixer.music.get_busy():
 | 
			
		||||
        pygame.mixer.music.unpause()
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            pygame.mixer.music.load("sounds/music_menu.ogg")
 | 
			
		||||
            pygame.mixer.music.set_volume(0.75)
 | 
			
		||||
            pygame.mixer.music.play(-1)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
    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
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_UP and i > 0:
 | 
			
		||||
                i = i - 1
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_HOME and i != 0:
 | 
			
		||||
                i = 0
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_END and i != len(options) - 1:
 | 
			
		||||
                i = len(options) -1
 | 
			
		||||
                try:
 | 
			
		||||
                    sounds['menu-move'].play()
 | 
			
		||||
                except:
 | 
			
		||||
                    pass
 | 
			
		||||
                if options[i] != "donate": pygame.mixer.music.unpause()
 | 
			
		||||
            if event.key == pygame.K_RETURN:
 | 
			
		||||
                try:
 | 
			
		||||
                    j = -1
 | 
			
		||||
                    try:
 | 
			
		||||
                        sounds['menu-select'].play()
 | 
			
		||||
                        time.sleep(sounds['menu-select'].get_length())
 | 
			
		||||
                    except:
 | 
			
		||||
                        pass
 | 
			
		||||
                    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()
 | 
			
		||||
    webbrowser.open('https://ko-fi.com/stormux')
 | 
			
		||||
		Reference in New Issue
	
	Block a user