Compare commits
	
		
			3 Commits
		
	
	
		
			fe772cbb1e
			...
			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