"""
Part of library for Main Menu.
"""
import sys
import pygame
ARROWS = [(pygame.K_UP, "up"), (pygame.K_DOWN, "down"), (pygame.K_LEFT,
"left"), (pygame.K_RIGHT, "right")]
"""List of keys (arrows) for Menu changing index.
"""
HIT = [(pygame.K_RETURN, "hit"), (pygame.K_SPACE, "hit")]
"""List of keys for Menu selecting option.
"""
sys.__stdout__ = sys.stdout
[docs]def useless(*useless_args):
"""Function that doesn't do anything.
"""
pass
[docs]def ch_image(new_image):
"""Returns function which changes pusher image.
:param new_image: new image for option
:type new_image: pygame.Surface
:return: function
"""
def function(obj, self):
self.image = new_image
return function
[docs]def ch_pos(off):
"""Returns function which changes pusher position.
:param off: *x* and *y* change
:type off: tuple of numbers
:return: function
"""
def function(obj, self):
if obj.off_type == "pixel":
self.x1 += off[0]
self.y1 += off[1]
else:
self.x1 += obj.screen_w * off[0] / 100
self.y1 += obj.screen_h * off[1] / 100
return function
[docs]def ch_text(new_text, error=True):
"""Returns function which changes pusher text (only for text_pushers).
:param str new_text: new text for pusher
:param bool error: if ``True`` raises error if pusher isn't text_pusher
:returns: function
"""
def function(obj, self):
if type(self) == text_pusher:
self.text = new_text
self.update()
else:
if error:
raise TypeError(str(type(self)) +
" should be text_pusher!")
return function
[docs]def ch_color(new_color, error=True):
"""Returns function which changes pusher color (only for text_pushers).
:param new_color: new color for pusher
:type new_color: color-like tuple
:param bool error: if ``True`` raises error if pusher isn't text_pusher
:returns: function
"""
def function(obj, self):
if type(self) == text_pusher:
self.color = new_color
self.update()
else:
if error:
raise TypeError(str(type(self)) +
" should be text_pusher!")
return function
[docs]def ch_font(new_font, error=True):
"""Returns function which changes pusher font (only for text_pushers).
:param new_font: font for puhser to be changed
:type new_font: pygame.font.Font
:param bool error: if ``True`` raises error if pusher isn't text_pusher
:returns: function
"""
def function(obj, self):
if type(self) == text_pusher:
self.font = new_font
self.update()
else:
if error:
raise TypeError(str(type(self)) +
" should be text_pusher!")
return function
[docs]def reset(im=True, pos=True):
"""Returns function which resets pusher.
:param bool im: if ``True`` resets image
:param bool pos: if ``True`` resets position
:returns: function
"""
def function(obj, self):
self.reset(im, pos)
return function
[docs]def link(place):
"""Returns function which leads game to onother place.
:param place: place for game current place
:type place: :py:class:`PLACE`
:returns: function
"""
def function(obj, self):
obj.game.change(place)
return function
[docs]def Quit(obj, self):
"""Quits game.
"""
obj.game.kill()
[docs]def joined(List):
"""Returns function which executes all function in a given list.
:param list List: list of all functions which will be executed
:returns: function
"""
def function(self, obj):
for f in List:
f(self, obj)
return function
[docs]def set_printer(printer):
"""Sets default printer for print.
:param printer: printer which will be default for print()
"""
sys.stdout = printer
[docs]def normalize_print():
"""Normalize print().
"""
sys.stout = sys.__stdout__
[docs]def equal(image1, image2):
"""Test if two images are equal.*
"""
if image1.get_size() != image2.get_size():
return False
X, Y = image1.get_size()
for y in range(Y):
for x in range(X):
if image1.get_at((x, y)) != image1.get_at((x, y)):
return False
return True
[docs]class PLACE:
"""Bacis class for states of main menu.
:param bool active: place activity
"""
def __init__(self, active=False):
self.active = active
def __bool__(self):
return self.active
[docs] def activate(self):
"""Activates place.
"""
self.active = True
[docs] def deactivate(self):
"""Deactivates place.
"""
self.active = False
[docs]class Game:
"""Bacis game class main menu.
:param main_place: first place for game
:type place: :py:class:`PLACE`
"""
def __init__(self, main_place):
self.current = main_place
self.current.activate()
self.menues = []
self.running = True
self.options = {}
[docs] def blit(self):
"""Blits game current menu.
"""
if self.running:
for menu in self.menues:
if self.current in menu.places:
menu.blit()
[docs] def change(self, new):
"""Change game current place.
:param new: new place which will be blitted
:type new: :py:class:`PLACE`
"""
self.current.deactivate()
self.current = new
self.current.activate()
[docs] def define(self, **options):
"""Defines options parameters for all menues.
:param options: option parameters for all menues (see
:py:class:`Main_menu` and :py:class:`option`)
"""
self.options = options
for menu in self.menues:
menu.define(**options)
[docs] def declare(self, type=None, **options):
"""Defines menues parameters.
:param type: type of all menues in game (None for will be defined
later).
:type type: type
:param options: menu parameters (see :py:class:`Main_menu`)
"""
self.type = type
self.Options = options
[docs] def get_from(self, prior=False, **rest):
"""Returns menu from game combining previously defined and rest
arguments.
:param bool prior: if ``True`` rest options are prefered otherwise
previously defined are prefered.
:param rest: menu parameters which aren't defined by
:py:meth:`Game.declare`
:returns: menu or any other type defined by *type* argument
"""
self.opt = rest
for x in self.Options:
if not prior or not x in self.opt:
self.opt[x] = self.Options[x]
if "type" in self.opt:
if prior or not self.type:
t = self.opt["type"]
del self.opt["type"]
return t(**self.opt)
else:
del self.opt["type"]
return self.type(**self.opt)
[docs] def set_from(self, prior=False, **rest):
"""Like :py:meth:`get_from` but this function automatically sets
menu for game menu.
:param bool prior: if ``True`` rest options are prefered otherwise
previously defined are prefered.
:param rest: menu parameters which aren't defined by
:py:meth:`Game.declare`
:returns: menu or any other type defined by *type* argument
"""
M = self.get_from(prior, **rest)
M.set_game(self)
return M
[docs] def run(self):
"""Returns is game still running.
:returns: ``True`` if game is running otherwise ``False``
"""
return self.running
[docs] def kill(self):
"""Ends game running.
"""
self.running = False
[docs]class Hyper_game(Game):
"""Game for easy places controlling.
"""
def __init__(self):
self.places = []
self.menues = []
self.index = 0
[docs] def blit(self):
"""Blits game current menu.
"""
self.menues[self.index].blit()
[docs]class Keyboard:
"""Bacis keyboard class.
:param definer: defines keyboard keys
:type definer: list of tuples
"""
def __init__(self, definer):
self.definer = definer
self.new_definer = {}
self.keys = {}
for event, ac in self.definer:
self.new_definer[event] = ac
self.keys[ac] = 0
self.definer = self.new_definer
def __getitem__(self, index):
return self.keys[index]
[docs] def extend(self, extension):
"""Extends keyboard definer.
:param extension: defines new keyboard keys
:type extension: list of tuples
"""
self.ext = extension
for event, ac in self.ext:
self.new_definer[event] = ac
self.keys[ac] = 0
[docs] def update(self):
"""Updates keyboard.*
"""
for K in self.keys:
if self.keys[K] == 1:
self.keys[K] = 2
elif self.keys[K] == 3:
self.keys[K] = 0
for event in pygame.event.get():
if event.type in (pygame.KEYDOWN, pygame.KEYUP):
if event.key in self.definer:
if type(self.definer[event.key]) == list:
for x in self.definer[event.key]:
if event.type == pygame.KEYDOWN:
self.keys[x] = 1
elif event.type == pygame.KEYUP:
self.keys[x] = 3
else:
if event.type == pygame.KEYDOWN:
self.keys[self.definer[event.key]] = 1
elif event.type == pygame.KEYUP:
self.keys[self.definer[event.key]] = 3
[docs]class Main_menu:
"""Basic menu class.
:param places: place/es on which will menu be blitted
:type places: :py:class:`PLACE` or list of :py:class:`PLACE`
:param int x_distance: x distance between each option
:param int y_distance: y distance between each option
:param options: menu options
:type options: :py:class:`option`
:param off: how *off* menu is (on relation to the center of screen)
:type off: tuple of two ints
:param str off_type: if 'pixel' *off* will be measured in pixels, if
'percent' (or '%') *off* will be measured in percent of screen
:param keyboard: keyboard which will be used in menu
:type keyboard: :py:class:`Keyboard`
"""
def __init__(self, places, x_distance, y_distance, *options,
off=(0, 0), off_type='pixel', start=(50, 50),
keyboard=Keyboard(ARROWS + HIT)):
self.places = places
if type(self.places) != list:
self.places = [self.places]
self.distance = x_distance, y_distance
self.screen = pygame.display.get_surface()
self.screen_w, self.screen_h = self.screen.get_size()
self.options = list(options)
self.off = off
self.off_type = off_type
self.start = start
self.keyboard = keyboard
self.center = (self.screen_w * self.start[0] / 100, self.screen_h *
self.start[1] / 100)
if self.off_type == 'pixel':
self.center = (self.center[0] + self.off[0], self.center[1] +
self.off[1])
if self.off_type in ('percent', '%'):
self.center = (self.center[0] + self.screen_w * self.off[0] /
100, self.center[1] + self.screen_h * self.off[1] / 100)
def __repr__(self):
fin = "Menu object:\n"
fin += "at " + str(self.center) + "(center),\n"
fin += "with " + str(len(self.options)) + " option" + "s" if\
len(self.options) > 1 else "" + ",\n"
try:
fin += "index at option " + str(self.index)
except:
fin += "no index currently"
return fin
[docs] def add_option(self, option, seted_option=False):
"""Adds new option to menu.
:param option: option which will be added to menu
:type option: :py:class:option
:param bool seted_option: if ``True`` menu index will be on that
option
"""
self.options.append(option)
if seted_option:
self.current = option
[docs] def set_options(self):
"""Should be executed on the end of options adding.
"""
self.first_x = self.center[0] - (len(self.options) - 1) / 2 *\
self.distance[0]
self.first_y = self.center[1] - (len(self.options) - 1) / 2 *\
self.distance[1]
for pos, opt in enumerate(self.options):
opt.set_position(self.first_x + pos * self.distance[0],
self.first_y + pos * self.distance[1])
if opt == self.current:
self.index = pos
self.current.bold()
[docs] def define(self, type=None, **options):
"""Defines options parameters.
:param type: type of option
:type type: type
:param options: arguments for options
"""
self.type = type
self.Options = options
self.Options["menu"] = self
[docs] def get_from(self, prior=False, **rest):
"""Returns option from menu combining previously defined and rest
arguments.
:param bool prior: if ``True`` rest options are prefered otherwise
previously defined are prefered.
:param rest: option parameters which aren't defined by
:py:meth:`Main_menu.define`
:returns: option or any other type defined by *type* argument
"""
self.opt = rest
for x in self.Options:
if not prior or not x in self.opt:
self.opt[x] = self.Options[x]
if "type" in self.opt:
if prior or not self.type:
t = self.opt["type"]
del self.opt["type"]
return t(**self.opt)
else:
del self.opt["type"]
return self.type(**self.opt)
[docs] def set_from(self, seted_option=False, prior=False, **rest):
"""Sets option to menu.
:param bool seted_option: if ``True`` menu index will be on that
option
:param bool prior: if ``True`` rest options are prefered otherwise
previously defined are prefered
:param rest: option parameters which aren't defined by
:py:meth:`Main_menu.define`
"""
self.add_option(self.get_from(prior, **rest), seted_option)
[docs] def set_game(self, game):
"""Sets menu to given game.
:param game: game which will be added to menu
:type game: :py:class:`Game`
"""
self.game = game
self.game.add_menu(self)
[docs] def blit(self):
"""Blits menu.
"""
self.keyboard.update()
if self.keyboard["up"] == 1:
self.current.un_bold()
self.index -= 1
self.index %= len(self.options)
self.current = self.options[self.index]
self.current.bold()
elif self.keyboard["down"] == 1:
self.current.un_bold()
self.index += 1
self.index %= len(self.options)
self.current = self.options[self.index]
self.current.bold()
if self.keyboard["hit"] == 1:
self.current.hit()
for opt in self.options:
opt.blit()
[docs] def get_keyboard(self):
"""Returns game menu keyboard.
:returns: menu keyboard
:rtype: :py:class:`Keyboard`
"""
return self.keyboard
[docs] def reset(self, *rest):
"""Turns on all options in Main Menu.
:param rest: unimportant parameter.
"""
for opt in self.options:
opt.proces = 2
[docs]class printer:
"""Basic printer (for print()) class.
:param int x: x of print place
:param int y: y of print place
:param font: text font
:type font: pygame.font.Font
:param int newl: space between lines
"""
def __init__(self, x, y, font, newl, color=(0, 0, 0)):
self.x = x
self.y = y
self.screen = pygame.display.get_surface()
self.text = ""
self.newl = newl
self.font = font
self.color = color
[docs] def write(self, text):
"""Writes text to priter.*
"""
self.text += text
[docs] def clear(self):
"""Clears text on printer
"""
self.text = ""
[docs] def blit(self):
"""Blits printer
"""
for p, l in enumerate(self.text.splitlines()):
self.ren = self.font.render(l, True, self.color)
self.screen.blit(self.ren, (self.x, self.y + p * self.newl))
[docs]class option:
"""Bacis menu option class.
:param image: option image
:type image: pygame.Surface
:param menu: option menu
:type menu: :py:class:`Main_menu`
:param function do: what happens when option is activated
:param function pos_do: what happens when index comes to option
:param function anti_pos_do: what happens when index to another option
"""
def __init__(self, image, menu, do=useless, pos_do=useless,
anti_pos_do=useless):
self.image = image
self.x2, self.y2 = self.image.get_size()
self.menu = menu
self.do = do
self.pos_do = pos_do
self.anti_pos_do = anti_pos_do
def __repr__(self):
return "option object @" + str(self.x1) + ", " + str(self.y1)
[docs] def set_position(self, x, y):
"""Sets option's position.*
"""
self.x1 = x - self.x2 / 2
self.y1 = y - self.y2 / 2
self.pusher = pusher(self.x1, self.y1, self.image, self.menu,
self.do, self.pos_do, self.anti_pos_do)
[docs] def blit(self):
"""Blits option.*
"""
self.pusher.blit()
[docs] def hit(self):
"""
Executed when menu index is on option and when space ispressed.*
"""
self.pusher.hit()
[docs] def bold(self):
"""Executed when menu index comes on option.*
"""
self.pusher.bold()
[docs] def un_bold(self):
"""Executed when menu index comes out of option.*
"""
self.pusher.un_bold()
[docs]class pusher:
"""Bacis independed option class (mostly used in option class).*
"""
def __init__(self, x1, y1, image, obj, do=None, pos_do=None,
anti_pos_do=None):
self.x1, self.y1 = self.sx, self.sy = x1, y1
self.image = self.simage = image
self.x2, self.y2 = self.image.get_size()
self.obj = obj
self.do = do
self.pos_do = pos_do
self.anti_pos_do = anti_pos_do
[docs] def blit(self):
"""Blits pusher.*
"""
self.obj.screen.blit(self.image, (self.x1, self.y1))
[docs] def hit(self):
"""
Executed when menu index is on option and when space is pressed.*
"""
self.do(self.obj, self)
[docs] def bold(self):
"""Executed when menu index comes on option.*
"""
self.pos_do(self.obj, self)
[docs] def un_bold(self):
"""Executed when menu index comes out of option.*
"""
self.anti_pos_do(self.obj, self)
[docs] def reset(self, im, pos):
"""Resets pusher.
"""
if im:
self.image = self.simage
self.x2, self.y2 = self.image.get_size()
if pos:
self.x1, self.y1 = self.sx, self.sy
[docs]class text_option(option):
"""Textual menu option class(subclass of option).
:param font: option font
:type font: pygame.font.Font
:param color: option color
:type color: color-like tuple
:param str text: option text
:param menu: option menu
:type menu: :py:class:`Main_menu`
:param function do: what happens when option is activated
:param function pos_do: what happens when index comes to option
:param function anti_pos_do: what happens when index to another option
"""
def __init__(self, font, text, color, menu, do=useless, pos_do=useless,
anti_pos_do=useless):
self.font = font
self.text = text
self.color = color
super().__init__(self.font.render(self.text, True, self.color),
menu, do, pos_do, anti_pos_do)
[docs] def set_position(self, x, y):
"""Sets option's position.*
"""
self.center = (x, y)
super().set_position(x, y)
self.pusher = text_pusher(self.x1, self.y1, self.font, self.text,
self.color, self.menu, self.do, self.pos_do, self.anti_pos_do)
[docs] def blit(self):
"""Blits option.*
"""
super().blit()
if not equal(self.image, self.pusher.image):
self.image = self.pusher.image
self.x2, self.y2 = self.image.get_size()
self.set_position(*self.center)
[docs]class text_pusher(pusher):
"""Textual independed option class (mostly used in text_option class,
subclass of pusher).*
"""
def __init__(self, x1, y1, font, text, color, obj, do,
pos_do, anti_pos_do):
self.font = font
self.text = text
self.color = color
super().__init__(x1, y1, self.font.render(self.text, True,
self.color), obj, do, pos_do, anti_pos_do)
[docs] def update(self):
"""Updates pusher.*
"""
self.image = self.font.render(self.text, True, self.color)
class fake_txt_opt(text_option):
def __init__(self, font, text, color, menu, *args, **kwargs):
super().__init__(font, text, color, menu)
def bold(self):
if self.menu.keyboard["up"] == 1:
self.menu.index -= 1
self.menu.index %= len(self.menu.options)
self.menu.current = self.menu.options[self.menu.index]
self.menu.current.bold()
elif self.menu.keyboard["down"] == 1:
self.menu.index += 1
self.menu.index %= len(self.menu.options)
self.menu.current = self.menu.options[self.menu.index]
self.menu.current.bold()