23 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
16 changed files with 286 additions and 59 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) [![License: GNU GPL v3.0](https://img.shields.io/badge/license-GNU%20GPL%20v3.0-blue)](LICENSE)

View File

@@ -11,7 +11,6 @@ if __name__ == '__main__':
enable_dev = False enable_dev = False
resolution = 240 resolution = 240
# enable dev mode if --dev argument is passed
if len(sys.argv) > 0: if len(sys.argv) > 0:
for arg in sys.argv: for arg in sys.argv:
if arg == '--delete-save': if arg == '--delete-save':
@@ -19,8 +18,10 @@ if __name__ == '__main__':
os.remove(save_dir + '/save.json') os.remove(save_dir + '/save.json')
if '--res=' in arg: if '--res=' in arg:
resolution = int(arg.split('=')[1]) resolution = int(arg.split('=')[1])
if arg == '--dev':
enable_dev = True
game.start_game(resolution) game.start_game(resolution=resolution, dev=enable_dev)
pygame.quit() pygame.quit()
sys.exit() 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

@@ -6,41 +6,53 @@ from ..io.input_handler import InputHandler
class GameSurface(pygame.Surface): class GameSurface(pygame.Surface):
""" """
Class to be used as the backbone of all game surfaces. 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: Attributes:
running (bool): Boolean to tell whether the surface is running or not. running: Boolean to tell whether the surface is running or not.
next_surface (:obj:`str`): What the next surface should be after halting. next_surface: String that represents the next surface to be switched to after halting.
resource_dir (:obj:`str`): The path of the game's main resource directory. resource_dir: String that contains the path of the game's main resource directory.
game_fps (int): How many frames per second the game will run at. game_fps: An integer value of how many frames per second the game will run at.
additional_args (dict): Additional arguments to send to the next surface after halting. 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, resources_dir, game_fps): def __init__(self, game_res: tuple, resources_dir: str, game_fps: int):
""" """
Create a GameSurface object. Create a GameSurface object.
Args: Args:
game_res (int): The internal resolution of the surface. game_res: The internal resolution of the surface.
resources_dir (:obj:`str`): The path of the game's main resource directory. resources_dir: The path of the game's main resource directory.
game_fps: (int): How many frames per second the game will run at. game_fps: How many frames per second the game will run at.
""" """
super().__init__(game_res, pygame.SRCALPHA) super().__init__(game_res, pygame.SRCALPHA)
self.running = True self.running = True # Surfaces should be running by default
self.next_surface = None self.next_surface = None
self.resource_dir = resources_dir self.resource_dir = resources_dir
self._clock = pygame.time.Clock()
self.game_fps = game_fps self.game_fps = game_fps
self._input_handler = InputHandler(self._clock)
self.additional_args = {} self.additional_args = {}
self.dev_override = False
self._bg = pygame.image.load(self.resource_dir + '/images/bg.png').convert_alpha() self.background = pygame.image.load(self.resource_dir + '/images/bg.png').convert_alpha()
self.sprites = pygame.sprite.Group()
self._clock = pygame.time.Clock()
self._input_handler = InputHandler(self._clock)
self._sprites = pygame.sprite.Group()
def preprocess(self): def preprocess(self):
""" """
Advance the surface by one frame and draw the background. 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._clock.tick(self.game_fps)
self.blit(self._bg, (0, 0)) self.blit(self.background, (0, 0))
self.sprites.update() self._sprites.update()
self.sprites.draw(self) self._sprites.draw(self)
self._input_handler.update() self._input_handler.update()
if self._input_handler.dev_found:
self.dev_override = True
self.running = False

View File

@@ -1,21 +1,26 @@
"""Main module for the game. Runs setting up the Pygame window and handles scene switching."""
import pygame import pygame
import os import os
import pocket_friends import pocket_friends
import importlib import importlib
valid_surfaces = [ valid_surfaces = [
'_dev_menu',
'_black_screen',
'_error_screen',
'title', 'title',
'egg_select', 'egg_select',
'selection_info', 'selection_info',
'error_screen'
] ]
# Add all the surface modules to a dictionary for easy switching # Add all the surface modules to a dictionary for easy switching
surface_modules = {} surface_modules = {}
for module in valid_surfaces: for module in valid_surfaces:
surface_modules[module] = importlib.import_module('pocket_friends.surfaces.{0}'.format(module)) if module[0] == '_':
starting_surface = 'title' 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. # FPS for the game to run at.
game_fps = 16 game_fps = 16
@@ -30,12 +35,13 @@ resources_dir = script_dir + '/resources'
os.environ['SDL_FBDEV'] = '/dev/fb1' os.environ['SDL_FBDEV'] = '/dev/fb1'
def start_game(resolution=240): def start_game(resolution: int, dev: bool):
""" """
Starts the game. Starts the game.
Args: Args:
resolution (int, optional): Resolution to display the game at. Defaults to 240. resolution: Resolution to display the game at.
dev: Boolean to enable the developer menu at start or not
""" """
pygame.init() pygame.init()
@@ -43,6 +49,11 @@ def start_game(resolution=240):
# Hide the cursor for the Pi display. # Hide the cursor for the Pi display.
pygame.mouse.set_visible(False) pygame.mouse.set_visible(False)
if dev:
starting_surface = '_dev_menu'
else:
starting_surface = 'title'
window = pygame.display.set_mode((resolution, resolution)) window = pygame.display.set_mode((resolution, resolution))
surface = surface_modules.get(starting_surface).Surface((game_res, game_res), resources_dir, game_fps) surface = surface_modules.get(starting_surface).Surface((game_res, game_res), resources_dir, game_fps)
@@ -62,11 +73,21 @@ def start_game(resolution=240):
frame = pygame.transform.scale(surface, (resolution, resolution)) frame = pygame.transform.scale(surface, (resolution, resolution))
window.blit(frame, frame.get_rect()) 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: if not surface.running:
next_surface = surface.next_surface # Force the dev menu to appear if the flag has been passed
additional_args = surface.additional_args 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: if next_surface not in valid_surfaces:
next_surface = 'error_screen' 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, surface = surface_modules.get(next_surface).Surface((game_res, game_res), resources_dir,
game_fps, **additional_args) game_fps, **additional_args)
pygame.display.flip() pygame.display.flip()

