migrating project to GitHub, version 0.0.1
135
.gitignore
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# Save file
|
||||||
|
/save.json
|
||||||
|
|
||||||
|
# pocket-friends compile script
|
||||||
|
/compile.bat
|
1
data/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Imports classes for running the game."""
|
56
data/development/FakeGPIO.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"""
|
||||||
|
Module used to fake the RPi.GPIO module so that
|
||||||
|
the game 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
|
1
data/development/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Initializes all classes needed for the development environment, and faking GPIO inputs."""
|
50
data/development/button_test.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Module to test the GPIO input on the Raspberry Pi.
|
||||||
|
"""
|
||||||
|
from collections import deque
|
||||||
|
from ..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()
|
154
data/development/dev_menu.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
"""
|
||||||
|
Development menu for the game on Raspberry Pi. NOTE: THIS DOES NOTHING ON A COMPUTER!
|
||||||
|
"""
|
||||||
|
import data.game
|
||||||
|
import importlib.util
|
||||||
|
import os
|
||||||
|
import pygame
|
||||||
|
import time
|
||||||
|
from .button_test import button_test
|
||||||
|
from .menus import Menu
|
||||||
|
from ..gpio_handler import GPIOHandler, Constants
|
||||||
|
|
||||||
|
dev_version = '0.0.1'
|
||||||
|
|
||||||
|
try:
|
||||||
|
importlib.util.find_spec('RPi.GPIO')
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
except ImportError:
|
||||||
|
import data.development.FakeGPIO 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 game.
|
||||||
|
"""
|
||||||
|
GPIOHandler.teardown()
|
||||||
|
data.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 {0}\nGame Version {1}'.format(dev_version, data.game.version))
|
||||||
|
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)
|
120
data/development/menus.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"""
|
||||||
|
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
|
487
data/game.py
Normal file
@ -0,0 +1,487 @@
|
|||||||
|
"""
|
||||||
|
Main file for the entire game. Controls everything except for GPIO input.
|
||||||
|
"""
|
||||||
|
from collections import deque
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pygame
|
||||||
|
from pygame.locals import *
|
||||||
|
from .gpio_handler import Constants, GPIOHandler
|
||||||
|
|
||||||
|
version = '0.0.1'
|
||||||
|
game_fps = 16
|
||||||
|
|
||||||
|
|
||||||
|
class FileHandler:
|
||||||
|
"""
|
||||||
|
Class that handles the game attributes and save files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# Attributes that are saved to a file to recover upon startup.
|
||||||
|
self.attributes = {
|
||||||
|
'time_elapsed': 0,
|
||||||
|
'age': 0,
|
||||||
|
'health': 0,
|
||||||
|
'hunger': 0,
|
||||||
|
'happiness': 0,
|
||||||
|
'evolution_stage': -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
def write_save(self):
|
||||||
|
"""
|
||||||
|
Writes attributes of class to "save.json" file.
|
||||||
|
"""
|
||||||
|
with open('save.json', 'w') as save_file:
|
||||||
|
json.dump(self.attributes, save_file)
|
||||||
|
save_file.close()
|
||||||
|
|
||||||
|
def read_save(self):
|
||||||
|
"""
|
||||||
|
Reads from "save.json" and inserts into attributes dictionary. Creates file if it does not exist.
|
||||||
|
"""
|
||||||
|
# Open up the save file and read it into self.attributes.
|
||||||
|
try:
|
||||||
|
with open('save.json', 'r') as save_file:
|
||||||
|
self.attributes = json.load(save_file)
|
||||||
|
save_file.close()
|
||||||
|
|
||||||
|
# If there is no save file, write one with the defaults.
|
||||||
|
except FileNotFoundError:
|
||||||
|
self.write_save()
|
||||||
|
|
||||||
|
|
||||||
|
class PlaygroundFriend(pygame.sprite.Sprite):
|
||||||
|
"""
|
||||||
|
Class for the sprite of the creature on the main playground.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pygame.sprite.Sprite.__init__(self)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectionEgg(pygame.sprite.Sprite):
|
||||||
|
"""
|
||||||
|
Class for the eggs on the egg selection screen.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, egg_color):
|
||||||
|
pygame.sprite.Sprite.__init__(self)
|
||||||
|
|
||||||
|
image_directory = 'resources/images/egg_images/{0}'.format(egg_color)
|
||||||
|
|
||||||
|
# Load the egg from the given color and get the bounding rectangle for the image.
|
||||||
|
self.images = []
|
||||||
|
for filename in os.listdir(image_directory):
|
||||||
|
self.images.append(pygame.image.load(image_directory + '/' + filename))
|
||||||
|
|
||||||
|
self.rect = self.images[0].get_rect()
|
||||||
|
self.index = 0
|
||||||
|
self.image = self.images[self.index]
|
||||||
|
|
||||||
|
self.animation_frames = game_fps / len(self.images)
|
||||||
|
self.current_frame = 0
|
||||||
|
|
||||||
|
def update_frame_dependent(self):
|
||||||
|
"""
|
||||||
|
Updates the image of Sprite every 6 frame (approximately every 0.1 second if frame rate is 60).
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.current_frame += 1
|
||||||
|
if self.current_frame >= self.animation_frames:
|
||||||
|
self.current_frame = 0
|
||||||
|
self.index = (self.index + 1) % len(self.images)
|
||||||
|
self.image = self.images[self.index]
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""This is the method that's being called when 'all_sprites.update(dt)' is called."""
|
||||||
|
self.update_frame_dependent()
|
||||||
|
|
||||||
|
|
||||||
|
# Makes Pygame draw on the display of the RPi.
|
||||||
|
os.environ["SDL_FBDEV"] = "/dev/fb1"
|
||||||
|
|
||||||
|
# Useful for debugging on the PC. Imports a fake RPi.GPIO library if one is not found (which it can't
|
||||||
|
# be on a PC, RPi.GPIO cannot be installed outside of a Raspberry Pi.
|
||||||
|
try:
|
||||||
|
importlib.util.find_spec('RPi.GPIO')
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
except ImportError:
|
||||||
|
import data.development.FakeGPIO as GPIO
|
||||||
|
|
||||||
|
|
||||||
|
def game():
|
||||||
|
"""
|
||||||
|
Starts the game.
|
||||||
|
"""
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
# Hide the cursor for the Pi display.
|
||||||
|
pygame.mouse.set_visible(False)
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
rendered_size = 80
|
||||||
|
screen_size = 800
|
||||||
|
|
||||||
|
window = pygame.display.set_mode((screen_size, screen_size))
|
||||||
|
surface = pygame.Surface((rendered_size, rendered_size))
|
||||||
|
|
||||||
|
# Only really useful for PCs. Does nothing on the Raspberry Pi.
|
||||||
|
pygame.display.set_caption('Pocket Friends')
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
# Font used for small text in the game. Bigger text is usually image files.
|
||||||
|
small_font = pygame.font.Font('resources/fonts/5Pts5.ttf', 10)
|
||||||
|
|
||||||
|
# Default game state when the game first starts.
|
||||||
|
game_state = 'title'
|
||||||
|
running = True
|
||||||
|
file_handler = FileHandler()
|
||||||
|
|
||||||
|
# A group of all the sprites on screen. Used to update all sprites at onc
|
||||||
|
all_sprites = pygame.sprite.Group()
|
||||||
|
|
||||||
|
# Start the GPIO handler to take in buttons from the RPi HAT.
|
||||||
|
GPIOHandler.setup()
|
||||||
|
|
||||||
|
# Dev code used to exit the game. Default Down, Down, Up, Up, Down, Down, Up, Up, A, A, B
|
||||||
|
dev_code = deque()
|
||||||
|
for button in [Constants.buttons.get('j_d'), Constants.buttons.get('j_d'), Constants.buttons.get('j_u'),
|
||||||
|
Constants.buttons.get('j_u'), Constants.buttons.get('j_d'), Constants.buttons.get('j_d'),
|
||||||
|
Constants.buttons.get('j_u'), Constants.buttons.get('j_u'), Constants.buttons.get('a'),
|
||||||
|
Constants.buttons.get('a'), Constants.buttons.get('b')]:
|
||||||
|
dev_code.append(button)
|
||||||
|
|
||||||
|
# Log of the inputs.
|
||||||
|
input_log = deque()
|
||||||
|
|
||||||
|
# Time since last input. Used to help regulate double presses of buttons.
|
||||||
|
last_input_tick = 0
|
||||||
|
|
||||||
|
def draw():
|
||||||
|
"""
|
||||||
|
Draws the main pygame display.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Draws all the sprites on screen and scales the screen to the correct size from the rendered size.
|
||||||
|
all_sprites.update()
|
||||||
|
all_sprites.draw(surface)
|
||||||
|
frame = pygame.transform.scale(surface, (screen_size, screen_size))
|
||||||
|
window.blit(frame, frame.get_rect())
|
||||||
|
|
||||||
|
# Update the entire display.
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
def draw_bg():
|
||||||
|
"""
|
||||||
|
Draws the main game background image onto a given surface.
|
||||||
|
"""
|
||||||
|
bg_image = pygame.image.load('resources/images/bg.png').convert()
|
||||||
|
surface.blit(bg_image, (0, 0))
|
||||||
|
|
||||||
|
def log_button(pressed_button):
|
||||||
|
"""
|
||||||
|
Logs the button presses to register the dev code.
|
||||||
|
:param pressed_button: The button code to be logged
|
||||||
|
"""
|
||||||
|
input_log.append(pressed_button)
|
||||||
|
if len(input_log) > len(dev_code):
|
||||||
|
input_log.popleft()
|
||||||
|
|
||||||
|
def create_event(pressed_button):
|
||||||
|
"""
|
||||||
|
Creates a pygame event with a given keyboard code
|
||||||
|
:param pressed_button:
|
||||||
|
"""
|
||||||
|
nonlocal last_input_tick
|
||||||
|
# Register a button click so long as the last button click happened no less than two frames ago
|
||||||
|
if pygame.time.get_ticks() - last_input_tick > clock.get_time() * 2:
|
||||||
|
pygame.event.post(pygame.event.Event(KEYDOWN, {'key': pressed_button}))
|
||||||
|
pygame.event.post(pygame.event.Event(KEYUP, {'key': pressed_button}))
|
||||||
|
log_button(pressed_button)
|
||||||
|
last_input_tick = pygame.time.get_ticks()
|
||||||
|
|
||||||
|
def check_dev_code():
|
||||||
|
"""
|
||||||
|
Checks if the dev code has been entered. If it has, stop the program.
|
||||||
|
"""
|
||||||
|
nonlocal running
|
||||||
|
|
||||||
|
if dev_code == input_log:
|
||||||
|
running = False
|
||||||
|
|
||||||
|
def handle_gpio():
|
||||||
|
"""
|
||||||
|
Handles getting GPIO button presses and making a pygame event when a press is detected.
|
||||||
|
"""
|
||||||
|
for pressed_button in Constants.buttons:
|
||||||
|
code = Constants.buttons.get(pressed_button)
|
||||||
|
|
||||||
|
# Check if a button has been pressed. If it has, create a pygame event for it.
|
||||||
|
if GPIOHandler.get_press(code):
|
||||||
|
create_event(code)
|
||||||
|
|
||||||
|
def keyboard_handler():
|
||||||
|
"""
|
||||||
|
Simulates key presses to GPIO button presses. Also handles quitting the game.
|
||||||
|
"""
|
||||||
|
nonlocal running
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
create_event(Constants.buttons.get('a'))
|
||||||
|
if keyboard_event.key == pygame.K_b:
|
||||||
|
create_event(Constants.buttons.get('b'))
|
||||||
|
if keyboard_event.key == pygame.K_PERIOD:
|
||||||
|
create_event(Constants.buttons.get('j_i'))
|
||||||
|
if keyboard_event.key == pygame.K_RIGHT:
|
||||||
|
create_event(Constants.buttons.get('j_r'))
|
||||||
|
if keyboard_event.key == pygame.K_LEFT:
|
||||||
|
create_event(Constants.buttons.get('j_l'))
|
||||||
|
if keyboard_event.key == pygame.K_DOWN:
|
||||||
|
create_event(Constants.buttons.get('j_d'))
|
||||||
|
if keyboard_event.key == pygame.K_UP:
|
||||||
|
create_event(Constants.buttons.get('j_u'))
|
||||||
|
if keyboard_event.key == pygame.K_ESCAPE:
|
||||||
|
running = False
|
||||||
|
|
||||||
|
def pre_handler():
|
||||||
|
"""
|
||||||
|
Runs at the beginning of each loop, handles drawing the background, controlling game speed, and
|
||||||
|
controlling the GPIO button inputs and keyboard handler
|
||||||
|
"""
|
||||||
|
# Regulate the speed of the game.
|
||||||
|
clock.tick(game_fps)
|
||||||
|
|
||||||
|
# Handle all inputs for both debugging and real GPIO button presses.
|
||||||
|
keyboard_handler()
|
||||||
|
handle_gpio()
|
||||||
|
check_dev_code()
|
||||||
|
|
||||||
|
# Draw the background.
|
||||||
|
draw_bg()
|
||||||
|
|
||||||
|
while running:
|
||||||
|
if game_state == 'title':
|
||||||
|
all_sprites.empty()
|
||||||
|
pre_handler()
|
||||||
|
|
||||||
|
# Draw the title image in the middle of the screen.
|
||||||
|
title_image = pygame.image.load('resources/images/title.png').convert_alpha()
|
||||||
|
surface.blit(title_image, (0, 0))
|
||||||
|
draw()
|
||||||
|
|
||||||
|
# Show the title for 1 second then move on to the initialization phase of the game.
|
||||||
|
pygame.time.wait(1000)
|
||||||
|
game_state = 'init'
|
||||||
|
|
||||||
|
elif game_state == 'playground':
|
||||||
|
all_sprites.empty()
|
||||||
|
game_state = None # Playground currently not implemented, send to error screen.
|
||||||
|
|
||||||
|
elif game_state == 'init':
|
||||||
|
all_sprites.empty()
|
||||||
|
pre_handler()
|
||||||
|
draw()
|
||||||
|
|
||||||
|
# Read the save file.
|
||||||
|
file_handler.read_save()
|
||||||
|
|
||||||
|
# Determines if it is a new game or not by looking at the evolution stage. If it is -1, the egg has
|
||||||
|
# not been created yet, and the game sends you to the egg selection screen. If not, the game sends
|
||||||
|
# you to the playground.
|
||||||
|
if file_handler.attributes['evolution_stage'] == -1:
|
||||||
|
game_state = 'egg_select'
|
||||||
|
else:
|
||||||
|
game_state = 'playground'
|
||||||
|
|
||||||
|
elif game_state == 'egg_select':
|
||||||
|
|
||||||
|
# Submenu used within the egg selection menu.
|
||||||
|
submenu = 'main'
|
||||||
|
|
||||||
|
selected = 0
|
||||||
|
|
||||||
|
while running and game_state == 'egg_select':
|
||||||
|
all_sprites.empty()
|
||||||
|
if submenu == 'main':
|
||||||
|
|
||||||
|
# Creates and holds the egg objects in a list.
|
||||||
|
eggs = [SelectionEgg('red'), SelectionEgg('orange'), SelectionEgg('yellow'),
|
||||||
|
SelectionEgg('green'),
|
||||||
|
SelectionEgg('blue'), SelectionEgg('indigo'), SelectionEgg('violet'), SelectionEgg('white'),
|
||||||
|
SelectionEgg('rainbow')]
|
||||||
|
|
||||||
|
# How many eggs per row should be displayed.
|
||||||
|
eggs_per_row = 3
|
||||||
|
distance_between_eggs = 36 / eggs_per_row
|
||||||
|
|
||||||
|
# Count the total rows.
|
||||||
|
total_rows = -(-len(eggs) // eggs_per_row)
|
||||||
|
distance_between_rows = 32 / eggs_per_row
|
||||||
|
|
||||||
|
# Determine the location of each egg.
|
||||||
|
for egg in eggs:
|
||||||
|
current_row = eggs.index(egg) // eggs_per_row
|
||||||
|
rows_after = total_rows - (current_row + 1)
|
||||||
|
egg_in_row = eggs.index(egg) % eggs_per_row
|
||||||
|
eggs_after = min(len(eggs) - (current_row * eggs_per_row), eggs_per_row) - (egg_in_row + 1)
|
||||||
|
|
||||||
|
x_offset = 32
|
||||||
|
y_offset = 30
|
||||||
|
|
||||||
|
# The x coordinate of an egg is determined by which egg in the row it is, and how many eggs
|
||||||
|
# are in that row. If there is only 1 egg in a row, it is in the middle of the screen. If
|
||||||
|
# there are two, they're on equal halves and so on.
|
||||||
|
x = x_offset - (eggs_after * distance_between_eggs) + (egg_in_row * distance_between_eggs)
|
||||||
|
y = y_offset - (rows_after * distance_between_rows) + (current_row * distance_between_rows)
|
||||||
|
|
||||||
|
egg.rect.x = x
|
||||||
|
egg.rect.y = y
|
||||||
|
|
||||||
|
# Add the egg to the sprite list.
|
||||||
|
all_sprites.add(egg)
|
||||||
|
selected = 0
|
||||||
|
|
||||||
|
def get_cursor_coords(selection):
|
||||||
|
"""
|
||||||
|
Gets the coordinates of an egg on the selection screen by index and returns it as a tuple
|
||||||
|
:param selection: index of the egg to be selected
|
||||||
|
:return: tuple of the coordinates of the selected egg
|
||||||
|
"""
|
||||||
|
cursor_x_offset = -2
|
||||||
|
cursor_y_offset = -2
|
||||||
|
|
||||||
|
return eggs[selection].rect.x + cursor_x_offset, eggs[selection].rect.y + cursor_y_offset
|
||||||
|
|
||||||
|
def sel_left():
|
||||||
|
"""
|
||||||
|
Select the egg to the left with constraints.
|
||||||
|
"""
|
||||||
|
nonlocal selected
|
||||||
|
|
||||||
|
if selected % eggs_per_row != 0:
|
||||||
|
selected -= 1
|
||||||
|
|
||||||
|
def sel_right():
|
||||||
|
"""
|
||||||
|
Select the egg to the right with constraints.
|
||||||
|
"""
|
||||||
|
nonlocal selected
|
||||||
|
|
||||||
|
row = selected // eggs_per_row
|
||||||
|
eggs_in_row = min(len(eggs) - (row * eggs_per_row), eggs_per_row)
|
||||||
|
|
||||||
|
if selected % eggs_per_row != eggs_in_row - 1:
|
||||||
|
selected += 1
|
||||||
|
|
||||||
|
def sel_up():
|
||||||
|
"""
|
||||||
|
Select the egg above with constraints.
|
||||||
|
"""
|
||||||
|
nonlocal selected
|
||||||
|
|
||||||
|
if selected // eggs_per_row != 0:
|
||||||
|
selected -= eggs_per_row
|
||||||
|
|
||||||
|
def sel_down():
|
||||||
|
"""
|
||||||
|
Select the egg below with constraints.
|
||||||
|
"""
|
||||||
|
nonlocal selected
|
||||||
|
|
||||||
|
if selected // eggs_per_row != total_rows - 1:
|
||||||
|
selected += eggs_per_row
|
||||||
|
|
||||||
|
while running and game_state == 'egg_select' and submenu == 'main':
|
||||||
|
|
||||||
|
pre_handler()
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == Constants.buttons.get('j_r'):
|
||||||
|
sel_right()
|
||||||
|
if event.key == Constants.buttons.get('j_l'):
|
||||||
|
sel_left()
|
||||||
|
if event.key == Constants.buttons.get('j_d'):
|
||||||
|
sel_down()
|
||||||
|
if event.key == Constants.buttons.get('j_u'):
|
||||||
|
sel_up()
|
||||||
|
if event.key == Constants.buttons.get('a'):
|
||||||
|
# Advance to the egg info screen for the selected egg.
|
||||||
|
submenu = 'egg_info'
|
||||||
|
|
||||||
|
# Draws the cursor on screen.
|
||||||
|
cursor = pygame.image.load('resources/images/clock_selector.png').convert_alpha()
|
||||||
|
surface.blit(cursor, get_cursor_coords(selected))
|
||||||
|
|
||||||
|
draw()
|
||||||
|
|
||||||
|
elif submenu == 'egg_info':
|
||||||
|
while running and game_state == 'egg_select' and submenu == 'egg_info':
|
||||||
|
pre_handler()
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == Constants.buttons.get('a'):
|
||||||
|
# Go to an invalid game state if continuing.
|
||||||
|
game_state = None
|
||||||
|
if event.key == Constants.buttons.get('b'):
|
||||||
|
# Go back to the egg selection screen.
|
||||||
|
submenu = 'main'
|
||||||
|
|
||||||
|
# Quick debugging for which egg is selected.
|
||||||
|
selection_debug = small_font.render('Egg {0}'.format(selected), False, (64, 64, 64))
|
||||||
|
surface.blit(selection_debug, (5, 35))
|
||||||
|
|
||||||
|
draw()
|
||||||
|
|
||||||
|
else: # Go to the error state if an invalid state is set.
|
||||||
|
game_state = None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Error screen. This appears when an invalid game state has been selected.
|
||||||
|
|
||||||
|
all_sprites.empty()
|
||||||
|
frames_passed = 0 # Counter for frames, helps ensure the game isnt frozen.
|
||||||
|
|
||||||
|
while running and game_state != 'title':
|
||||||
|
|
||||||
|
pre_handler()
|
||||||
|
|
||||||
|
# Draw the error screen
|
||||||
|
error_screen = pygame.image.load('resources/images/debug/invalid.png').convert_alpha()
|
||||||
|
surface.blit(error_screen, (0, -8))
|
||||||
|
|
||||||
|
# Counts the frames passed. Resets every second.
|
||||||
|
frames_passed += 1
|
||||||
|
if frames_passed >= game_fps:
|
||||||
|
frames_passed = 0
|
||||||
|
|
||||||
|
# Draws the frame counter.
|
||||||
|
frame_counter = small_font.render('frames: {0}'.format(frames_passed), False, (64, 64, 64))
|
||||||
|
surface.blit(frame_counter, (1, 70))
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == Constants.buttons.get('b'):
|
||||||
|
# Reset back to the title screen.
|
||||||
|
game_state = 'title'
|
||||||
|
|
||||||
|
draw()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
Calls the game() function to start the game.
|
||||||
|
"""
|
||||||
|
game()
|
||||||
|
|
||||||
|
GPIOHandler.teardown()
|
||||||
|
pygame.quit()
|
71
data/gpio_handler.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"""
|
||||||
|
Module that helps with the handling of taking 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
|
||||||
|
|
||||||
|
try:
|
||||||
|
importlib.util.find_spec('RPi.GPIO')
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
except ImportError:
|
||||||
|
import data.development.FakeGPIO as GPIO
|
||||||
|
|
||||||
|
|
||||||
|
class Constants:
|
||||||
|
"""
|
||||||
|
Contains the constants used by the HAT to read in buttons
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class GPIOHandler:
|
||||||
|
"""
|
||||||
|
Class to handle the GPIO inputs from the buttons.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setup():
|
||||||
|
"""
|
||||||
|
Primes the GPIO pins for reading the inputs of the buttons.
|
||||||
|
"""
|
||||||
|
GPIO.setmode(GPIO.BOARD)
|
||||||
|
|
||||||
|
GPIO.setup(Constants.buttons.get('a'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('b'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('j_i'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('j_u'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('j_d'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('j_l'), GPIO.IN)
|
||||||
|
GPIO.setup(Constants.buttons.get('j_r'), GPIO.IN)
|
||||||
|
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('a'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('b'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('j_i'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('j_u'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('j_d'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('j_l'), GPIO.FALLING)
|
||||||
|
GPIO.add_event_detect(Constants.buttons.get('j_r'), GPIO.FALLING)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def teardown():
|
||||||
|
"""
|
||||||
|
Cleans up the GPIO handler.
|
||||||
|
"""
|
||||||
|
GPIO.cleanup()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_press(button):
|
||||||
|
"""
|
||||||
|
Returns true if a button has moved 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)
|
25
pocket_friends.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
Launch script for Pocket Friends.
|
||||||
|
"""
|
||||||
|
import pygame
|
||||||
|
import sys
|
||||||
|
from data.game import main as game_main
|
||||||
|
from data.development.dev_menu import main as dev_menu_main
|
||||||
|
|
||||||
|
enable_dev = False
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# enable dev mode if --dev argument is passed
|
||||||
|
if len(sys.argv) > 0:
|
||||||
|
for args in sys.argv:
|
||||||
|
if args == '--dev':
|
||||||
|
enable_dev = True
|
||||||
|
|
||||||
|
if not enable_dev:
|
||||||
|
game_main()
|
||||||
|
else:
|
||||||
|
dev_menu_main()
|
||||||
|
|
||||||
|
pygame.quit()
|
||||||
|
sys.exit()
|
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
pygame==1.9.4
|
BIN
resources/fonts/5Pts5.ttf
Normal file
BIN
resources/images/bg.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/images/clock_selector.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
resources/images/debug/invalid.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
resources/images/egg_images/blue/blue.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
resources/images/egg_images/dev_egg/dev_egg_0.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
resources/images/egg_images/dev_egg/dev_egg_1.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
resources/images/egg_images/green/green.png
Normal file
After Width: | Height: | Size: 807 B |
BIN
resources/images/egg_images/indigo/indigo.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
resources/images/egg_images/orange/orange.png
Normal file
After Width: | Height: | Size: 820 B |
BIN
resources/images/egg_images/rainbow/rainbow.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
resources/images/egg_images/red/red.png
Normal file
After Width: | Height: | Size: 864 B |
BIN
resources/images/egg_images/violet/violet.png
Normal file
After Width: | Height: | Size: 740 B |
BIN
resources/images/egg_images/white/white.png
Normal file
After Width: | Height: | Size: 490 B |
BIN
resources/images/egg_images/yellow/yellow.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
resources/images/icon.png
Normal file
After Width: | Height: | Size: 312 B |
BIN
resources/images/title.png
Normal file
After Width: | Height: | Size: 20 KiB |