60 Commits

Author SHA1 Message Date
3fde1996ca added quit to dev menu 2023-05-15 01:20:37 -04:00
eef3a8756d black screen now returns to the dev menu instead of the title 2023-05-15 01:14:59 -04:00
8675caaca6 updated docstrings and comments 2023-05-15 01:04:45 -04:00
9fd2bc03df fixed incorrect game_res type in documentation 2023-05-15 01:04:33 -04:00
680a2cab75 fixed incorrectly tagged type 2023-05-15 00:31:05 -04:00
9669e6185d fixed dev menu not having proper background 2023-05-15 00:28:20 -04:00
8c49c62f66 marked error_screen as a protected module, added --dev launch option 2023-05-15 00:27:14 -04:00
30e25999c4 marked attributes of a few classes as protected 2023-05-15 00:26:44 -04:00
f765bb3dcb updated comments and docstrings 2023-05-15 00:24:57 -04:00
86e8c51494 added black screen to dev menu 2023-05-14 23:04:26 -04:00
f3751def40 Merge remote-tracking branch 'origin/new_dev_menu' 2023-05-14 22:38:08 -04:00
4ba12d5078 added app restart to dev screen 2023-05-14 19:48:29 -04:00
e5c17b60e7 fixed it for the last goddamn time 2023-05-14 19:46:59 -04:00
d7a01340b7 fixed update commands 2023-05-14 19:45:34 -04:00
ad97725634 added bin to commands 2023-05-14 19:44:08 -04:00
2def4f846a added update command 2023-05-14 19:41:44 -04:00
d4ff6d0786 fixed shutdown dev command 2023-05-14 19:32:12 -04:00
7c49424350 made dev commands actually do something 2023-05-14 19:26:38 -04:00
1c467371a8 integrated dev menu into game with secret code 2023-05-14 19:26:09 -04:00
8a34ed7c01 created dev menu selector and test functions 2023-05-14 19:06:50 -04:00
b6432e233a changed dev menu to white 2023-05-14 17:55:37 -04:00
4249a7c3f1 added preliminary dev menu 2023-05-14 17:47:56 -04:00
bcf933992e fixed promo image not showing after restructure 2023-05-14 17:28:10 -04:00
187baf6f3e removed old dev menu files 2023-05-14 17:26:57 -04:00
e974687da3 removed unnecessary print statement 2023-05-14 17:21:41 -04:00
bd2931ebe8 update to dev_0.1.0 2023-05-14 17:20:28 -04:00
60757a23ff Merge remote-tracking branch 'origin/surface-rebuild'
# Conflicts:
#	pocket_friends/game_files/game.py
2023-05-14 17:18:47 -04:00
6a0201da85 removed redundant starting function 2023-05-14 17:10:34 -04:00
06755ee937 rearragned project, removed unneeded game_files folder 2023-05-13 13:11:47 -04:00
83a7e98a7f created surface.py and GameSurface class 2023-05-13 13:07:33 -04:00
41ab0406f8 removed unused import 2023-05-13 11:47:48 -04:00
4904ed2184 fixed documentation 2023-05-13 11:47:36 -04:00
63a87c12dd removed some old print statements 2023-05-13 10:50:25 -04:00
2b2c2d946a added a bool to disable tick checking if needed 2023-05-13 10:48:59 -04:00
0f69120213 removed gpio modules (rest in peace) 2023-05-13 10:46:34 -04:00
35ac1cff51 removed windowed flag, added resolution flag 2023-05-13 10:30:27 -04:00
38a5812839 re-enabled the game lol 2023-05-13 10:17:30 -04:00
f6e9869282 fixed rpi display not rendering anything 2023-05-13 10:16:43 -04:00
afd228d81a copied code from old codebase to see if anything happens 2023-05-13 09:56:55 -04:00
6debf73267 made screen size constant 2023-05-13 09:49:51 -04:00
1fef87e1c3 disabled loading other surfaces as a test 2023-05-13 00:04:34 -04:00
e9df84897a removed print statements 2023-05-12 23:57:23 -04:00
07be003a54 reordered stuff in a desperate attempt 2023-05-12 23:55:06 -04:00
c547f76b35 attempt at fixing hardware 2023-05-12 23:49:36 -04:00
aa7bcccada changed default resolution to 240x240 2023-05-12 23:45:35 -04:00
e6cb2bd4c8 added --windowed parameter, fixed missing surface assignment 2023-05-12 23:42:21 -04:00
d932538b41 changed resolution to 240x240 fullscreen by default 2023-05-12 23:36:21 -04:00
526b51b754 disabled gpio_handler.py to always return false to ON_HARDWARE 2023-05-12 23:34:15 -04:00
b8776a6f7f fixed GPIO handler error 2023-05-12 23:28:00 -04:00
9b752bde21 fixed game not rendering on hardware 2023-05-12 23:21:33 -04:00
8e9e96d1bb added hardware check 2023-05-12 23:17:33 -04:00
5d3fc6ca49 changed python version to 3.7, changed pygame to 1.9.6 2023-05-12 22:57:56 -04:00
3fa4e2190e changed python version back to 3.9 2023-05-12 17:34:40 -04:00
b65f112b36 removed unused import 2023-05-12 17:34:27 -04:00
5b2c724882 added error screen 2023-05-12 17:34:13 -04:00
d8775fb1a9 updated comments, made some attributes private 2023-05-12 13:49:26 -04:00
4045930bd9 updated data.py docstrings 2023-05-12 11:46:23 -04:00
d322949b5a changed unneeded attributes in SpriteSheet to regular variables in the constructor 2023-05-12 11:46:08 -04:00
b97d54f49d created new submodule to contain game elements 2023-05-12 11:44:58 -04:00
65365adc3d updated docstrings to follow google styling, changed checking of hardware to only happen once 2023-05-12 11:20:00 -04:00
82 changed files with 607 additions and 759 deletions

