Compare commits
10 Commits
639198e8de
...
401b2a8527
Author | SHA1 | Date | |
---|---|---|---|
|
401b2a8527 | ||
|
428a48678d | ||
|
9a6d6374f9 | ||
|
df386cbbd9 | ||
|
38522aee78 | ||
|
0e9c52f5e1 | ||
|
fabf48ff42 | ||
|
0c73e98876 | ||
|
0ef11785ec | ||
|
58ab5aa854 |
227
__init__.py
227
__init__.py
@ -10,11 +10,12 @@ from os.path import isfile, join
|
|||||||
from inspect import isfunction
|
from inspect import isfunction
|
||||||
from xdg import BaseDirectory
|
from xdg import BaseDirectory
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
import pyglet
|
import pygame
|
||||||
import pyperclip
|
import pyperclip
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
import textwrap
|
||||||
import webbrowser
|
import webbrowser
|
||||||
# Global variable for speech provider
|
# Global variable for speech provider
|
||||||
try:
|
try:
|
||||||
@ -29,6 +30,7 @@ except ImportError:
|
|||||||
print("No other speech providers found.")
|
print("No other speech providers found.")
|
||||||
exit()
|
exit()
|
||||||
import math
|
import math
|
||||||
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
|
|
||||||
localConfig = configparser.ConfigParser()
|
localConfig = configparser.ConfigParser()
|
||||||
@ -114,19 +116,45 @@ def read_config(readGlobal = False):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def speak(text, interupt = True):
|
def speak(text, interupt=True):
|
||||||
if speechProvider == "speechd":
|
if speechProvider == "speechd":
|
||||||
if interupt == True: spd.cancel()
|
if interupt: spd.cancel()
|
||||||
spd.say(text)
|
spd.say(text)
|
||||||
else:
|
else:
|
||||||
if speechProvider == "accessible_output2":
|
if speechProvider == "accessible_output2":
|
||||||
s.speak(text, interrupt=True)
|
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():
|
def exit_game():
|
||||||
if speechProvider == "speechd": spd.close()
|
if speechProvider == "speechd": spd.close()
|
||||||
# Close the pyglet window
|
pygame.mixer.music.stop()
|
||||||
pyglet.app.exit()
|
pygame.quit()
|
||||||
|
exit()
|
||||||
|
|
||||||
def initialize_gui(gameTitle):
|
def initialize_gui(gameTitle):
|
||||||
# Check for, and possibly create, storm-games path
|
# Check for, and possibly create, storm-games path
|
||||||
@ -141,18 +169,101 @@ def initialize_gui(gameTitle):
|
|||||||
global gameName
|
global gameName
|
||||||
gameName = gameTitle
|
gameName = gameTitle
|
||||||
setproctitle(str.lower(str.replace(gameTitle, " ", "")))
|
setproctitle(str.lower(str.replace(gameTitle, " ", "")))
|
||||||
# init pyglet window
|
# start pygame
|
||||||
window = pyglet.window.Window(500, 300, gameTitle)
|
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'}
|
# 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"])]
|
soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"])]
|
||||||
# make a dict with pyglet media {'bottle':<soundobject>}
|
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 = {}
|
soundData = {}
|
||||||
for f in soundFiles:
|
for f in soundFiles:
|
||||||
soundData[f.split('.')[0]] = pyglet.media.load("sounds/" + f, streaming = False)
|
soundData[f.split('.')[0]] = pygame.mixer.Sound("sounds/" + f)
|
||||||
soundData['game-intro'].play()
|
soundData['game-intro'].play()
|
||||||
time.sleep(soundData['game-intro'].duration)
|
time.sleep(soundData['game-intro'].get_length())
|
||||||
return soundData
|
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):
|
def cut_scene(sounds, soundName):
|
||||||
pygame.event.clear()
|
pygame.event.clear()
|
||||||
pygame.mixer.stop()
|
pygame.mixer.stop()
|
||||||
@ -164,45 +275,46 @@ def cut_scene(sounds, soundName):
|
|||||||
pygame.mixer.stop()
|
pygame.mixer.stop()
|
||||||
pygame.event.pump()
|
pygame.event.pump()
|
||||||
|
|
||||||
def obj_play(sounds, soundName, playerPos, objPos):
|
|
||||||
distance = playerPos - objPos
|
|
||||||
if distance > 9 or distance < -9:
|
|
||||||
# The item is out of range, so play it at 0
|
|
||||||
left = 0
|
|
||||||
right = 0
|
|
||||||
elif distance == 0:
|
|
||||||
left = 0.9
|
|
||||||
right = 0.9
|
|
||||||
else:
|
|
||||||
angle = math.radians(distance * 5)
|
|
||||||
left = math.sqrt(2)/2.0 * (math.cos(angle) + math.sin(angle))
|
|
||||||
right = math.sqrt(2)/2.0 * (math.cos(angle) - math.sin(angle))
|
|
||||||
if left < 0: left *= -1
|
|
||||||
if right < 0: right *= -1
|
|
||||||
# x is the channel for the sound
|
|
||||||
x = sounds[soundName].play(-1)
|
|
||||||
# Apply the position information to the channel
|
|
||||||
x.set_volume(left, right)
|
|
||||||
# return the channel so that it can be used in the update and stop functions.
|
|
||||||
return x
|
|
||||||
|
|
||||||
def obj_update(x, playerPos, objPos):
|
def calculate_volume_and_pan(player_pos, obj_pos):
|
||||||
distance = playerPos - objPos
|
distance = abs(player_pos - obj_pos)
|
||||||
if distance > 9 or distance < -9:
|
max_distance = 12 # Maximum audible distance
|
||||||
left = 0
|
if distance > max_distance:
|
||||||
right = 0
|
return 0, 0, 0 # No sound if out of range
|
||||||
elif distance == 0:
|
# Calculate volume (non-linear scaling for more noticeable changes)
|
||||||
left = 0.9
|
volume = ((max_distance - distance) / max_distance) ** 1.5
|
||||||
right = 0.9
|
# 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:
|
else:
|
||||||
angle = math.radians(distance * 5)
|
# Player is on the object
|
||||||
left = math.sqrt(2)/2.0 * (math.cos(angle) + math.sin(angle))
|
left = right = 1
|
||||||
right = math.sqrt(2)/2.0 * (math.cos(angle) - math.sin(angle))
|
return volume, left, right
|
||||||
if left < 0: left *= -1
|
|
||||||
if right < 0: right *= -1
|
def obj_play(sounds, soundName, player_pos, obj_pos):
|
||||||
# Apply the position information to the channel
|
volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
|
||||||
x.set_volume(left, right)
|
if volume == 0:
|
||||||
# return the channel
|
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
|
return x
|
||||||
|
|
||||||
def obj_stop(x):
|
def obj_stop(x):
|
||||||
@ -213,6 +325,22 @@ def obj_stop(x):
|
|||||||
except:
|
except:
|
||||||
return x
|
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):
|
def play_random(sounds, soundName, pause = False, interrupt = False):
|
||||||
key = []
|
key = []
|
||||||
for i in sounds.keys():
|
for i in sounds.keys():
|
||||||
@ -282,7 +410,7 @@ def learn_sounds(sounds):
|
|||||||
loop = True
|
loop = True
|
||||||
pygame.mixer.music.pause()
|
pygame.mixer.music.pause()
|
||||||
i = 0
|
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"])]
|
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 keeps track of last spoken index so it isn't voiced on key up.
|
||||||
j = -1
|
j = -1
|
||||||
while loop == True:
|
while loop == True:
|
||||||
@ -317,9 +445,12 @@ def game_menu(sounds, *options):
|
|||||||
if pygame.mixer.music.get_busy():
|
if pygame.mixer.music.get_busy():
|
||||||
pygame.mixer.music.unpause()
|
pygame.mixer.music.unpause()
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
pygame.mixer.music.load("sounds/music_menu.ogg")
|
pygame.mixer.music.load("sounds/music_menu.ogg")
|
||||||
pygame.mixer.music.set_volume(0.75)
|
pygame.mixer.music.set_volume(0.75)
|
||||||
pygame.mixer.music.play(-1)
|
pygame.mixer.music.play(-1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
i = 0
|
i = 0
|
||||||
# j keeps track of last spoken index so it isn't voiced on key up.
|
# j keeps track of last spoken index so it isn't voiced on key up.
|
||||||
j = -1
|
j = -1
|
||||||
|
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
pygame>=2.0.0
|
||||||
|
pyperclip>=1.8.0
|
||||||
|
requests>=2.25.0
|
||||||
|
pyxdg>=0.27
|
||||||
|
setproctitle>=1.2.0
|
||||||
|
numpy>=1.19.0
|
||||||
|
accessible-output2>=0.14
|
2
requirements_linux.txt
Normal file
2
requirements_linux.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-r requirements.txt
|
||||||
|
python-speechd>=0.11.1
|
Loading…
Reference in New Issue
Block a user