Compare commits
54 Commits
5b2c724882
...
master
Author | SHA1 | Date | |
---|---|---|---|
3fde1996ca | |||
eef3a8756d | |||
8675caaca6 | |||
9fd2bc03df | |||
680a2cab75 | |||
9669e6185d | |||
8c49c62f66 | |||
30e25999c4 | |||
f765bb3dcb | |||
86e8c51494 | |||
f3751def40 | |||
4ba12d5078 | |||
e5c17b60e7 | |||
d7a01340b7 | |||
ad97725634 | |||
2def4f846a | |||
d4ff6d0786 | |||
7c49424350 | |||
1c467371a8 | |||
8a34ed7c01 | |||
b6432e233a | |||
4249a7c3f1 | |||
bcf933992e | |||
187baf6f3e | |||
e974687da3 | |||
bd2931ebe8 | |||
60757a23ff | |||
6a0201da85 | |||
06755ee937 | |||
83a7e98a7f | |||
41ab0406f8 | |||
4904ed2184 | |||
63a87c12dd | |||
2b2c2d946a | |||
0f69120213 | |||
35ac1cff51 | |||
38a5812839 | |||
f6e9869282 | |||
afd228d81a | |||
6debf73267 | |||
1fef87e1c3 | |||
e9df84897a | |||
07be003a54 | |||
c547f76b35 | |||
aa7bcccada | |||
e6cb2bd4c8 | |||
d932538b41 | |||
526b51b754 | |||
b8776a6f7f | |||
9b752bde21 | |||
8e9e96d1bb | |||
5d3fc6ca49 | |||
3fa4e2190e | |||
b65f112b36 |
@@ -1,5 +1,5 @@
|
||||
|
||||

|
||||