View File

@@ -1,5 +1,5 @@
import pygame import pygame
from collections import deque
class InputHandler: class InputHandler:
""" """
@@ -24,6 +24,12 @@ class InputHandler:
self.clock = pygame_clock self.clock = pygame_clock
self.tick_check = tick_check self.tick_check = tick_check
self.last_input_tick = 0 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): def create_event(self, pressed_button):
""" """
@@ -37,6 +43,9 @@ class InputHandler:
if pygame.time.get_ticks() - self.last_input_tick > self.clock.get_time() * 2: 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.KEYDOWN, {'key': pressed_button}))
pygame.event.post(pygame.event.Event(pygame.KEYUP, {'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: else:
pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button})) pygame.event.post(pygame.event.Event(pygame.KEYDOWN, {'key': pressed_button}))
pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button})) pygame.event.post(pygame.event.Event(pygame.KEYUP, {'key': pressed_button}))
@@ -70,3 +79,5 @@ class InputHandler:
def update(self): def update(self):
"""Run either the GPIO handler or the keyboard handler to check for input and create events.""" """Run either the GPIO handler or the keyboard handler to check for input and create events."""
self.handle_keyboard() self.handle_keyboard()
if self.dev_code == self.dev_check:
self.dev_found = True

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

View File

@@ -48,7 +48,7 @@ class Surface(surface.GameSurface):
egg.rect.y = y egg.rect.y = y
# Add the egg to the sprite list. # Add the egg to the sprite list.
self.sprites.add(egg) self._sprites.add(egg)
self.selected_egg = 0 self.selected_egg = 0
self.selected_color = '' self.selected_color = ''