View File

@@ -1,5 +1,5 @@
![Pocket Friends](https://gitea.citruxx.com/ndyer/pocket-friends/raw/branch/master/pocket_friends/game_files/resources/images/promotional.png)
![Pocket Friends](https://gitea.citruxx.com/ndyer/pocket-friends/raw/branch/master/pocket_friends/resources/images/promotional.png)
[![License: GNU GPL v3.0](https://img.shields.io/badge/license-GNU%20GPL%20v3.0-blue)](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

View File

@@ -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',

View File

@@ -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'

View File

@@ -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()

View 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)

View 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]

View 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

View 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

View 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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1 @@
"""Submodule for use in the game. Helps with the drawing of various objects such as sprites and text boxes."""

View File

@@ -4,53 +4,77 @@ import json
class SpriteSheet:
"""
Imports a sprite sheet as separate pygame images given an image file and a json file.
Class to be used by sprites in order to give them a texture and an animation.
Attributes:
images (list) List of all the sprites in the animation separated from the sprite sheet.
"""
def __init__(self, sprite_sheet, texture_json):
"""
Creates a sprite sheet given a sprite image and its corresponding JSON file.
Args:
sprite_sheet (str): The path of the sprite sheet image component.
texture_json (str): The path of the sprite sheet JSON component, contains the number of frames in the
sprite sheet, and the width and height of an individual sprite from the sprite sheet.
"""
# Load in whole sprite sheet as one image.
self.sprite_sheet = pygame.image.load(sprite_sheet).convert_alpha()
sprite_sheet = pygame.image.load(sprite_sheet).convert_alpha()
self.images = []
# Get the sprite sheet json file.
with open(texture_json, 'r') as json_file:
self.img_attrib = json.load(json_file)
img_attrib = json.load(json_file)
json_file.close()
# Count for how many images have been added in the image list
image_count = 0
# Get the sprite size as a tuple
sprite_size = self.img_attrib['width'], self.img_attrib['height']
sprite_size = img_attrib['width'], img_attrib['height']
# Iterate through every image location on the sprite sheet given the sprite size
for i in range(self.sprite_sheet.get_size()[1] // sprite_size[1]):
for i in range(sprite_sheet.get_size()[1] // sprite_size[1]):
i *= sprite_size[1]
for j in range(self.sprite_sheet.get_size()[0] // sprite_size[0]):
for j in range(sprite_sheet.get_size()[0] // sprite_size[0]):
j *= sprite_size[0]
# Create a new transparent surface
sprite = pygame.Surface(sprite_size, pygame.SRCALPHA)
# Blit the sprite onto the image
sprite.blit(self.sprite_sheet, (0, 0), (j, i, sprite_size[0], sprite_size[1]))
sprite.blit(sprite_sheet, (0, 0), (j, i, sprite_size[0], sprite_size[1]))
# Add the image to the list of images
self.images.append(sprite)
image_count += 1
# Break the loop if the specified number of frames has been reached.
if image_count >= self.img_attrib['frames']:
if image_count >= img_attrib['frames']:
break
if image_count >= self.img_attrib['frames']:
if image_count >= img_attrib['frames']:
break
class SelectionEgg(pygame.sprite.Sprite):
"""
Class for the eggs on the egg selection screen.
Sprite to render the egg on the egg selection screen.
Attributes:
egg_color (str): The color of the egg (also its name).
description (str): The description of the egg to be displayed when selected.
contentedness (int): How likely the egg is to stay happy, ranges from 0-5.
metabolism (int): How quickly the egg will get hungry, ranges from 0-5.
rect (pygame.Rect): Pygame rectangle used to position the egg on screen.
"""
def __init__(self, egg_color, resources_dir):
"""
Creates a SelectionEgg object given an egg color and a resource location.
Args:
egg_color (str): The color egg that should be rendered.
resources_dir (str): The path of the resources directory.
"""
pygame.sprite.Sprite.__init__(self)
self.egg_color = egg_color
@@ -68,20 +92,20 @@ class SelectionEgg(pygame.sprite.Sprite):
# Load the egg from the given color and get the bounding rectangle for the image.
sprite_sheet = SpriteSheet(resources_dir + '/images/bloops/{0}/egg.png'.format(self.egg_color),
resources_dir + '/images/bloops/{0}/egg.json'.format(self.egg_color))
self.images = sprite_sheet.images
self._images = sprite_sheet.images
# Get the rectangle from the first image in the list
self.rect = self.images[0].get_rect()
self.index = 0
self.image = self.images[self.index]
self.rect = self._images[0].get_rect()
self._index = 0
self.image = self._images[self._index]
def update(self):
"""
Updates the sprite object.
Update the sprite to the next animation frame.
"""
# Animate the sprite
self.index = (self.index + 1) % len(self.images)
self.image = self.images[self.index]
self._index = (self._index + 1) % len(self._images)
self.image = self._images[self._index]
class InfoText:
@@ -89,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.
@@ -164,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
@@ -240,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))

View 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
View 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()

View File

@@ -1,81 +0,0 @@
import pygame
import os
from pathlib import Path
import pocket_friends
import importlib
valid_surfaces = [
'title',
'egg_select',
'selection_info'
]
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:
raise Exception('Given surface is not listed in valid surfaces!')
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()

View File

@@ -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."""

View File

@@ -1,56 +0,0 @@
"""
Module used to fake the RPi.GPIO module so that
the program can be run without the actual hardware.
"""
# Constants used by RPi.GPIO
BOARD = 0
IN = 0
FALLING = 0
def setmode(new_mode):
"""
Fake setmode function.
:param new_mode:
"""
pass
def setup(channel, mode, initial=None, pull_up_down=None):
"""
Fake setup function.
:param channel:
:param mode:
:param initial:
:param pull_up_down:
"""
pass
def add_event_detect(channel, edge_type, callback=None, bouncetime=0):
"""
Fake function to add a non-existent event detect.
:param channel:
:param edge_type:
:param callback:
:param bouncetime:
"""
pass
def event_detected(channel):
"""
Fake function to detect an event. Always returns false.
:param channel:
:return:
"""
return False
def cleanup(channel=None):
"""
Fake cleanup function.
:param channel:
"""
pass

View File

@@ -1,62 +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.
try:
importlib.util.find_spec('RPi.GPIO')
import RPi.GPIO as GPIO
except ImportError:
import pocket_friends.game_files.io.fake_gpio as GPIO
# 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():
"""
Primes 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():
"""
Cleans up the GPIO handler.
"""
GPIO.cleanup()
def get_press(button):
"""
Returns true if a button has changed from not pressed to pressed.
:param button: button to be detected
:return: True if the button is has been pressed, False otherwise
"""
return GPIO.event_detected(button)

View File

@@ -1,82 +0,0 @@
import pygame
import importlib.util
import pocket_friends.game_files.io.gpio_handler as gpio_handler
class InputHandler:
"""
Class that is implemented into surfaces in order to control the
pressing of buttons on both the real hardware and on a keyboard.
"""
def __init__(self, pygame_clock):
self.clock = pygame_clock
# If not on actual hardware, fake the GPIO in order to get it working correctly.
try:
importlib.util.find_spec('RPi.GPIO')
import RPi.GPIO as GPIO
self.on_hardware = True
except ImportError:
import pocket_friends.game_files.io.fake_gpio as GPIO
self.on_hardware = False
self.last_input_tick = 0
def create_event(self, pressed_button):
"""
Creates a pygame event with a given keyboard code
:param pressed_button:
"""
# 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 self.on_hardware:
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):
"""
Handles getting GPIO button presses and making a pygame event when a press is detected.
"""
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 keyboard_handler(self):
"""
Simulates key presses to GPIO button presses. Also handles quitting the game.
"""
# Checks if a corresponding keyboard key has been pressed. If it has, emulate a button press.
for keyboard_event in pygame.event.get():
if keyboard_event.type == pygame.QUIT:
running = False
if keyboard_event.type == pygame.KEYDOWN:
if keyboard_event.key == pygame.K_a:
self.create_event(gpio_handler.BUTTONS.get('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'))
if keyboard_event.key == pygame.K_RIGHT:
self.create_event(gpio_handler.BUTTONS.get('j_r'))
if keyboard_event.key == pygame.K_LEFT:
self.create_event(gpio_handler.BUTTONS.get('j_l'))
if keyboard_event.key == pygame.K_DOWN:
self.create_event(gpio_handler.BUTTONS.get('j_d'))
if keyboard_event.key == pygame.K_UP:
self.create_event(gpio_handler.BUTTONS.get('j_u'))
if keyboard_event.key == pygame.K_ESCAPE:
running = False
def update(self):
"""
Run the input handler and check for inputs.
"""
if self.on_hardware:
self.handle_gpio()
else:
self.keyboard_handler()

View File

@@ -1,60 +0,0 @@
import pygame
from . import sprites
import pocket_friends.game_files.io.gpio_handler as gpio_handler
from .sprites import SelectionEgg
from ..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 = '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'):
pass
if event.key == gpio_handler.BUTTONS.get('b'):
self.running = False
self.additional_args = {'selected_color': self.selected_egg}
self.next_surface = 'egg_select'

View File

@@ -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

View 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."""

View File

@@ -4,9 +4,11 @@ import json
class SaveData:
"""
Class that represents the save data of the game.
"""
Class used to read and write save data for the game
Attributes:
attributes (dict): Dictionary containing all the attributes to read and write from a save file.
"""
def __init__(self):
"""
Constructs the object with all starting values.

View File

@@ -0,0 +1,83 @@
import pygame
from collections import deque
class InputHandler:
"""
Class that is implemented into surfaces in order to control the
pressing of buttons on both the real hardware and on a keyboard.
Attributes:
clock (pygame.time.Clock): Pygame clock used for input time calculations.
last_input_tick (int): The tick that the last input was registered on.
"""
def __init__(self, pygame_clock, tick_check=True):
"""
Create a InputHandler object using a given Pygame clock.
Args:
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):
"""
Create a pygame event given a GPIO code and post it to the pygame event handler.
Args:
pressed_button (int): The GPIO code to be registered and pressed.
"""
# Register a button click so long as the last button click happened no less than two frames ago
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_keyboard(self):
"""Handle keyboard presses and generate corresponding GPIO codes to create events."""
# Checks if a corresponding keyboard key has been pressed. If it has, emulate a button press.
for keyboard_event in pygame.event.get():
if keyboard_event.type == pygame.QUIT:
running = False
if keyboard_event.type == pygame.KEYDOWN:
if keyboard_event.key == pygame.K_a:
self.create_event(pygame.K_a)
if keyboard_event.key == pygame.K_b:
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(pygame.K_RIGHT)
if keyboard_event.key == pygame.K_LEFT:
self.create_event(pygame.K_LEFT)
if keyboard_event.key == pygame.K_DOWN:
self.create_event(pygame.K_DOWN)
if keyboard_event.key == pygame.K_UP:
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."""
self.handle_keyboard()
if self.dev_code == self.dev_check:
self.dev_found = True

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 422 B

After

Width:  |  Height:  |  Size: 422 B

View File

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 565 B

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 864 B

After

Width:  |  Height:  |  Size: 864 B

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 246 B

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 188 B

After

Width:  |  Height:  |  Size: 188 B

View File

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 178 B

View File

Before

Width:  |  Height:  |  Size: 217 B

After

Width:  |  Height:  |  Size: 217 B

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 419 B

View File

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

View File

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 350 B

View File

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 344 B

View File

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 235 B

View File

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 302 B

View File

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

View File

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

View File

Before

Width:  |  Height:  |  Size: 213 B

After

Width:  |  Height:  |  Size: 213 B

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,23 +1,11 @@
import pygame
from . 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

View 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'

View 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

View File

@@ -1 +1 @@
pygame~=2.1.2
pygame~=1.9.6

View File

@@ -21,6 +21,6 @@ setuptools.setup(
classifiers=[
],
install_requires=required,
python_requires='>=3.10',
python_requires='>=3.7',
include_package_data=True,
)