|
||||
|
||||
[](LICENSE)
|
||||
|
||||
@@ -13,7 +13,7 @@ There are currently no releases of the game. To install the current version on G
|
||||
## Installing From Source
|
||||
|
||||
Requirements:
|
||||
- Python 3.10 or greater
|
||||
- Python 3.7
|
||||
- Pip
|
||||
- Git
|
||||
|
||||
|
@@ -4,7 +4,7 @@ import pocket_friends
|
||||
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
PyInstaller.__main__.run([
|
||||
PyInstaller.__main__.preprocess([
|
||||
'{0}/pocket_friends/__main__.py'.format(script_dir),
|
||||
'--clean',
|
||||
'--noconsole',
|
||||
|
@@ -1,4 +1,4 @@
|
||||
"""Pocket Friends is a game where you raise your own little pocket friend! These pocket friends, called bloops,
|
||||
are great little companions to have! You can feed them, play with them, and watch them grow up!"""
|
||||
|
||||
__version__ = 'dev_0.0.3'
|
||||
__version__ = 'dev_0.1.0'
|
||||
|
@@ -5,19 +5,23 @@ import os
|
||||
import pygame
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pocket_friends.game_files.game as game
|
||||
import pocket_friends.game as game
|
||||
|
||||
if __name__ == '__main__':
|
||||
enable_dev = False
|
||||
resolution = 240
|
||||
|
||||
# enable dev mode if --dev argument is passed
|
||||
if len(sys.argv) > 0:
|
||||
for args in sys.argv:
|
||||
if args == '--delete-save':
|
||||
for arg in sys.argv:
|
||||
if arg == '--delete-save':
|
||||
save_dir = os.path.join(Path.home(), '.pocket_friends')
|
||||
os.remove(save_dir + '/save.json')
|
||||
if '--res=' in arg:
|
||||
resolution = int(arg.split('=')[1])
|
||||
if arg == '--dev':
|
||||
enable_dev = True
|
||||
|
||||
game.main()
|
||||
game.start_game(resolution=resolution, dev=enable_dev)
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
30
pocket_friends/_development/_dev.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pygame
|
||||
|
||||
|
||||
def reboot_system():
|
||||
os.system('sudo reboot')
|
||||
|
||||
|
||||
def shutdown_system():
|
||||
os.system('sudo shutdown now')
|
||||
|
||||
|
||||
def update():
|
||||
pygame.quit()
|
||||
os.system('bash ~/update.sh')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def restart_app():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def show_black():
|
||||
return 'show_black'
|
||||
|
||||
|
||||
def quit():
|
||||
sys.exit(0)
|
34
pocket_friends/_development/_sprites.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import pygame
|
||||
|
||||
|
||||
class FunctionSelector:
|
||||
|
||||
def __init__(self, resources_dir, game_res, functions):
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
self.functions = functions
|
||||
self.max_lines = 6 # Max number of lines to be shown on screen at a time.
|
||||
self.offset = 0
|
||||
self.game_res = game_res
|
||||
|
||||
self.selected = 0
|
||||
self.max_index = len(self.functions) - 1
|
||||
self.selector = pygame.image.load(resources_dir + '/images/dev_menu/selector.png').convert_alpha()
|
||||
|
||||
def draw(self, surface):
|
||||
for i in range(self.max_index + 1):
|
||||
text = self.font.render(self.functions[i], False, (0, 0, 0))
|
||||
surface.blit(text, (8, (-3 + (i * 6))))
|
||||
surface.blit(self.selector, (0, (self.selected * 6)))
|
||||
|
||||
def scroll_down(self):
|
||||
self.selected += 1
|
||||
if self.selected > self.max_index:
|
||||
self.selected = self.max_index
|
||||
|
||||
def scroll_up(self):
|
||||
self.selected -= 1
|
||||
if self.selected < 0:
|
||||
self.selected = 0
|
||||
|
||||
def get_function(self):
|
||||
return self.functions[self.selected]
|
33
pocket_friends/_development/surfaces/_black_screen.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import pygame
|
||||
from pocket_friends.elements import surface
|
||||
|
||||
|
||||
class Surface(surface.GameSurface):
|
||||
def __init__(self, game_res, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
self.game_fps = game_fps
|
||||
self.frames = -3 * game_fps
|
||||
self.delay = 1
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
self.title = pygame.image.load(resources_dir + '/images/debug/invalid.png').convert_alpha()
|
||||
self.next_surface = '_dev_menu'
|
||||
|
||||
def update(self):
|
||||
self.preprocess()
|
||||
self.fill((0, 0, 0))
|
||||
|
||||
text_1 = self.font.render('Press B', False, (64, 64, 64))
|
||||
text_2 = self.font.render('to return', False, (64, 64, 64))
|
||||
|
||||
if self.frames < 0:
|
||||
self.blit(text_1, (3, 20))
|
||||
self.blit(text_2, (3, 40))
|
||||
|
||||
self.frames += 1
|
||||
if self.frames > self.game_fps:
|
||||
self.frames = 0
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_b:
|
||||
self.running = False
|
58
pocket_friends/_development/surfaces/_dev_menu.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import pygame
|
||||
from pocket_friends.elements import surface
|
||||
from pocket_friends._development._sprites import FunctionSelector
|
||||
import pocket_friends._development._dev as dev
|
||||
|
||||
dev_functions = {
|
||||
'Restart': 'reboot_system',
|
||||
'Shutdown': 'shutdown_system',
|
||||
'Update': 'update',
|
||||
'Re-open App': 'restart_app',
|
||||
'Black screen': 'show_black',
|
||||
'!!! Close App': 'quit'
|
||||
}
|
||||
|
||||
|
||||
class Surface(surface.GameSurface):
|
||||
def __init__(self, game_res, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
self.frames = 1
|
||||
self.game_fps = game_fps
|
||||
self.delay = 1
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
self.background = pygame.image.load(self.resource_dir + '/images/dev_menu/dev_bg.png').convert_alpha()
|
||||
|
||||
functions = []
|
||||
for key in dev_functions.keys():
|
||||
functions.append(key)
|
||||
|
||||
self.function_selector = FunctionSelector(resources_dir, game_res, functions)
|
||||
|
||||
def execute(self):
|
||||
|
||||
executing_function = getattr(dev, dev_functions.get(self.function_selector.get_function()))
|
||||
output = executing_function()
|
||||
if output == 'show_black':
|
||||
self.next_surface = '_black_screen'
|
||||
self.running = False
|
||||
|
||||
def update(self):
|
||||
self.preprocess()
|
||||
|
||||
text = self.font.render('f: {0}'.format(self.frames), False, (128, 128, 128))
|
||||
self.blit(text, (3, 68))
|
||||
self.function_selector.draw(self)
|
||||
|
||||
self.frames += 1
|
||||
self.frames %= self.game_fps
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_UP:
|
||||
self.function_selector.scroll_up()
|
||||
if event.key == pygame.K_DOWN:
|
||||
self.function_selector.scroll_down()
|
||||
if event.key == pygame.K_a:
|
||||
self.execute()
|
||||
if event.key == pygame.K_b:
|
||||
self.running = False
|
28
pocket_friends/_development/surfaces/_error_screen.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import pygame
|
||||
from pocket_friends.elements import surface
|
||||
|
||||
|
||||
class Surface(surface.GameSurface):
|
||||
def __init__(self, game_res, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
self.frames = 1
|
||||
self.game_fps = game_fps
|
||||
self.delay = 1
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
self.title = pygame.image.load(resources_dir + '/images/debug/invalid.png').convert_alpha()
|
||||
self.next_surface = 'title'
|
||||
|
||||
def update(self):
|
||||
self.preprocess()
|
||||
|
||||
self.blit(self.title, (0, -4))
|
||||
text = self.font.render('Frames: {0}'.format(self.frames), False, (64, 64, 64))
|
||||
self.blit(text, (3, 68))
|
||||
|
||||
self.frames += 1
|
||||
self.frames %= self.game_fps
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_b:
|
||||
self.running = False
|
@@ -1,50 +0,0 @@
|
||||
"""
|
||||
Module to test the GPIO input on the Raspberry Pi.
|
||||
"""
|
||||
from collections import deque
|
||||
from pocket_friends.game_files.io.gpio_handler import Constants, GPIOHandler
|
||||
|
||||
|
||||
def button_test():
|
||||
"""
|
||||
GPIO button test. Checks for a GPIO input and prints it out, simple as that.
|
||||
"""
|
||||
running = True
|
||||
|
||||
# Exit code used to quit the button test.
|
||||
exit_code = deque()
|
||||
for button in ['j_d', 'j_d', 'j_u', 'j_u', 'j_d', 'j_d', 'j_u', 'j_u', 'a', 'a', 'b']:
|
||||
exit_code.append(button)
|
||||
|
||||
# Input log to check for quitting out of the button test.
|
||||
input_log = deque()
|
||||
|
||||
GPIOHandler.setup()
|
||||
|
||||
def log(pressed_button):
|
||||
"""
|
||||
Logs the pressed button into the input log.
|
||||
:param pressed_button:
|
||||
"""
|
||||
input_log.append(pressed_button)
|
||||
if len(input_log) > len(exit_code): # Don't let the input log exceed the length of the exit code.
|
||||
input_log.popleft()
|
||||
|
||||
def check_exit():
|
||||
"""
|
||||
Check if the input log and the exit code are the same. If they are, quit the button test.
|
||||
"""
|
||||
nonlocal running
|
||||
|
||||
if exit_code == input_log:
|
||||
running = False
|
||||
|
||||
while running:
|
||||
for button in Constants.buttons:
|
||||
code = Constants.buttons.get(button)
|
||||
if GPIOHandler.get_press(code): # If a button is pressed, print it out and do a quit check.
|
||||
print('event: {0}'.format(button))
|
||||
log(button)
|
||||
check_exit()
|
||||
|
||||
GPIOHandler.teardown()
|
@@ -1,152 +0,0 @@
|
||||
"""
|
||||
Development menu for the hardware on Raspberry Pi. NOTE: THIS DOES NOTHING ON A COMPUTER!
|
||||
"""
|
||||
import pocket_friends.game_files.game
|
||||
import importlib.util
|
||||
import os
|
||||
import pygame
|
||||
import time
|
||||
from .button_test import button_test
|
||||
from .menus import Menu
|
||||
from pocket_friends.game_files.io.gpio_handler import GPIOHandler, Constants
|
||||
|
||||
try:
|
||||
importlib.util.find_spec('RPi.GPIO')
|
||||
import RPi.GPIO as GPIO
|
||||
except ImportError:
|
||||
import pocket_friends.game_files.io.fake_gpio as GPIO
|
||||
|
||||
# Global variable to keep track of the current menu.
|
||||
menu = 'main'
|
||||
|
||||
|
||||
def run_button_test():
|
||||
"""
|
||||
Runs the GPIO button test.
|
||||
"""
|
||||
GPIOHandler.teardown()
|
||||
button_test()
|
||||
GPIOHandler.setup()
|
||||
|
||||
|
||||
def clear_screen():
|
||||
"""
|
||||
Clears the screen.
|
||||
"""
|
||||
print("\n" * 20)
|
||||
|
||||
|
||||
def start_game():
|
||||
"""
|
||||
Cleans the GPIO and starts the hardware.
|
||||
"""
|
||||
GPIOHandler.teardown()
|
||||
pocket_friends.game_files.game.main()
|
||||
pygame.quit()
|
||||
GPIOHandler.setup()
|
||||
|
||||
|
||||
def quit_menu():
|
||||
"""
|
||||
Quits the menu.
|
||||
"""
|
||||
exit(0)
|
||||
|
||||
|
||||
def quit_with_error():
|
||||
"""
|
||||
Quits the menu with error code 3.
|
||||
"""
|
||||
exit(3)
|
||||
|
||||
|
||||
def change_menu(new_menu):
|
||||
"""
|
||||
Changes the global menu variable for the dev menu
|
||||
:param new_menu: the menu to change to
|
||||
"""
|
||||
global menu
|
||||
menu = new_menu
|
||||
clear_screen()
|
||||
print('...')
|
||||
time.sleep(0.75)
|
||||
|
||||
|
||||
def shutdown():
|
||||
"""
|
||||
Shuts down the linux system.
|
||||
"""
|
||||
os.system('sudo shutdown now')
|
||||
|
||||
|
||||
def restart():
|
||||
"""
|
||||
Restarts the linux system.
|
||||
"""
|
||||
os.system('sudo reboot')
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Starts the dev menu.
|
||||
"""
|
||||
|
||||
# The following defines all of the options in the various different menus.
|
||||
|
||||
main_menu = Menu('Pocket Friends Dev Menu')
|
||||
main_menu.add_option(Menu.Option('Start Game', start_game))
|
||||
main_menu.add_option(Menu.Option('Button Test', run_button_test))
|
||||
main_menu.add_option(Menu.Option('Restart Dev Menu', quit_with_error))
|
||||
main_menu.add_option(Menu.Option('Shutdown Pi', change_menu, 'shutdown'))
|
||||
main_menu.add_option(Menu.Option('Restart Pi', change_menu, 'restart'))
|
||||
main_menu.add_option(Menu.Option('Quit Dev Menu', change_menu, 'quit'))
|
||||
|
||||
shutdown_confirm = Menu('Are you sure you want to shutdown?')
|
||||
shutdown_confirm.add_option(Menu.Option('No', change_menu, 'main'))
|
||||
shutdown_confirm.add_option(Menu.Option('Yes', shutdown))
|
||||
|
||||
restart_confirm = Menu('Are you sure you want to restart?')
|
||||
restart_confirm.add_option(Menu.Option('No', change_menu, 'main'))
|
||||
restart_confirm.add_option(Menu.Option('Yes', restart))
|
||||
|
||||
quit_confirm = Menu('Are you sure you want to exit?')
|
||||
quit_confirm.add_option(Menu.Option('No', change_menu, 'main'))
|
||||
quit_confirm.add_option(Menu.Option('Yes', quit_menu))
|
||||
|
||||
GPIOHandler.setup()
|
||||
|
||||
def menu_handler(current_menu):
|
||||
"""
|
||||
Draws the menu and handles the GPIO inputs
|
||||
:param current_menu: the current menu being drawn on the screen
|
||||
"""
|
||||
current_menu.draw_menu()
|
||||
|
||||
while True: # Main GPIO input loop
|
||||
|
||||
# Limits how often the program checks for a GPIO input. Eases CPU usage.
|
||||
time.sleep(0.125)
|
||||
|
||||
if GPIOHandler.get_press(Constants.buttons.get('j_d')):
|
||||
current_menu.select_next()
|
||||
break
|
||||
if GPIOHandler.get_press(Constants.buttons.get('j_u')):
|
||||
current_menu.select_prev()
|
||||
break
|
||||
if GPIOHandler.get_press(Constants.buttons.get('a')):
|
||||
current_menu.run_selection()
|
||||
break
|
||||
|
||||
while True: # Loop for drawing the menus.
|
||||
|
||||
while menu == 'main':
|
||||
menu_handler(main_menu)
|
||||
|
||||
while menu == 'shutdown':
|
||||
menu_handler(shutdown_confirm)
|
||||
|
||||
while menu == 'restart':
|
||||
menu_handler(restart_confirm)
|
||||
|
||||
while menu == 'quit':
|
||||
menu_handler(quit_confirm)
|
@@ -1,120 +0,0 @@
|
||||
"""
|
||||
Menu class to help with drawing menus on screen
|
||||
"""
|
||||
|
||||
|
||||
class Menu:
|
||||
"""
|
||||
Menu class. Creates a menu with text to display and options
|
||||
"""
|
||||
|
||||
def __init__(self, menu_text=''):
|
||||
self._menu_text = menu_text
|
||||
self._options = []
|
||||
self._selection = 0
|
||||
|
||||
def add_option(self, option):
|
||||
"""
|
||||
Adds an option to the menu. Only allows instances of Menu.Option
|
||||
:param option:
|
||||
"""
|
||||
if not isinstance(option, Menu.Option):
|
||||
raise TypeError('option must be an instance of Menu.Option')
|
||||
else:
|
||||
self._options.append(option)
|
||||
|
||||
def get_option(self, index):
|
||||
"""
|
||||
Gets an option object given the index of the option. Raises IndexError if given
|
||||
index is out of bounds.
|
||||
:param index: the index of the option
|
||||
:return: the option object
|
||||
"""
|
||||
try:
|
||||
return self._options[index]
|
||||
except IndexError as ex:
|
||||
raise IndexError('option index out of range') from ex
|
||||
|
||||
def select_next(self):
|
||||
"""
|
||||
Selects the next option in the list. Wraps around to the first option if
|
||||
the last option is currently selected.
|
||||
"""
|
||||
self._selection += 1
|
||||
if self._selection >= len(self._options):
|
||||
self._selection = 0
|
||||
|
||||
def select_prev(self):
|
||||
"""
|
||||
Selects the previous option in the list. Wraps around to the last option if
|
||||
the first option is currently selected.
|
||||
"""
|
||||
self._selection -= 1
|
||||
if self._selection < 0:
|
||||
if len(self._options) > 0:
|
||||
self._selection = len(self._options) - 1
|
||||
else:
|
||||
self._selection = 0
|
||||
|
||||
def run_selection(self, *args, **kwargs):
|
||||
"""
|
||||
Runs the function that the currently selected option object points to.
|
||||
:param args: arguments to be passed to the function
|
||||
:param kwargs: keyword arguments to be passed to the function
|
||||
"""
|
||||
try:
|
||||
return self._options[self._selection].run(*args, **kwargs)
|
||||
except IndexError as ex:
|
||||
raise Exception('menu has no options, cannot run a non-existent option') from ex
|
||||
|
||||
def draw_menu(self):
|
||||
"""
|
||||
Draws the menu on screen with a leading 20 blank lines.
|
||||
"""
|
||||
print('\n' * 20)
|
||||
|
||||
print(self._menu_text + '\n')
|
||||
|
||||
for option in self._options:
|
||||
selection_char = '>'
|
||||
|
||||
if self._options.index(option) != self._selection:
|
||||
selection_char = ' '
|
||||
|
||||
print('{0} {1}'.format(selection_char, option.get_text()))
|
||||
|
||||
class Option:
|
||||
"""
|
||||
Class that defines options for the Menu class.
|
||||
"""
|
||||
|
||||
def __init__(self, option_text='', function=None, *args, **kwargs):
|
||||
self._option_text = option_text
|
||||
self._function = function
|
||||
self._default_args = args
|
||||
self._default_kwargs = kwargs
|
||||
|
||||
def get_text(self):
|
||||
"""
|
||||
Returns the text to be displayed by the option
|
||||
:return: the option text
|
||||
"""
|
||||
return self._option_text
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
"""
|
||||
Runs the function that the option object points to. Returns None if
|
||||
there is no function or the given function is not valid.
|
||||
:param args: arguments to be passed to the function
|
||||
:param kwargs: keyword arguments to be passed to the function
|
||||
:return: the return value of the function (if any)
|
||||
"""
|
||||
if len(args) == 0:
|
||||
args = self._default_args
|
||||
if len(kwargs) == 0:
|
||||
kwargs = self._default_kwargs
|
||||
|
||||
try:
|
||||
return self._function(*args, **kwargs)
|
||||
except TypeError:
|
||||
return None
|
1
pocket_friends/elements/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Submodule for use in the game. Helps with the drawing of various objects such as sprites and text boxes."""
|
@@ -21,7 +21,6 @@ class SpriteSheet:
|
||||
# Load in whole sprite sheet as one image.
|
||||
sprite_sheet = pygame.image.load(sprite_sheet).convert_alpha()
|
||||
self.images = []
|
||||
print(type(self.images))
|
||||
|
||||
# Get the sprite sheet json file.
|
||||
with open(texture_json, 'r') as json_file:
|
||||
@@ -97,7 +96,6 @@ class SelectionEgg(pygame.sprite.Sprite):
|
||||
|
||||
# Get the rectangle from the first image in the list
|
||||
self.rect = self._images[0].get_rect()
|
||||
print(type(self.rect))
|
||||
self._index = 0
|
||||
self.image = self._images[self._index]
|
||||
|
||||
@@ -115,9 +113,14 @@ class InfoText:
|
||||
Class for drawing large amounts of text on the screen at a time
|
||||
"""
|
||||
|
||||
def __init__(self, resources_dir, game_res, text='Lorem ipsum dolor sit amet, consectetur adipiscing elit. '
|
||||
'Nam commodo tempor aliquet. Suspendisse placerat accumsan'
|
||||
' neque, nec volutpat nunc porta ut.'):
|
||||
def __init__(self, resources_dir, game_res, text='Test text.'):
|
||||
"""
|
||||
Creates an InfoText object to be used on a surface.
|
||||
Args:
|
||||
resources_dir (str): The full path of the game's resources directory
|
||||
game_res (int): The internal resolution of the game. Used for correct scaling.
|
||||
text (:obj:`str`, optional): The given text to render. Defaults to "Test text."'
|
||||
"""
|
||||
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
self.text = [] # Text broken up into a list according to how it will fit on screen.
|
||||
@@ -190,8 +193,9 @@ class InfoText:
|
||||
|
||||
def draw(self, surface):
|
||||
"""
|
||||
Draws the text on a given surface.
|
||||
:param surface: The surface for the text to be drawn on.
|
||||
Draw the text on a given surface.
|
||||
Args:
|
||||
surface (:obj:`pygame.Surface`): The surface to draw the text on
|
||||
"""
|
||||
# Constants to help draw the text
|
||||
line_separation = 7
|
||||
@@ -266,7 +270,8 @@ class EggInfo:
|
||||
def draw(self, surface):
|
||||
"""
|
||||
Draw the info icons on a given surface.
|
||||
:param surface: the surface to draw the icons on.
|
||||
Args:
|
||||
surface (:obj:`pygame.Surface`): The surface to draw the text on
|
||||
"""
|
||||
# Blit the info onto the given surface.
|
||||
surface.blit(self.surface, (self.x, self.y))
|
58
pocket_friends/elements/surface.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""Module to aid in the drawing of surfaces to make switching from one screen to another easier."""
|
||||
import pygame
|
||||
from ..io.input_handler import InputHandler
|
||||
|
||||
|
||||
class GameSurface(pygame.Surface):
|
||||
"""
|
||||
Class to be used as the backbone of all game surfaces.
|
||||
|
||||
The GameSurface class is what the game uses to draw all other elements on top of. It controls a number of game
|
||||
operations, including input handling, frame rate limiting, and surface switching. It is a child class of Pygame's
|
||||
Surface class and utilizes Pygame's sprite handling along with the ability to blit certain things on the screen
|
||||
in order to draw elements easily.
|
||||
|
||||
Attributes:
|
||||
running: Boolean to tell whether the surface is running or not.
|
||||
next_surface: String that represents the next surface to be switched to after halting.
|
||||
resource_dir: String that contains the path of the game's main resource directory.
|
||||
game_fps: An integer value of how many frames per second the game will run at.
|
||||
additional_args: Dictionary of additional arguments to send to the next surface after halting.
|
||||
background: A Pygame surface that will be drawn behind all other elements.
|
||||
"""
|
||||
def __init__(self, game_res: tuple, resources_dir: str, game_fps: int):
|
||||
"""
|
||||
Create a GameSurface object.
|
||||
Args:
|
||||
game_res: The internal resolution of the surface.
|
||||
resources_dir: The path of the game's main resource directory.
|
||||
game_fps: How many frames per second the game will run at.
|
||||
"""
|
||||
super().__init__(game_res, pygame.SRCALPHA)
|
||||
self.running = True # Surfaces should be running by default
|
||||
self.next_surface = None
|
||||
self.resource_dir = resources_dir
|
||||
self.game_fps = game_fps
|
||||
self.additional_args = {}
|
||||
self.dev_override = False
|
||||
|
||||
self.background = pygame.image.load(self.resource_dir + '/images/bg.png').convert_alpha()
|
||||
|
||||
self._clock = pygame.time.Clock()
|
||||
self._input_handler = InputHandler(self._clock)
|
||||
self._sprites = pygame.sprite.Group()
|
||||
|
||||
def preprocess(self):
|
||||
"""
|
||||
Draw the surface background and advance all the surface's sprites by one frame; Run the input handler.
|
||||
"""
|
||||
self._clock.tick(self.game_fps)
|
||||
|
||||
self.blit(self.background, (0, 0))
|
||||
self._sprites.update()
|
||||
self._sprites.draw(self)
|
||||
|
||||
self._input_handler.update()
|
||||
if self._input_handler.dev_found:
|
||||
self.dev_override = True
|
||||
self.running = False
|
95
pocket_friends/game.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Main module for the game. Runs setting up the Pygame window and handles scene switching."""
|
||||
|
||||
import pygame
|
||||
import os
|
||||
import pocket_friends
|
||||
import importlib
|
||||
|
||||
valid_surfaces = [
|
||||
'_dev_menu',
|
||||
'_black_screen',
|
||||
'_error_screen',
|
||||
'title',
|
||||
'egg_select',
|
||||
'selection_info',
|
||||
]
|
||||
|
||||
# Add all the surface modules to a dictionary for easy switching
|
||||
surface_modules = {}
|
||||
for module in valid_surfaces:
|
||||
if module[0] == '_':
|
||||
surface_modules[module] = importlib.import_module('pocket_friends._development.surfaces.{0}'.format(module))
|
||||
else:
|
||||
surface_modules[module] = importlib.import_module('pocket_friends.surfaces.{0}'.format(module))
|
||||
|
||||
# FPS for the game to run at.
|
||||
game_fps = 16
|
||||
# The internal resolution of the game
|
||||
game_res = 80
|
||||
|
||||
# Get the path for where all game resources are (images, fonts, sounds, etc.)
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
resources_dir = script_dir + '/resources'
|
||||
|
||||
# Makes Pygame draw on the display of the RPi.
|
||||
os.environ['SDL_FBDEV'] = '/dev/fb1'
|
||||
|
||||
|
||||
def start_game(resolution: int, dev: bool):
|
||||
"""
|
||||
Starts the game.
|
||||
|
||||
Args:
|
||||
resolution: Resolution to display the game at.
|
||||
dev: Boolean to enable the developer menu at start or not
|
||||
"""
|
||||
|
||||
pygame.init()
|
||||
|
||||
# Hide the cursor for the Pi display.
|
||||
pygame.mouse.set_visible(False)
|
||||
|
||||
if dev:
|
||||
starting_surface = '_dev_menu'
|
||||
else:
|
||||
starting_surface = 'title'
|
||||
|
||||
window = pygame.display.set_mode((resolution, resolution))
|
||||
surface = surface_modules.get(starting_surface).Surface((game_res, game_res), resources_dir, game_fps)
|
||||
|
||||
# Only really useful for PCs. Does nothing on the Raspberry Pi.
|
||||
pygame.display.set_caption('Pocket Friends {0}'.format(pocket_friends.__version__))
|
||||
|
||||
# Add an icon to the pygame window.
|
||||
icon = pygame.image.load(resources_dir + '/icon/icon.png').convert_alpha()
|
||||
pygame.display.set_icon(icon)
|
||||
|
||||
running = True
|
||||
|
||||
while running:
|
||||
surface.update()
|
||||
|
||||
# The game is only 80x80px, however it is upscaled to whatever the running resolution is.
|
||||
frame = pygame.transform.scale(surface, (resolution, resolution))
|
||||
window.blit(frame, frame.get_rect())
|
||||
|
||||
# When the current surface is not running, check to make sure that the next surface will be valid
|
||||
if not surface.running:
|
||||
# Force the dev menu to appear if the flag has been passed
|
||||
if surface.dev_override:
|
||||
next_surface = '_dev_menu'
|
||||
else:
|
||||
next_surface = surface.next_surface
|
||||
|
||||
# Send to the error screen if the given surface isn't a valid one
|
||||
if next_surface not in valid_surfaces:
|
||||
next_surface = '_error_screen'
|
||||
|
||||
# Get the additional args to pass on from the ending surface to the next one
|
||||
additional_args = surface.additional_args
|
||||
# Create the new surface and pass through the additional argss
|
||||
surface = surface_modules.get(next_surface).Surface((game_res, game_res), resources_dir,
|
||||
game_fps, **additional_args)
|
||||
pygame.display.flip()
|
||||
|
||||
pygame.quit()
|
@@ -1 +0,0 @@
|
||||
"""Sub-module for use in the game. Helps with the drawing of various objects such as sprites and text boxes."""
|
@@ -1,82 +0,0 @@
|
||||
import pygame
|
||||
import os
|
||||
from pathlib import Path
|
||||
import pocket_friends
|
||||
import importlib
|
||||
|
||||
valid_surfaces = [
|
||||
'title',
|
||||
'egg_select',
|
||||
'selection_info',
|
||||
'error_screen'
|
||||
]
|
||||
surface_modules = {}
|
||||
|
||||
for module in valid_surfaces:
|
||||
surface_modules[module] = importlib.import_module('pocket_friends.game_files.surfaces.{0}'.format(module))
|
||||
print('imported ' + module)
|
||||
|
||||
# FPS for the entire game to run at.
|
||||
game_fps = 16
|
||||
# The resolution the game is rendered at.
|
||||
game_res = 80
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
save_dir = os.path.join(Path.home(), '.pocket_friends')
|
||||
resources_dir = script_dir + '/resources'
|
||||
starting_surface = 'title'
|
||||
|
||||
|
||||
# Gets the directory of the script for importing and the save directory
|
||||
|
||||
|
||||
def game():
|
||||
"""
|
||||
Starts the game.
|
||||
"""
|
||||
pygame.init()
|
||||
|
||||
# The game is normally rendered at 80 pixels and upscaled from there. If changing displays, change the
|
||||
# screen_size to reflect what the resolution of the new display is.
|
||||
screen_size = 320
|
||||
|
||||
pygame.mouse.set_visible(False)
|
||||
pygame.display.set_caption('Pocket Friends {0}'.format(pocket_friends.__version__))
|
||||
window = pygame.display.set_mode((screen_size, screen_size))
|
||||
surface = surface_modules.get(starting_surface).Surface((game_res, game_res), resources_dir, game_fps)
|
||||
|
||||
# Add an icon to the pygame window.
|
||||
icon = pygame.image.load(resources_dir + '/icon/icon.png').convert_alpha()
|
||||
pygame.display.set_icon(icon)
|
||||
|
||||
# Default game state when the game first starts.
|
||||
running = True
|
||||
|
||||
# A group of all the sprites on screen. Used to update all sprites at onc
|
||||
all_sprites = pygame.sprite.Group()
|
||||
|
||||
# Time since last input. Used to help regulate double presses of buttons.
|
||||
last_input_tick = 0
|
||||
|
||||
while running:
|
||||
surface.update()
|
||||
frame = pygame.transform.scale(surface, (screen_size, screen_size))
|
||||
window.blit(frame, frame.get_rect())
|
||||
|
||||
if not surface.running:
|
||||
next_surface = surface.next_surface
|
||||
additional_args = surface.additional_args
|
||||
if next_surface not in valid_surfaces:
|
||||
next_surface = 'error_screen'
|
||||
surface = surface_modules.get(next_surface).Surface((game_res, game_res), resources_dir,
|
||||
game_fps, **additional_args)
|
||||
print(surface.name)
|
||||
pygame.display.flip()
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Calls the game() function to start the game.
|
||||
"""
|
||||
game()
|
||||
|
||||
pygame.quit()
|
@@ -1,2 +0,0 @@
|
||||
"""Sub-package for handling all I/O operations including keyboard input (GPIO input when connected to a Raspberry Pi)
|
||||
and save data reading and writing."""
|
@@ -1,75 +0,0 @@
|
||||
"""
|
||||
Dummy module that contains empty functions and variables that exist in RPi.GPIO. This module is to be imported
|
||||
if importing RPi.GPIO fails.
|
||||
"""
|
||||
|
||||
# Constants used by RPi.GPIO
|
||||
BOARD = 0
|
||||
IN = 0
|
||||
FALLING = 0
|
||||
|
||||
|
||||
def setmode(new_mode):
|
||||
"""
|
||||
Fake setmode function.
|
||||
Args:
|
||||
new_mode:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def setup(channel, mode, initial=None, pull_up_down=None):
|
||||
"""
|
||||
Fake setup function.
|
||||
Args:
|
||||
channel:
|
||||
mode:
|
||||
initial:
|
||||
pull_up_down:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def add_event_detect(channel, edge_type, callback=None, bouncetime=0):
|
||||
"""
|
||||
Fake function to add a non-existent event detect.
|
||||
Args:
|
||||
channel:
|
||||
edge_type:
|
||||
callback:
|
||||
bouncetime:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def event_detected(channel):
|
||||
"""
|
||||
Fake function to detect an event. Always returns false.
|
||||
Args:
|
||||
channel:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def cleanup(channel=None):
|
||||
"""
|
||||
Fake cleanup function.
|
||||
Args:
|
||||
channel:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
pass
|
@@ -1,65 +0,0 @@
|
||||
"""Handle inputs from the GPIO pins on the Raspberry Pi and converting them to events to be used in other places (
|
||||
pygame, etc.)"""
|
||||
import importlib.util
|
||||
|
||||
# If the RPi.GPIO module is not found (aka the program is not running on a Pi), import the fake
|
||||
# GPIO module instead to prevent a crash.
|
||||
|
||||
ON_HARDWARE = None # Flag to tell other methods if the program is running on hardware or not
|
||||
|
||||
try:
|
||||
importlib.util.find_spec('RPi.GPIO')
|
||||
import RPi.GPIO as GPIO
|
||||
ON_HARDWARE = True
|
||||
except ImportError:
|
||||
import pocket_friends.game_files.io.fake_gpio as GPIO
|
||||
ON_HARDWARE = False
|
||||
|
||||
# Dictionary of all the buttons used and what their corresponding GPIO codes are
|
||||
BUTTONS = {
|
||||
'a': 31, # A button
|
||||
'b': 29, # B button
|
||||
'j_i': 7, # Joystick in
|
||||
'j_u': 11, # Joystick up
|
||||
'j_d': 15, # Joystick down
|
||||
'j_l': 13, # Joystick left
|
||||
'j_r': 16 # Joystick right
|
||||
}
|
||||
|
||||
|
||||
def setup():
|
||||
"""Prime the GPIO pins for reading the inputs of the buttons."""
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
GPIO.setup(BUTTONS.get('a'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('b'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('j_i'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('j_u'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('j_d'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('j_l'), GPIO.IN)
|
||||
GPIO.setup(BUTTONS.get('j_r'), GPIO.IN)
|
||||
|
||||
GPIO.add_event_detect(BUTTONS.get('a'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('b'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('j_i'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('j_u'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('j_d'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('j_l'), GPIO.FALLING)
|
||||
GPIO.add_event_detect(BUTTONS.get('j_r'), GPIO.FALLING)
|
||||
|
||||
|
||||
def teardown():
|
||||
"""Clean up the GPIO handler."""
|
||||
GPIO.cleanup()
|
||||
|
||||
|
||||
def get_press(button):
|
||||
"""
|
||||
Checks if a given button code has been pressed and returns a bool depending on the state.
|
||||
Args:
|
||||
button: button to be detected
|
||||
|
||||
Returns: True if the button is has been pressed, False otherwise
|
||||
|
||||
"""
|
||||
return GPIO.event_detected(button)
|
@@ -1,41 +0,0 @@
|
||||
import pygame
|
||||
|
||||
from pocket_friends.game_files.io import gpio_handler
|
||||
from pocket_friends.game_files.io.input_handler import InputHandler
|
||||
|
||||
|
||||
class Surface(pygame.Surface):
|
||||
def __init__(self, window_size, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(window_size, pygame.SRCALPHA)
|
||||
self.name = 'error_screen'
|
||||
self.running = True
|
||||
self.next_surface = 'title'
|
||||
self.clock = pygame.time.Clock()
|
||||
self.input_handler = InputHandler(self.clock)
|
||||
self.additional_args = {}
|
||||
|
||||
self.bg = pygame.image.load(resources_dir + '/images/bg.png').convert_alpha()
|
||||
self.title = pygame.image.load(resources_dir + '/images/debug/invalid.png').convert_alpha()
|
||||
self.frames = 1
|
||||
self.game_fps = game_fps
|
||||
self.delay = 1
|
||||
self.font = pygame.font.Font(resources_dir + '/fonts/5Pts5.ttf', 10)
|
||||
|
||||
def update(self):
|
||||
self.clock.tick(self.game_fps)
|
||||
|
||||
self.blit(self.bg, (0, 0))
|
||||
self.blit(self.title, (0, -4))
|
||||
text = self.font.render('Frames: {0}'.format(self.frames), False, (64, 64, 64))
|
||||
self.blit(text, (3, 68))
|
||||
|
||||
self.frames += 1
|
||||
self.frames %= self.game_fps
|
||||
|
||||
self.input_handler.update()
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == gpio_handler.BUTTONS.get('b'):
|
||||
self.running = False
|
||||
print('stop')
|
@@ -1,72 +0,0 @@
|
||||
import pygame
|
||||
from ..elements import sprites
|
||||
import pocket_friends.game_files.io.gpio_handler as gpio_handler
|
||||
from ..io.input_handler import InputHandler
|
||||
|
||||
|
||||
class Surface(pygame.Surface):
|
||||
"""
|
||||
|
||||
"""
|
||||
def __init__(self, window_size, resources_dir, game_fps, **kwargs):
|
||||
"""
|
||||
|
||||
Args:
|
||||
window_size:
|
||||
resources_dir:
|
||||
game_fps:
|
||||
**kwargs:
|
||||
"""
|
||||
super().__init__(window_size, pygame.SRCALPHA)
|
||||
self.name = 'selection_info'
|
||||
self.running = True
|
||||
self.next_surface = None
|
||||
self.resource_dir = resources_dir
|
||||
self.clock = pygame.time.Clock()
|
||||
self.game_fps = game_fps
|
||||
self.input_handler = InputHandler(self.clock)
|
||||
self.additional_args = {}
|
||||
|
||||
self.bg = pygame.image.load(self.resource_dir + '/images/bg.png').convert_alpha()
|
||||
self.sprites = pygame.sprite.Group()
|
||||
|
||||
self.selected_egg = None
|
||||
for key in kwargs.keys():
|
||||
if key == 'selected_egg':
|
||||
self.selected_egg = kwargs.get(key)
|
||||
|
||||
egg = sprites.SelectionEgg(self.selected_egg, resources_dir)
|
||||
egg.rect.x = 8
|
||||
egg.rect.y = 3
|
||||
self.sprites.add(egg)
|
||||
|
||||
self.info_text = sprites.InfoText(resources_dir, window_size[0], egg.description)
|
||||
self.info_icons = sprites.EggInfo(resources_dir, egg.contentedness, egg.metabolism, (32, 4))
|
||||
|
||||
def update(self):
|
||||
self.clock.tick(self.game_fps)
|
||||
|
||||
self.blit(self.bg, (0, 0))
|
||||
self.sprites.update()
|
||||
self.sprites.draw(self)
|
||||
self.info_text.draw(self)
|
||||
self.info_icons.draw(self)
|
||||
|
||||
self.input_handler.update()
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == gpio_handler.BUTTONS.get('j_d'):
|
||||
# Scroll down on the info screen.
|
||||
self.info_text.scroll_down()
|
||||
if event.key == gpio_handler.BUTTONS.get('j_u'):
|
||||
# Scroll up on the info screen.
|
||||
self.info_text.scroll_up()
|
||||
if event.key == gpio_handler.BUTTONS.get('a'):
|
||||
self.running = False
|
||||
self.additional_args = {'selected_color': self.selected_egg}
|
||||
self.next_surface = 'playground'
|
||||
if event.key == gpio_handler.BUTTONS.get('b'):
|
||||
self.running = False
|
||||
self.additional_args = {'selected_color': self.selected_egg}
|
||||
self.next_surface = 'egg_select'
|
@@ -1,28 +0,0 @@
|
||||
import pygame
|
||||
import time
|
||||
|
||||
|
||||
class Surface(pygame.Surface):
|
||||
def __init__(self, window_size, resources_dir, game_fps):
|
||||
super().__init__(window_size, pygame.SRCALPHA)
|
||||
self.name = 'title'
|
||||
self.running = True
|
||||
self.next_surface = None
|
||||
self.clock = pygame.time.Clock()
|
||||
self.additional_args = {}
|
||||
|
||||
self.bg = pygame.image.load(resources_dir + '/images/bg.png').convert_alpha()
|
||||
self.title = pygame.image.load(resources_dir + '/images/title.png').convert_alpha()
|
||||
self.frames = 1
|
||||
self.game_fps = game_fps
|
||||
self.delay = 1
|
||||
|
||||
def update(self):
|
||||
self.clock.tick(self.game_fps)
|
||||
self.blit(self.bg, (0, 0))
|
||||
self.blit(self.title, (0, 0))
|
||||
|
||||
self.frames += 1
|
||||
if self.frames > self.game_fps * self.delay:
|
||||
self.next_surface = 'egg_select'
|
||||
self.running = False
|
2
pocket_friends/io/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Subpackage for handling all I/O operations including keyboard input (GPIO input when connected to a Raspberry Pi)
|
||||
and save data reading and writing."""
|
@@ -1,6 +1,5 @@
|
||||
import pygame
|
||||
import pocket_friends.game_files.io.gpio_handler as gpio_handler
|
||||
|
||||
from collections import deque
|
||||
|
||||
class InputHandler:
|
||||
"""
|
||||
@@ -13,16 +12,24 @@ class InputHandler:
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, pygame_clock):
|
||||
def __init__(self, pygame_clock, tick_check=True):
|
||||
"""
|
||||
Create a InputHandler object using a given Pygame clock.
|
||||
|
||||
Args:
|
||||
pygame_clock (pygame.time.Clock): A pygame clock to use as the clock for input time calculations.
|
||||
pygame_clock (:obj:`pygame.time.Clock`): A pygame clock to use as the clock for input time calculations.
|
||||
tick_check (bool, optional): Bool to ignore inputs that happen to quickly after another. Defaults to True.
|
||||
|
||||
"""
|
||||
self.clock = pygame_clock
|
||||
self.tick_check = tick_check
|
||||
self.last_input_tick = 0
|
||||
self.dev_check = deque()
|
||||
self.dev_code = deque()
|
||||
for button in [pygame.K_DOWN, pygame.K_DOWN, pygame.K_UP, pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT,
|
||||
pygame.K_RIGHT, pygame.K_LEFT, pygame.K_a]:
|
||||
self.dev_code.append(button)
|
||||
self.dev_found = False
|
||||
|
||||
def create_event(self, pressed_button):
|
||||
"""
|
||||
@@ -32,22 +39,18 @@ class InputHandler:
|
||||
|
||||
"""
|
||||
# Register a button click so long as the last button click happened no less than two frames ago
|
||||
if pygame.time.get_ticks() - self.last_input_tick > self.clock.get_time() * 2 or not gpio_handler.ON_HARDWARE:
|
||||
if self.tick_check:
|
||||
if pygame.time.get_ticks() - self.last_input_tick > self.clock.get_time() * 2:
|
||||
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button}))
|
||||
pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button}))
|
||||
self.dev_check.append(pressed_button)
|
||||
if len(self.dev_check) > len(self.dev_code):
|
||||
self.dev_check.popleft()
|
||||
else:
|
||||
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button}))
|
||||
pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button}))
|
||||
self.last_input_tick = pygame.time.get_ticks()
|
||||
|
||||
def handle_gpio(self):
|
||||
"""
|
||||
Handle GPIO events and create events for them.
|
||||
"""
|
||||
for pressed_button in gpio_handler.BUTTONS:
|
||||
code = gpio_handler.BUTTONS.get(pressed_button)
|
||||
|
||||
# Check if a button has been pressed. If it has, create a pygame event for it.
|
||||
if gpio_handler.get_press(code):
|
||||
self.create_event(code)
|
||||
|
||||
def handle_keyboard(self):
|
||||
"""Handle keyboard presses and generate corresponding GPIO codes to create events."""
|
||||
|
||||
@@ -57,25 +60,24 @@ class InputHandler:
|
||||
running = False
|
||||
if keyboard_event.type == pygame.KEYDOWN:
|
||||
if keyboard_event.key == pygame.K_a:
|
||||
self.create_event(gpio_handler.BUTTONS.get('a'))
|
||||
self.create_event(pygame.K_a)
|
||||
if keyboard_event.key == pygame.K_b:
|
||||
self.create_event(gpio_handler.BUTTONS.get('b'))
|
||||
if keyboard_event.key == pygame.K_PERIOD:
|
||||
self.create_event(gpio_handler.BUTTONS.get('j_i'))
|
||||
self.create_event(pygame.K_b)
|
||||
if keyboard_event.key == pygame.K_KP_ENTER:
|
||||
self.create_event(pygame.K_KP_ENTER)
|
||||
if keyboard_event.key == pygame.K_RIGHT:
|
||||
self.create_event(gpio_handler.BUTTONS.get('j_r'))
|
||||
self.create_event(pygame.K_RIGHT)
|
||||
if keyboard_event.key == pygame.K_LEFT:
|
||||
self.create_event(gpio_handler.BUTTONS.get('j_l'))
|
||||
self.create_event(pygame.K_LEFT)
|
||||
if keyboard_event.key == pygame.K_DOWN:
|
||||
self.create_event(gpio_handler.BUTTONS.get('j_d'))
|
||||
self.create_event(pygame.K_DOWN)
|
||||
if keyboard_event.key == pygame.K_UP:
|
||||
self.create_event(gpio_handler.BUTTONS.get('j_u'))
|
||||
self.create_event(pygame.K_UP)
|
||||
if keyboard_event.key == pygame.K_ESCAPE:
|
||||
running = False
|
||||
|
||||
def update(self):
|
||||
"""Run either the GPIO handler or the keyboard handler to check for input and create events."""
|
||||
if gpio_handler.ON_HARDWARE:
|
||||
self.handle_gpio()
|
||||
else:
|
||||
self.handle_keyboard()
|
||||
self.handle_keyboard()
|
||||
if self.dev_code == self.dev_check:
|
||||
self.dev_found = True
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 807 B After Width: | Height: | Size: 807 B |
Before Width: | Height: | Size: 610 B After Width: | Height: | Size: 610 B |
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 820 B |
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
Before Width: | Height: | Size: 736 B After Width: | Height: | Size: 736 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 422 B After Width: | Height: | Size: 422 B |
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 565 B |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 864 B After Width: | Height: | Size: 864 B |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
pocket_friends/resources/images/dev_menu/dev_bg.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
pocket_friends/resources/images/dev_menu/selector.png
Normal file
After Width: | Height: | Size: 839 B |
Before Width: | Height: | Size: 246 B After Width: | Height: | Size: 246 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 188 B After Width: | Height: | Size: 188 B |
Before Width: | Height: | Size: 178 B After Width: | Height: | Size: 178 B |
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 217 B |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B |
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 268 B |
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@@ -1,23 +1,11 @@
|
||||
import pygame
|
||||
from ..elements import sprites
|
||||
import pocket_friends.game_files.io.gpio_handler as gpio_handler
|
||||
from ..io.input_handler import InputHandler
|
||||
from pocket_friends.elements import sprites
|
||||
from pocket_friends.elements import surface
|
||||
|
||||
|
||||
class Surface(pygame.Surface):
|
||||
def __init__(self, window_size, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(window_size, pygame.SRCALPHA)
|
||||
self.name = 'egg_select'
|
||||
self.running = True
|
||||
self.next_surface = None
|
||||
self.resource_dir = resources_dir
|
||||
self.clock = pygame.time.Clock()
|
||||
self.game_fps = game_fps
|
||||
self.input_handler = InputHandler(self.clock)
|
||||
self.additional_args = {}
|
||||
|
||||
self.bg = pygame.image.load(self.resource_dir + '/images/bg.png').convert_alpha()
|
||||
self.sprites = pygame.sprite.Group()
|
||||
class Surface(surface.GameSurface):
|
||||
def __init__(self, game_res, resources_dir, game_fps, **kwargs):
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
|
||||
preselected_color = None
|
||||
for key in kwargs.keys():
|
||||
@@ -60,7 +48,7 @@ class Surface(pygame.Surface):
|
||||
egg.rect.y = y
|
||||
|
||||
# Add the egg to the sprite list.
|
||||
self.sprites.add(egg)
|
||||
self._sprites.add(egg)
|
||||
|
||||
self.selected_egg = 0
|
||||
self.selected_color = ''
|
||||
@@ -117,13 +105,7 @@ class Surface(pygame.Surface):
|
||||
self.selected_egg += self.eggs_per_row
|
||||
|
||||
def update(self):
|
||||
self.clock.tick(self.game_fps)
|
||||
|
||||
self.blit(self.bg, (0, 0))
|
||||
self.sprites.update()
|
||||
self.sprites.draw(self)
|
||||
|
||||
self.input_handler.update()
|
||||
self.preprocess()
|
||||
|
||||
cursor = pygame.image.load(
|
||||
self.resource_dir + '/images/gui/egg_selector.png').convert_alpha()
|
||||
@@ -133,15 +115,15 @@ class Surface(pygame.Surface):
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == gpio_handler.BUTTONS.get('j_r'):
|
||||
if event.key == pygame.K_RIGHT:
|
||||
self.sel_right()
|
||||
if event.key == gpio_handler.BUTTONS.get('j_l'):
|
||||
if event.key == pygame.K_LEFT:
|
||||
self.sel_left()
|
||||
if event.key == gpio_handler.BUTTONS.get('j_d'):
|
||||
if event.key == pygame.K_DOWN:
|
||||
self.sel_down()
|
||||
if event.key == gpio_handler.BUTTONS.get('j_u'):
|
||||
if event.key == pygame.K_UP:
|
||||
self.sel_up()
|
||||
if event.key == gpio_handler.BUTTONS.get('a'):
|
||||
if event.key == pygame.K_a:
|
||||
self.additional_args = {'selected_egg': self.selected_color}
|
||||
self.next_surface = 'selection_info'
|
||||
self.running = False
|
63
pocket_friends/surfaces/selection_info.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Module for selection info. Shows egg stats after an egg has been selected from the starting
|
||||
screen. Contains the surface object to be rendered."""
|
||||
|
||||
import pygame
|
||||
from pocket_friends.elements import sprites
|
||||
from pocket_friends.elements import surface
|
||||
|
||||
|
||||
class Surface(surface.GameSurface):
|
||||
"""
|
||||
Surface object for the selection info screen. Contains the selected egg, egg properties, and description in a
|
||||
scrollable text box. Child class of surface.GameSurface.
|
||||
"""
|
||||
def __init__(self, game_res: tuple, resources_dir: str, game_fps: int, **kwargs: str):
|
||||
"""
|
||||
Create a selection info surface object.
|
||||
|
||||
Args:
|
||||
game_res: The internal resolution of the surface.
|
||||
resources_dir: The path of the game's main resource directory.
|
||||
game_fps: How many frames per second the game will run at.
|
||||
|
||||
Keyword Args:
|
||||
selected_egg: The egg that was selected by the previous screen.
|
||||
"""
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
|
||||
self._selected_egg = None
|
||||
for key in kwargs.keys():
|
||||
if key == 'selected_egg':
|
||||
self._selected_egg = kwargs.get(key)
|
||||
|
||||
egg = sprites.SelectionEgg(self._selected_egg, resources_dir)
|
||||
egg.rect.x = 8
|
||||
egg.rect.y = 3
|
||||
self._sprites.add(egg)
|
||||
|
||||
self._info_text = sprites.InfoText(resources_dir, game_res[0], egg.description)
|
||||
self._info_icons = sprites.EggInfo(resources_dir, egg.contentedness, egg.metabolism, (32, 4))
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Draw objects on the screen and advance the surface logic by one frame; Handle user input.
|
||||
"""
|
||||
self.preprocess()
|
||||
|
||||
self._info_text.draw(self)
|
||||
self._info_icons.draw(self)
|
||||
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_DOWN:
|
||||
self._info_text.scroll_down()
|
||||
if event.key == pygame.K_UP:
|
||||
self._info_text.scroll_up()
|
||||
if event.key == pygame.K_a:
|
||||
self.running = False
|
||||
self.additional_args = {'selected_color': self._selected_egg}
|
||||
self.next_surface = 'playground'
|
||||
if event.key == pygame.K_b:
|
||||
self.running = False
|
||||
self.additional_args = {'selected_color': self._selected_egg}
|
||||
self.next_surface = 'egg_select'
|
37
pocket_friends/surfaces/title.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""Title screen module. Contains the surface object to be rendered on the screen."""
|
||||
import pygame
|
||||
from pocket_friends.elements import surface
|
||||
|
||||
|
||||
class Surface(surface.GameSurface):
|
||||
"""
|
||||
Surface object for the title screen. Child class of surface.GameSurface.
|
||||
"""
|
||||
def __init__(self, game_res: tuple, resources_dir: str, game_fps: int):
|
||||
"""
|
||||
Create a title screen surface object.
|
||||
Args:
|
||||
game_res: The internal resolution of the surface.
|
||||
resources_dir: The path of the game's main resource directory.
|
||||
game_fps: How many frames per second the game will run at.
|
||||
"""
|
||||
|
||||
super().__init__(game_res, resources_dir, game_fps)
|
||||
|
||||
self._frames = 0 # Counter for the number of frames passed
|
||||
self._delay = 1 # Delay in seconds that the title should stay on screen
|
||||
|
||||
# Image of the title screen
|
||||
self._title = pygame.image.load(resources_dir + '/images/title.png').convert_alpha()
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Advance the surface logic by one frame.
|
||||
"""
|
||||
self.preprocess()
|
||||
self.blit(self._title, (0, 0))
|
||||
|
||||
self._frames += 1
|
||||
if self._frames > self.game_fps * self._delay:
|
||||
self.next_surface = 'egg_select'
|
||||
self.running = False
|
@@ -1 +1 @@
|
||||
pygame~=2.1.2
|
||||
pygame~=1.9.6
|