View File

@@ -1,49 +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 import pygame
from pocket_friends.elements import sprites from pocket_friends.elements import sprites
from pocket_friends.elements import surface from pocket_friends.elements import surface
class Surface(surface.GameSurface): class Surface(surface.GameSurface):
def __init__(self, game_res, resources_dir, game_fps, **kwargs): """
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) super().__init__(game_res, resources_dir, game_fps)
preselected_color = None self._selected_egg = None
for key in kwargs.keys():
if key == 'selected_color':
preselected_color = kwargs.get(key)
self.selected_egg = None
for key in kwargs.keys(): for key in kwargs.keys():
if key == 'selected_egg': if key == 'selected_egg':
self.selected_egg = kwargs.get(key) self._selected_egg = kwargs.get(key)
egg = sprites.SelectionEgg(self.selected_egg, resources_dir) egg = sprites.SelectionEgg(self._selected_egg, resources_dir)
egg.rect.x = 8 egg.rect.x = 8
egg.rect.y = 3 egg.rect.y = 3
self.sprites.add(egg) self._sprites.add(egg)
self.info_text = sprites.InfoText(resources_dir, game_res[0], egg.description) 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)) self._info_icons = sprites.EggInfo(resources_dir, egg.contentedness, egg.metabolism, (32, 4))
def update(self): def update(self):
"""
Draw objects on the screen and advance the surface logic by one frame; Handle user input.
"""
self.preprocess() self.preprocess()
self.info_text.draw(self) self._info_text.draw(self)
self.info_icons.draw(self) self._info_icons.draw(self)
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.KEYDOWN: if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN: if event.key == pygame.K_DOWN:
# Scroll down on the info screen. self._info_text.scroll_down()
self.info_text.scroll_down()
if event.key == pygame.K_UP: if event.key == pygame.K_UP:
# Scroll up on the info screen. self._info_text.scroll_up()
self.info_text.scroll_up()
if event.key == pygame.K_a: if event.key == pygame.K_a:
self.running = False self.running = False
self.additional_args = {'selected_color': self.selected_egg} self.additional_args = {'selected_color': self._selected_egg}
self.next_surface = 'playground' self.next_surface = 'playground'
if event.key == pygame.K_b: if event.key == pygame.K_b:
self.running = False self.running = False
self.additional_args = {'selected_color': self.selected_egg} self.additional_args = {'selected_color': self._selected_egg}
self.next_surface = 'egg_select' self.next_surface = 'egg_select'

View File

@@ -1,24 +1,37 @@
"""Title screen module. Contains the surface object to be rendered on the screen."""
import pygame import pygame
from pocket_friends.elements import surface from pocket_friends.elements import surface
class Surface(surface.GameSurface): class Surface(surface.GameSurface):
def __init__(self, game_res, resources_dir, game_fps): """
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) super().__init__(game_res, resources_dir, game_fps)
self.frames = 0 self._frames = 0 # Counter for the number of frames passed
self.delay = 1 self._delay = 1 # Delay in seconds that the title should stay on screen
self.title = pygame.image.load(resources_dir + '/images/title.png').convert_alpha() # Image of the title screen
self._title = pygame.image.load(resources_dir + '/images/title.png').convert_alpha()
def update(self): def update(self):
""" """
Advance the surface logic by one frame. Advance the surface logic by one frame.
""" """
self.preprocess() self.preprocess()
self.blit(self.title, (0, 0)) self.blit(self._title, (0, 0))
self.frames += 1 self._frames += 1
if self.frames > self.game_fps * self.delay: if self._frames > self.game_fps * self._delay:
self.next_surface = 'egg_select' self.next_surface = 'egg_select'
self.running = False self.running = False