├── .gitignore ├── MANIFEST.in ├── README ├── README.pygame ├── VERSION_NUMBERING ├── WHATSNEW ├── WHATSNEW.pygame ├── doc-src ├── Makefile ├── _static │ ├── LICENSES │ ├── favicon.ico │ └── sge_logo_alpha_black.png ├── collision.rst ├── conf.py ├── dsp.rst ├── gfx.rst ├── hello_world.rst ├── index.rst ├── input.rst ├── joystick.rst ├── keyboard.rst ├── make.bat ├── mouse.rst ├── pong.rst ├── pong_better.rst ├── s.rst ├── sge.rst └── snd.rst ├── examples ├── cc0-1.0.txt ├── circle_popper.py ├── data │ ├── LICENSES │ ├── WhereWasI.ogg │ ├── bounce.wav │ ├── bounce_wall.wav │ ├── circle.png │ ├── circle_pop-0.png │ ├── circle_pop-1.png │ ├── circle_pop-10.png │ ├── circle_pop-11.png │ ├── circle_pop-12.png │ ├── circle_pop-2.png │ ├── circle_pop-3.png │ ├── circle_pop-4.png │ ├── circle_pop-5.png │ ├── circle_pop-6.png │ ├── circle_pop-7.png │ ├── circle_pop-8.png │ ├── circle_pop-9.png │ ├── fence.png │ ├── pop.ogg │ ├── rotator.png │ └── score.wav ├── jstest.py ├── large_room.py ├── pong.py ├── rotation.py ├── splitscreen.py └── transitions.py ├── pyproject.toml ├── setup.py └── sge ├── COPYING ├── COPYING.LESSER ├── __init__.py ├── collision.py ├── dsp.py ├── gfx.py ├── input.py ├── joystick.py ├── keyboard.py ├── mouse.py ├── r.py ├── s.py └── snd.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | doc 8 | build 9 | eggs 10 | parts 11 | bin 12 | var 13 | sdist 14 | develop-eggs 15 | .installed.cfg 16 | _build 17 | 18 | # Installer logs 19 | pip-log.txt 20 | 21 | # Unit test / coverage reports 22 | .coverage 23 | .tox 24 | 25 | #Translations 26 | *.mo 27 | 28 | #Mr Developer 29 | .mr.developer.cfg 30 | 31 | MANIFEST 32 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.* WHATSNEW WHATSNEW.* VERSION_NUMBERING MANIFEST.in pyproject.toml 2 | recursive-include doc * 3 | recursive-include doc-src * 4 | recursive-include examples * 5 | global-include COPYING COPYING.* LICENSE LICENSES 6 | global-exclude *~ *.pyc *.pyo 7 | prune build 8 | prune dist 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This file has been dedicated to the public domain, to the extent 2 | possible under applicable law, via CC0. See 3 | http://creativecommons.org/publicdomain/zero/1.0/ for more 4 | information. This file is offered as-is, without any warranty. 5 | 6 | ======================================================================== 7 | 8 | LICENSE 9 | 10 | The Pygame SGE is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU Lesser General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | The Pygame SGE is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public License 21 | along with the Pygame SGE. If not, see . 22 | 23 | 24 | INSTALLATION 25 | 26 | First, install the dependencies: 27 | 28 | - Python 3.6 or later 29 | - Pygame 2.0 or later 30 | - uniseg 31 | 32 | Once you have all the dependencies, install the SGE with the included 33 | setup.py script, e.g. with "python setup.py install". 34 | 35 | Alternatively, you can just place the "sge" folder into the same 36 | directory as the Python program that uses it. This can be a good idea 37 | for the distribution of a game, since it reduces the number of 38 | dependencies the user needs to install. 39 | 40 | 41 | USAGE 42 | 43 | See the "doc" folder for detailed documentation on how to use the SGE. 44 | 45 | 46 | PYGAME SGE IMPLEMENTATION DETAILS 47 | 48 | See README.pygame for information specific to the Pygame SGE. 49 | 50 | -------------------------------------------------------------------------------- /README.pygame: -------------------------------------------------------------------------------- 1 | This file has been dedicated to the public domain, to the extent 2 | possible under applicable law, via CC0. See 3 | http://creativecommons.org/publicdomain/zero/1.0/ for more 4 | information. This file is offered as-is, without any warranty. 5 | 6 | ======================================================================== 7 | 8 | This file details specifics of the Pygame SGE implementation which 9 | may be useful to know. 10 | 11 | 12 | FORMATS SUPPORT 13 | 14 | sge.gfx.Sprite supports the following image formats if Pygame is built 15 | with full image support: 16 | 17 | - PNG 18 | - JPEG 19 | - Non-animated GIF 20 | - BMP 21 | - PCX 22 | - Uncompressed Truevision TGA 23 | - TIFF 24 | - ILBM 25 | - Netpbm 26 | - X Pixmap 27 | 28 | If Pygame is built without full image support, sge.gfx.Sprite will only 29 | be able to load uncompressed BMP images. 30 | 31 | sge.snd.Sound supports the following audio formats: 32 | 33 | - Uncompressed WAV 34 | - Ogg Vorbis 35 | 36 | sge.snd.Music supports the following audio formats: 37 | 38 | - Ogg Vorbis 39 | - MOD 40 | - XM 41 | - MIDI 42 | 43 | MP3 is also supported on some systems, but not all. On some systems, 44 | attempting to load an unsupported format can crash the game. Since MP3 45 | support is not available on all systems, it is best to avoid using it; 46 | consider using Ogg Vorbis instead. 47 | 48 | For starting position in MOD files, the pattern order number is used 49 | instead of the number of milliseconds. 50 | 51 | The pygame.mixer module, which is used for all audio playback, is 52 | optional and depends on SDL_mixer; if pygame.mixer is unavailable, 53 | sounds and music will not play. 54 | 55 | -------------------------------------------------------------------------------- /VERSION_NUMBERING: -------------------------------------------------------------------------------- 1 | This file has been dedicated to the public domain, to the extent 2 | possible under applicable law, via CC0. See 3 | http://creativecommons.org/publicdomain/zero/1.0/ for more 4 | information. This file is offered as-is, without any warranty. 5 | 6 | ======================================================================== 7 | 8 | This is an explanation of the SGE's version numbering, in particular how 9 | it indicates compatibility between various SGE releases. 10 | 11 | The SGE specification has only two components to its version number: the 12 | major version, and the minor version. 13 | 14 | Two versions of the SGE specification with different major version 15 | numbers can be expected to be incompatible. For example, there is no 16 | guarantee that a program designed for version 1.0 of the SGE will work 17 | with version 2.0 of the SGE. 18 | 19 | A version of the SGE specification with the same major version number, 20 | but a higher minor version number, than another version of the SGE 21 | specification can be assumed to be backward-compatible. This number is 22 | used to indicate things like unintrusive feature additions. For 23 | example, it can be expected that a program designed for version 1.0 of 24 | the SGE will work with version 1.1 of the SGE. 25 | 26 | As a special exception, two versions of the SGE with the major version 27 | number 0, but a different minor version number, can be expected to be 28 | incompatible (i.e. the minor version number is treated as the major 29 | version number). The major version number 0 is to be used until the 30 | specification has been properly tested (by developing a full, 31 | non-trivial game with it), so that it can be sure that version 1.0 of 32 | the specification is reasonably complete. 33 | 34 | SGE implementations, like the Pygame SGE, have either two or three 35 | components to their version numbers. The first two components should 36 | match the specification version they are implementing; for example, 37 | version 0.12 of the Pygame SGE implements version 0.12 of the SGE 38 | specification. 39 | 40 | The optional third component should be used to indicate any 41 | implementation-specific changes, such as bugfixes. 42 | -------------------------------------------------------------------------------- /doc-src/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = ../doc 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /doc-src/_static/LICENSES: -------------------------------------------------------------------------------- 1 | This file lists the licenses, authors, and sources for all the 2 | non-functional data files found in this directory. 3 | 4 | ======================================================================== 5 | 6 | sge_logo_alpha_black.png 7 | 8 | Authors: 9 | Francisco Cifuentes 10 | Modified by Layla Marchant 11 | License: CC BY 3.0 12 | 13 | ------------------------------------------------------------------------ 14 | 15 | favicon.ico 16 | 17 | Authors: 18 | Layla Marchant 19 | Based on SGE logo by Francisco Cifuentes 20 | License: CC BY 3.0 21 | -------------------------------------------------------------------------------- /doc-src/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/doc-src/_static/favicon.ico -------------------------------------------------------------------------------- /doc-src/_static/sge_logo_alpha_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/doc-src/_static/sge_logo_alpha_black.png -------------------------------------------------------------------------------- /doc-src/collision.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | sge.collision 3 | ************* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.collision 13 | 14 | sge.collision Functions 15 | ======================= 16 | 17 | .. autofunction:: sge.collision.rectangles_collide 18 | 19 | .. autofunction:: sge.collision.masks_collide 20 | 21 | .. autofunction:: sge.collision.rectangle 22 | 23 | .. autofunction:: sge.collision.ellipse 24 | 25 | .. autofunction:: sge.collision.circle 26 | 27 | .. autofunction:: sge.collision.line 28 | -------------------------------------------------------------------------------- /doc-src/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'Seclusion Game Engine' 23 | copyright = '' 24 | author = 'The Diligent Circle' 25 | 26 | # The short X.Y version 27 | version = '2.0' 28 | # The full version, including alpha/beta/rc tags 29 | release = '2.0.3' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | 'sphinx.ext.autodoc', 43 | ] 44 | 45 | # Add any paths that contain templates here, relative to this directory. 46 | templates_path = ['_templates'] 47 | 48 | # The suffix(es) of source filenames. 49 | # You can specify multiple suffix as a list of string: 50 | # 51 | # source_suffix = ['.rst', '.md'] 52 | source_suffix = '.rst' 53 | 54 | # The master toctree document. 55 | master_doc = 'index' 56 | 57 | # The language for content autogenerated by Sphinx. Refer to documentation 58 | # for a list of supported languages. 59 | # 60 | # This is also used if you do content translation via gettext catalogs. 61 | # Usually you set "language" from the command line for these cases. 62 | language = None 63 | 64 | # List of patterns, relative to source directory, that match files and 65 | # directories to ignore when looking for source files. 66 | # This pattern also affects html_static_path and html_extra_path. 67 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 68 | 69 | # The name of the Pygments (syntax highlighting) style to use. 70 | pygments_style = None 71 | 72 | 73 | # -- Options for HTML output ------------------------------------------------- 74 | 75 | # The theme to use for HTML and HTML Help pages. See the documentation for 76 | # a list of builtin themes. 77 | # 78 | html_theme = 'alabaster' 79 | 80 | # Theme options are theme-specific and customize the look and feel of a theme 81 | # further. For a list of options available for each theme, see the 82 | # documentation. 83 | # 84 | # html_theme_options = {} 85 | 86 | html_logo = "_static/sge_logo_alpha_black.png" 87 | html_favicon = "_static/favicon.ico" 88 | 89 | # Add any paths that contain custom static files (such as style sheets) here, 90 | # relative to this directory. They are copied after the builtin static files, 91 | # so a file named "default.css" will overwrite the builtin "default.css". 92 | html_static_path = ['_static'] 93 | 94 | # Custom sidebar templates, must be a dictionary that maps document names 95 | # to template names. 96 | # 97 | # The default sidebars (for documents that don't match any pattern) are 98 | # defined by theme itself. Builtin themes are using these templates by 99 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 100 | # 'searchbox.html']``. 101 | # 102 | # html_sidebars = {} 103 | 104 | 105 | # -- Options for HTMLHelp output --------------------------------------------- 106 | 107 | # Output file base name for HTML help builder. 108 | htmlhelp_basename = 'SGEdoc' 109 | 110 | 111 | # -- Options for LaTeX output ------------------------------------------------ 112 | 113 | latex_elements = { 114 | # The paper size ('letterpaper' or 'a4paper'). 115 | # 116 | # 'papersize': 'letterpaper', 117 | 118 | # The font size ('10pt', '11pt' or '12pt'). 119 | # 120 | # 'pointsize': '10pt', 121 | 122 | # Additional stuff for the LaTeX preamble. 123 | # 124 | # 'preamble': '', 125 | 126 | # Latex figure (float) alignment 127 | # 128 | # 'figure_align': 'htbp', 129 | } 130 | 131 | # Grouping the document tree into LaTeX files. List of tuples 132 | # (source start file, target name, title, 133 | # author, documentclass [howto, manual, or own class]). 134 | latex_documents = [ 135 | (master_doc, 'SGE.tex', 'Seclusion Game Engine Documentation', 136 | 'The Diligent Circle', 'manual'), 137 | ] 138 | 139 | 140 | # -- Options for manual page output ------------------------------------------ 141 | 142 | # One entry per manual page. List of tuples 143 | # (source start file, name, description, authors, manual section). 144 | man_pages = [ 145 | (master_doc, 'SGE', 'Seclusion Game Engine Documentation', 146 | [author], 1) 147 | ] 148 | 149 | 150 | # -- Options for Texinfo output ---------------------------------------------- 151 | 152 | # Grouping the document tree into Texinfo files. List of tuples 153 | # (source start file, target name, title, author, 154 | # dir menu entry, description, category) 155 | texinfo_documents = [ 156 | (master_doc, 'SGE', 'Seclusion Game Engine Documentation', 157 | author, 'SGE', '', 158 | 'Miscellaneous'), 159 | ] 160 | 161 | 162 | # -- Options for Epub output ------------------------------------------------- 163 | 164 | # Bibliographic Dublin Core info. 165 | epub_title = project 166 | 167 | # The unique identifier of the text. This can be a ISBN number 168 | # or the project homepage. 169 | # 170 | # epub_identifier = '' 171 | 172 | # A unique identification for the text. 173 | # 174 | # epub_uid = '' 175 | 176 | # A list of files that should not be packed into the epub file. 177 | epub_exclude_files = ['search.html'] 178 | 179 | 180 | # -- Extension configuration ------------------------------------------------- 181 | -------------------------------------------------------------------------------- /doc-src/dsp.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | sge.dsp 3 | ******* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.dsp 13 | 14 | sge.dsp Classes 15 | =============== 16 | 17 | sge.dsp.Game 18 | ------------ 19 | 20 | .. autoclass:: sge.dsp.Game 21 | 22 | .. automethod:: sge.dsp.Game.__init__ 23 | 24 | sge.dsp.Game Methods 25 | ~~~~~~~~~~~~~~~~~~~~ 26 | 27 | .. automethod:: sge.dsp.Game.start 28 | 29 | .. automethod:: sge.dsp.Game.end 30 | 31 | .. automethod:: sge.dsp.Game.pause 32 | 33 | .. automethod:: sge.dsp.Game.unpause 34 | 35 | .. automethod:: sge.dsp.Game.pump_input 36 | 37 | .. automethod:: sge.dsp.Game.regulate_speed 38 | 39 | .. automethod:: sge.dsp.Game.refresh 40 | 41 | .. automethod:: sge.dsp.Game.project_dot 42 | 43 | .. automethod:: sge.dsp.Game.project_line 44 | 45 | .. automethod:: sge.dsp.Game.project_rectangle 46 | 47 | .. automethod:: sge.dsp.Game.project_ellipse 48 | 49 | .. automethod:: sge.dsp.Game.project_circle 50 | 51 | .. automethod:: sge.dsp.Game.project_polyline 52 | 53 | .. automethod:: sge.dsp.Game.project_polygon 54 | 55 | .. automethod:: sge.dsp.Game.project_sprite 56 | 57 | .. automethod:: sge.dsp.Game.project_text 58 | 59 | sge.dsp.Game Event Methods 60 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 61 | 62 | .. automethod:: sge.dsp.Game.event_step 63 | 64 | .. automethod:: sge.dsp.Game.event_alarm 65 | 66 | .. automethod:: sge.dsp.Game.event_key_press 67 | 68 | .. automethod:: sge.dsp.Game.event_key_release 69 | 70 | .. automethod:: sge.dsp.Game.event_mouse_move 71 | 72 | .. automethod:: sge.dsp.Game.event_mouse_button_press 73 | 74 | .. automethod:: sge.dsp.Game.event_mouse_button_release 75 | 76 | .. automethod:: sge.dsp.Game.event_mouse_wheel_move 77 | 78 | .. automethod:: sge.dsp.Game.event_joystick 79 | 80 | .. automethod:: sge.dsp.Game.event_joystick_axis_move 81 | 82 | .. automethod:: sge.dsp.Game.event_joystick_hat_move 83 | 84 | .. automethod:: sge.dsp.Game.event_joystick_trackball_move 85 | 86 | .. automethod:: sge.dsp.Game.event_joystick_button_press 87 | 88 | .. automethod:: sge.dsp.Game.event_joystick_button_release 89 | 90 | .. automethod:: sge.dsp.Game.event_gain_keyboard_focus 91 | 92 | .. automethod:: sge.dsp.Game.event_lose_keyboard_focus 93 | 94 | .. automethod:: sge.dsp.Game.event_gain_mouse_focus 95 | 96 | .. automethod:: sge.dsp.Game.event_lose_mouse_focus 97 | 98 | .. automethod:: sge.dsp.Game.event_window_resize 99 | 100 | .. automethod:: sge.dsp.Game.event_close 101 | 102 | .. automethod:: sge.dsp.Game.event_mouse_collision 103 | 104 | .. automethod:: sge.dsp.Game.event_paused_step 105 | 106 | .. automethod:: sge.dsp.Game.event_paused_key_press 107 | 108 | .. automethod:: sge.dsp.Game.event_paused_key_release 109 | 110 | .. automethod:: sge.dsp.Game.event_paused_mouse_move 111 | 112 | .. automethod:: sge.dsp.Game.event_paused_mouse_button_press 113 | 114 | .. automethod:: sge.dsp.Game.event_paused_mouse_button_release 115 | 116 | .. automethod:: sge.dsp.Game.event_paused_mouse_wheel_move 117 | 118 | .. automethod:: sge.dsp.Game.event_paused_joystick 119 | 120 | .. automethod:: sge.dsp.Game.event_paused_joystick_axis_move 121 | 122 | .. automethod:: sge.dsp.Game.event_paused_joystick_hat_move 123 | 124 | .. automethod:: sge.dsp.Game.event_paused_joystick_trackball_move 125 | 126 | .. automethod:: sge.dsp.Game.event_paused_joystick_button_press 127 | 128 | .. automethod:: sge.dsp.Game.event_paused_joystick_button_release 129 | 130 | .. automethod:: sge.dsp.Game.event_paused_gain_keyboard_focus 131 | 132 | .. automethod:: sge.dsp.Game.event_paused_lose_keyboard_focus 133 | 134 | .. automethod:: sge.dsp.Game.event_paused_gain_mouse_focus 135 | 136 | .. automethod:: sge.dsp.Game.event_paused_lose_mouse_focus 137 | 138 | .. automethod:: sge.dsp.Game.event_paused_window_resize 139 | 140 | .. automethod:: sge.dsp.Game.event_paused_close 141 | 142 | sge.dsp.Room 143 | ------------ 144 | 145 | .. autoclass:: sge.dsp.Room 146 | 147 | .. automethod:: sge.dsp.Room.__init__ 148 | 149 | sge.dsp.Room Methods 150 | ~~~~~~~~~~~~~~~~~~~~ 151 | 152 | .. automethod:: sge.dsp.Room.add 153 | 154 | .. automethod:: sge.dsp.Room.remove 155 | 156 | .. automethod:: sge.dsp.Room.start 157 | 158 | .. automethod:: sge.dsp.Room.get_objects_at 159 | 160 | .. automethod:: sge.dsp.Room.project_dot 161 | 162 | .. automethod:: sge.dsp.Room.project_line 163 | 164 | .. automethod:: sge.dsp.Room.project_rectangle 165 | 166 | .. automethod:: sge.dsp.Room.project_ellipse 167 | 168 | .. automethod:: sge.dsp.Room.project_circle 169 | 170 | .. automethod:: sge.dsp.Room.project_polyline 171 | 172 | .. automethod:: sge.dsp.Room.project_polygon 173 | 174 | .. automethod:: sge.dsp.Room.project_sprite 175 | 176 | .. automethod:: sge.dsp.Room.project_text 177 | 178 | sge.dsp.Room Event Methods 179 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 180 | 181 | .. automethod:: sge.dsp.Room.event_room_start 182 | 183 | .. automethod:: sge.dsp.Room.event_room_resume 184 | 185 | .. automethod:: sge.dsp.Room.event_room_end 186 | 187 | .. automethod:: sge.dsp.Room.event_step 188 | 189 | .. automethod:: sge.dsp.Room.event_alarm 190 | 191 | .. automethod:: sge.dsp.Room.event_key_press 192 | 193 | .. automethod:: sge.dsp.Room.event_key_release 194 | 195 | .. automethod:: sge.dsp.Room.event_mouse_move 196 | 197 | .. automethod:: sge.dsp.Room.event_mouse_button_press 198 | 199 | .. automethod:: sge.dsp.Room.event_mouse_button_release 200 | 201 | .. automethod:: sge.dsp.Room.event_mouse_wheel_move 202 | 203 | .. automethod:: sge.dsp.Room.event_joystick 204 | 205 | .. automethod:: sge.dsp.Room.event_joystick_axis_move 206 | 207 | .. automethod:: sge.dsp.Room.event_joystick_hat_move 208 | 209 | .. automethod:: sge.dsp.Room.event_joystick_trackball_move 210 | 211 | .. automethod:: sge.dsp.Room.event_joystick_button_press 212 | 213 | .. automethod:: sge.dsp.Room.event_joystick_button_release 214 | 215 | .. automethod:: sge.dsp.Room.event_gain_keyboard_focus 216 | 217 | .. automethod:: sge.dsp.Room.event_lose_keyboard_focus 218 | 219 | .. automethod:: sge.dsp.Room.event_gain_mouse_focus 220 | 221 | .. automethod:: sge.dsp.Room.event_lose_mouse_focus 222 | 223 | .. automethod:: sge.dsp.Room.event_close 224 | 225 | .. automethod:: sge.dsp.Room.event_paused_step 226 | 227 | .. automethod:: sge.dsp.Room.event_paused_key_press 228 | 229 | .. automethod:: sge.dsp.Room.event_paused_key_release 230 | 231 | .. automethod:: sge.dsp.Room.event_paused_mouse_move 232 | 233 | .. automethod:: sge.dsp.Room.event_paused_mouse_button_press 234 | 235 | .. automethod:: sge.dsp.Room.event_paused_mouse_button_release 236 | 237 | .. automethod:: sge.dsp.Room.event_paused_mouse_wheel_move 238 | 239 | .. automethod:: sge.dsp.Room.event_paused_joystick 240 | 241 | .. automethod:: sge.dsp.Room.event_paused_joystick_axis_move 242 | 243 | .. automethod:: sge.dsp.Room.event_paused_joystick_hat_move 244 | 245 | .. automethod:: sge.dsp.Room.event_paused_joystick_trackball_move 246 | 247 | .. automethod:: sge.dsp.Room.event_paused_joystick_button_press 248 | 249 | .. automethod:: sge.dsp.Room.event_paused_joystick_button_release 250 | 251 | .. automethod:: sge.dsp.Room.event_paused_gain_keyboard_focus 252 | 253 | .. automethod:: sge.dsp.Room.event_paused_lose_keyboard_focus 254 | 255 | .. automethod:: sge.dsp.Room.event_paused_gain_mouse_focus 256 | 257 | .. automethod:: sge.dsp.Room.event_paused_lose_mouse_focus 258 | 259 | .. automethod:: sge.dsp.Room.event_paused_close 260 | 261 | sge.dsp.View 262 | ------------ 263 | 264 | .. autoclass:: sge.dsp.View 265 | 266 | .. automethod:: sge.dsp.View.__init__ 267 | 268 | sge.dsp.Object 269 | -------------- 270 | 271 | .. autoclass:: sge.dsp.Object 272 | 273 | .. automethod:: sge.dsp.Object.__init__ 274 | 275 | sge.dsp.Object Methods 276 | ~~~~~~~~~~~~~~~~~~~~~~ 277 | 278 | .. automethod:: sge.dsp.Object.move_x 279 | 280 | .. automethod:: sge.dsp.Object.move_y 281 | 282 | .. automethod:: sge.dsp.Object.collision 283 | 284 | .. automethod:: sge.dsp.Object.destroy 285 | 286 | .. automethod:: sge.dsp.Object.create 287 | 288 | sge.dsp.Object Event Methods 289 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 290 | 291 | .. automethod:: sge.dsp.Object.event_create 292 | 293 | .. automethod:: sge.dsp.Object.event_destroy 294 | 295 | .. automethod:: sge.dsp.Object.event_step 296 | 297 | .. automethod:: sge.dsp.Object.event_alarm 298 | 299 | .. automethod:: sge.dsp.Object.event_animation_end 300 | 301 | .. automethod:: sge.dsp.Object.event_key_press 302 | 303 | .. automethod:: sge.dsp.Object.event_key_release 304 | 305 | .. automethod:: sge.dsp.Object.event_mouse_move 306 | 307 | .. automethod:: sge.dsp.Object.event_mouse_button_press 308 | 309 | .. automethod:: sge.dsp.Object.event_mouse_button_release 310 | 311 | .. automethod:: sge.dsp.Object.event_mouse_wheel_move 312 | 313 | .. automethod:: sge.dsp.Object.event_joystick 314 | 315 | .. automethod:: sge.dsp.Object.event_joystick_axis_move 316 | 317 | .. automethod:: sge.dsp.Object.event_joystick_hat_move 318 | 319 | .. automethod:: sge.dsp.Object.event_joystick_trackball_move 320 | 321 | .. automethod:: sge.dsp.Object.event_joystick_button_press 322 | 323 | .. automethod:: sge.dsp.Object.event_joystick_button_release 324 | 325 | .. automethod:: sge.dsp.Object.event_update_position 326 | 327 | .. automethod:: sge.dsp.Object.event_collision 328 | 329 | .. automethod:: sge.dsp.Object.event_paused_step 330 | 331 | .. automethod:: sge.dsp.Object.event_paused_key_press 332 | 333 | .. automethod:: sge.dsp.Object.event_paused_key_release 334 | 335 | .. automethod:: sge.dsp.Object.event_paused_mouse_move 336 | 337 | .. automethod:: sge.dsp.Object.event_paused_mouse_button_press 338 | 339 | .. automethod:: sge.dsp.Object.event_paused_mouse_button_release 340 | 341 | .. automethod:: sge.dsp.Object.event_paused_mouse_wheel_move 342 | 343 | .. automethod:: sge.dsp.Object.event_paused_joystick 344 | 345 | .. automethod:: sge.dsp.Object.event_paused_joystick_axis_move 346 | 347 | .. automethod:: sge.dsp.Object.event_paused_joystick_hat_move 348 | 349 | .. automethod:: sge.dsp.Object.event_paused_joystick_trackball_move 350 | 351 | .. automethod:: sge.dsp.Object.event_paused_joystick_button_press 352 | 353 | .. automethod:: sge.dsp.Object.event_paused_joystick_button_release 354 | 355 | sge.dsp Functions 356 | ================= 357 | 358 | .. autofunction:: sge.dsp.list_fullscreen_modes 359 | 360 | .. autofunction:: sge.dsp.fullscreen_mode_ok 361 | 362 | -------------------------------------------------------------------------------- /doc-src/gfx.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | sge.gfx 3 | ******* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.gfx 13 | 14 | sge.gfx Classes 15 | =============== 16 | 17 | sge.gfx.Color 18 | ------------- 19 | 20 | .. autoclass:: sge.gfx.Color 21 | 22 | .. automethod:: sge.gfx.Color.__init__ 23 | 24 | sge.gfx.Sprite 25 | -------------- 26 | 27 | .. autoclass:: sge.gfx.Sprite 28 | 29 | .. automethod:: sge.gfx.Sprite.__init__ 30 | 31 | sge.gfx.Sprite Methods 32 | ~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | .. automethod:: sge.gfx.Sprite.append_frame 35 | 36 | .. automethod:: sge.gfx.Sprite.insert_frame 37 | 38 | .. automethod:: sge.gfx.Sprite.extend 39 | 40 | .. automethod:: sge.gfx.Sprite.delete_frame 41 | 42 | .. automethod:: sge.gfx.Sprite.get_pixel 43 | 44 | .. automethod:: sge.gfx.Sprite.get_pixels 45 | 46 | .. automethod:: sge.gfx.Sprite.draw_dot 47 | 48 | .. automethod:: sge.gfx.Sprite.draw_line 49 | 50 | .. automethod:: sge.gfx.Sprite.draw_rectangle 51 | 52 | .. automethod:: sge.gfx.Sprite.draw_ellipse 53 | 54 | .. automethod:: sge.gfx.Sprite.draw_circle 55 | 56 | .. automethod:: sge.gfx.Sprite.draw_polyline 57 | 58 | .. automethod:: sge.gfx.Sprite.draw_polygon 59 | 60 | .. automethod:: sge.gfx.Sprite.draw_sprite 61 | 62 | .. automethod:: sge.gfx.Sprite.draw_text 63 | 64 | .. automethod:: sge.gfx.Sprite.draw_shader 65 | 66 | .. automethod:: sge.gfx.Sprite.draw_erase 67 | 68 | .. automethod:: sge.gfx.Sprite.draw_clear 69 | 70 | .. automethod:: sge.gfx.Sprite.draw_lock 71 | 72 | .. automethod:: sge.gfx.Sprite.draw_unlock 73 | 74 | .. automethod:: sge.gfx.Sprite.mirror 75 | 76 | .. automethod:: sge.gfx.Sprite.flip 77 | 78 | .. automethod:: sge.gfx.Sprite.resize_canvas 79 | 80 | .. automethod:: sge.gfx.Sprite.scale 81 | 82 | .. automethod:: sge.gfx.Sprite.rotate 83 | 84 | .. automethod:: sge.gfx.Sprite.swap_color 85 | 86 | .. automethod:: sge.gfx.Sprite.copy 87 | 88 | .. automethod:: sge.gfx.Sprite.get_spritelist 89 | 90 | .. automethod:: sge.gfx.Sprite.save 91 | 92 | .. automethod:: sge.gfx.Sprite.from_tween 93 | 94 | .. automethod:: sge.gfx.Sprite.from_text 95 | 96 | .. automethod:: sge.gfx.Sprite.from_tileset 97 | 98 | .. automethod:: sge.gfx.Sprite.from_screenshot 99 | 100 | sge.gfx.Font 101 | ------------ 102 | 103 | .. autoclass:: sge.gfx.Font 104 | 105 | .. automethod:: sge.gfx.Font.__init__ 106 | 107 | sge.gfx.Font Methods 108 | ~~~~~~~~~~~~~~~~~~~~ 109 | 110 | .. automethod:: sge.gfx.Font.get_width 111 | 112 | .. automethod:: sge.gfx.Font.get_height 113 | 114 | .. automethod:: sge.gfx.Font.from_sprite 115 | 116 | sge.gfx.BackgroundLayer 117 | ----------------------- 118 | 119 | .. autoclass:: sge.gfx.BackgroundLayer 120 | 121 | .. automethod:: sge.gfx.BackgroundLayer.__init__ 122 | 123 | sge.gfx.Background 124 | ------------------ 125 | 126 | .. autoclass:: sge.gfx.Background 127 | 128 | .. automethod:: sge.gfx.Background.__init__ 129 | -------------------------------------------------------------------------------- /doc-src/hello_world.rst: -------------------------------------------------------------------------------- 1 | ************************* 2 | Tutorial 1: Hello, world! 3 | ************************* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | The easiest way to learn something new is with an example. We will 13 | start with a very basic example: the traditional "Hello, world!" 14 | program. This example will just project "Hello, world!" onto the 15 | screen. 16 | 17 | Setting Up a Project 18 | ==================== 19 | 20 | First, we must create our project directory. I will use "~/hello". 21 | 22 | Next, create the game source file inside "~/hello". I am calling it 23 | "hello.py". 24 | 25 | Open hello.py so you can start editing it. 26 | 27 | Shebang 28 | ------- 29 | 30 | All Python files which are supposed to be executed should start with 31 | a shebang, which is a line that tells POSIX systems (such as GNU/Linux 32 | systems, BSD, and OS X) how to execute the file. For Python 3, the 33 | version of Python we will be using, the shebang is:: 34 | 35 | #!/usr/bin/env python3 36 | 37 | The shebang should be the very first line of the file. You should also 38 | make sure that the file itself uses Unix-style line endings ("\\n"); 39 | this can be done in most text editors via a drop-down list available 40 | when you save, and is done by IDLE automatically. Windows-style line 41 | endings ("\\r\\n") are often interpreted wrongly in POSIX systems, which 42 | defeats the purpose of the shebang. 43 | 44 | License 45 | ------- 46 | 47 | The file is copyrighted by default, so if you do not give the file a 48 | license, it will be illegal for anyone to copy and share the program. 49 | You should always choose a free/libre software license for your 50 | programs. In this example, I will use CC0, which is a public domain 51 | dedication tool. You can use CC0 if you want, or you can choose another 52 | license. You can learn about various free/libre software licenses at 53 | `http://gnu.org/licenses/ `_. 54 | 55 | The license text I am using for CC0 is:: 56 | 57 | # Hello, world! 58 | # 59 | # To the extent possible under law, the author(s) have dedicated all 60 | # copyright and related and neighboring rights to this software to the 61 | # public domain worldwide. This software is distributed without any 62 | # warranty. 63 | # 64 | # You should have received a copy of the CC0 Public Domain Dedication 65 | # along with this software. If not, see 66 | # . 67 | 68 | Place your license text just under the shebang so that it is prominent. 69 | 70 | Imports 71 | ------- 72 | 73 | Because we are using the SGE, we must import the ``sge`` library. Add 74 | the following line:: 75 | 76 | import sge 77 | 78 | Adding Game Logic 79 | ================= 80 | 81 | The Game Class 82 | -------------- 83 | 84 | In SGE games, everything is controlled by a "game" object. The game 85 | object controls everything at the global level, including global events. 86 | To define global events, we need to subclass :class:`sge.dsp.Game` and 87 | create our own game class. We can just call this class ``Game``:: 88 | 89 | class Game(sge.dsp.Game): 90 | 91 | def event_key_press(self, key, char): 92 | if key == 'escape': 93 | self.event_close() 94 | 95 | def event_close(self): 96 | self.end() 97 | 98 | Because our example is simple, we only need to define two events: the 99 | close event, which occurs when the OS tells the game to close (most 100 | typically when a close button is clicked on), and the key press event, 101 | which occurs when a key is pressed. We want the game to end if either 102 | the OS tells it to close or the Esc key is pressed. 103 | 104 | As you may have noticed, we define events by defining certain methods; 105 | in our case, we defined methods to override the 106 | :meth:`sge.dsp.Game.event_key_press` and 107 | :meth:`sge.dsp.Game.event_close` methods. 108 | 109 | Our definition of :meth:`event_close` is simple enough: we just call 110 | :meth:`sge.dsp.Game.end`, which ends the game. Our definition of 111 | :meth:`event_key_press` is slightly more complicated; first we have to 112 | check what key was pressed, indicated by the ``key`` argument. If the 113 | key is the Esc key, we call our :meth:`event_close` method. The reason 114 | for calling :meth:`event_close` instead of just calling :meth:`end` is 115 | simple: in the future, we might want to do more than just call 116 | :meth:`end`; perhaps, for example, we decide that we want to add a 117 | confirmation dialog before actually quitting. By connecting the key 118 | press event to the close event, if we do change what the close event 119 | does, that change will also translate to the pressing of the Esc key, 120 | avoiding needless duplication of work. 121 | 122 | The Room Class 123 | -------------- 124 | 125 | Rooms are distinguished places where things happen; for example, each 126 | level in a game would typically be its own room, the title screen might 127 | be a room, the credits screen might be a room, and the options menu 128 | might be a room. In this example, we are only going to have one room, 129 | and this room is going to serve only one function: display "Hello, 130 | world!" in the center of the screen. This will be our room class:: 131 | 132 | class Room(sge.dsp.Room): 133 | 134 | def event_step(self, time_passed, delta_mult): 135 | sge.game.project_text(font, "Hello, world!", sge.game.width / 2, 136 | sge.game.height / 2, 137 | color=sge.gfx.Color("black"), halign="center", 138 | valign="middle") 139 | 140 | You can see that the room class is defined very similarly to the game 141 | class. We subclass :class:`sge.dsp.Room` and add a method to override 142 | :meth:`sge.dsp.Room.event_step`, which defines the step event of our 143 | room class. The step event happens over and over again, once every 144 | "frame". You can think of frames as being like the frames in a video; 145 | each frame makes small changes to the image on the screen and then gives 146 | you the new image in a fraction of a second, providing an illusion of 147 | movement. 148 | 149 | To display "Hello, world!" onto the screen, we use 150 | :meth:`sge.dsp.Game.project_text`, which instantly displays any text we 151 | want onto the screen. :data:`sge.game` is a variable that always points 152 | to the :class:`sge.dsp.Game` object currently in use. 153 | 154 | The first argument of this method is the font to use; we don't have a 155 | font yet, but we are going to define one later and assign it to 156 | ``font``. Next is the text to display, which for us is 157 | ``"Hello, world!"``. 158 | 159 | The next arguments are the horizontal and vertical location of the text 160 | on the screen; we set these to half of the game's width and height, 161 | respectively, to place the text in the center. 162 | 163 | Now that all required arguments are defined, we are going to define the 164 | color of the text as a keyword argument, setting it explicitly to black. 165 | 166 | Finally, we define ``halign`` and ``valign`` as keyword arguments; these 167 | arguments specify the horizontal and vertical alignment of the text, 168 | respectively. 169 | 170 | You might be wondering: why do we keep doing this every frame? Can't we 171 | just do it once, since we're not changing the image? In fact, we can't. 172 | :meth:`sge.dsp.Game.project_text` shows our text, but it only does so 173 | for one frame. You can think of it as working like a movie projector: 174 | if you keep the projector on, you will continue to see the image, but as 175 | soon as the projector stops projecting the image, you can no longer see 176 | the image from the projector. :meth:`sge.dsp.Game.project_text` and 177 | other similar projection methods work the same way. 178 | 179 | Starting the Game 180 | ================= 181 | 182 | If you try to run hello.py now, you will notice that nothing happens. 183 | This is because, while we defined the game logic, we didn't actually 184 | execute it. 185 | 186 | Additionally, we are still missing a resource: the font object we want 187 | to use to project text onto the screen. We need to load this resource. 188 | 189 | We are going to fix both of these problems by adding some code after our 190 | class definitions:: 191 | 192 | # Create Game object 193 | Game() 194 | 195 | # Create backgrounds 196 | background = sge.gfx.Background([], sge.gfx.Color("white")) 197 | 198 | # Load fonts 199 | font = sge.gfx.Font() 200 | 201 | # Create rooms 202 | sge.game.start_room = Room(background=background) 203 | 204 | if __name__ == '__main__': 205 | sge.game.start() 206 | 207 | First, we create a :class:`sge.dsp.Game` object; we don't need to store 208 | it in anything since it is automatically stored in :data:`sge.game`. 209 | 210 | Second, we create a :class:`sge.gfx.Background` object to specify what 211 | the background looks like. We make our background all white, with no 212 | layers. (Layers are used to give backgrounds more than a solid color, 213 | which we don't need.) 214 | 215 | Third, we create our font. We don't really care what this font looks 216 | like, so we allow the SGE to pick a font. If you do care what font is 217 | used, you can pass the name of a font onto the ``name`` keyword 218 | argument. 219 | 220 | Fourth, we create a room. The only argument we pass is the background 221 | argument; we set this to the background we created earlier. Since it is 222 | the room that we are going to start the game with, we need to assign 223 | this room to the special attribute, :attr:`sge.game.start_room`, which 224 | indicates the room that the game starts with. 225 | 226 | Finally, with everything in place, we call the 227 | :meth:`sge.dsp.Game.start` method of our game object. This executes all 228 | the game logic we defined earlier. However, we only do this if the 229 | special Python variable, :data:`__name__`, is set to ``"__main__"``, 230 | which means that the current module is the main module, i.e. was 231 | executed rather than imported. It is a good practice to include this 232 | distinction between being executed and being imported in all of your 233 | Python scripts. 234 | 235 | The Final Result 236 | ================ 237 | 238 | That's it! If you execute the script now, you will see a white screen 239 | with black text in the center reading "Hello, world!" Pressing the Esc 240 | key or clicking on the close button in the window will close the 241 | program. Congratulations on writing your first SGE program! 242 | 243 | This is the completed Hello World program:: 244 | 245 | #!/usr/bin/env python3 246 | 247 | # Hello, world! 248 | # 249 | # To the extent possible under law, the author(s) have dedicated all 250 | # copyright and related and neighboring rights to this software to the 251 | # public domain worldwide. This software is distributed without any 252 | # warranty. 253 | # 254 | # You should have received a copy of the CC0 Public Domain Dedication 255 | # along with this software. If not, see 256 | # . 257 | 258 | import sge 259 | 260 | 261 | class Game(sge.dsp.Game): 262 | 263 | def event_key_press(self, key, char): 264 | if key == 'escape': 265 | self.event_close() 266 | 267 | def event_close(self): 268 | self.end() 269 | 270 | 271 | class Room(sge.dsp.Room): 272 | 273 | def event_step(self, time_passed, delta_mult): 274 | sge.game.project_text(font, "Hello, world!", sge.game.width / 2, 275 | sge.game.height / 2, 276 | color=sge.gfx.Color("black"), halign="center", 277 | valign="middle") 278 | 279 | 280 | # Create Game object 281 | Game() 282 | 283 | # Create backgrounds 284 | background = sge.gfx.Background([], sge.gfx.Color("white")) 285 | 286 | # Load fonts 287 | font = sge.gfx.Font() 288 | 289 | # Create rooms 290 | sge.game.start_room = Room(background=background) 291 | 292 | if __name__ == '__main__': 293 | sge.game.start() 294 | -------------------------------------------------------------------------------- /doc-src/index.rst: -------------------------------------------------------------------------------- 1 | *********************************** 2 | Seclusion Game Engine documentation 3 | *********************************** 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | sge 14 | hello_world 15 | pong 16 | pong_better 17 | input 18 | dsp 19 | gfx 20 | snd 21 | collision 22 | joystick 23 | keyboard 24 | mouse 25 | s 26 | 27 | Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | 34 | -------------------------------------------------------------------------------- /doc-src/input.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | sge.input 3 | ********* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.input 13 | 14 | Input Event Classes 15 | =================== 16 | 17 | .. autoclass:: sge.input.KeyPress 18 | 19 | .. autoclass:: sge.input.KeyRelease 20 | 21 | .. autoclass:: sge.input.MouseMove 22 | 23 | .. autoclass:: sge.input.MouseButtonPress 24 | 25 | .. autoclass:: sge.input.MouseButtonRelease 26 | 27 | .. autoclass:: sge.input.MouseWheelMove 28 | 29 | .. autoclass:: sge.input.JoystickAxisMove 30 | 31 | .. autoclass:: sge.input.JoystickHatMove 32 | 33 | .. autoclass:: sge.input.JoystickTrackballMove 34 | 35 | .. autoclass:: sge.input.JoystickButtonPress 36 | 37 | .. autoclass:: sge.input.JoystickButtonRelease 38 | 39 | .. autoclass:: sge.input.KeyboardFocusGain 40 | 41 | .. autoclass:: sge.input.KeyboardFocusLose 42 | 43 | .. autoclass:: sge.input.MouseFocusGain 44 | 45 | .. autoclass:: sge.input.MouseFocusLose 46 | 47 | .. autoclass:: sge.input.WindowResize 48 | 49 | .. autoclass:: sge.input.QuitRequest 50 | -------------------------------------------------------------------------------- /doc-src/joystick.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | sge.joystick 3 | ************ 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.joystick 13 | 14 | sge.joystick Functions 15 | ====================== 16 | 17 | .. autofunction:: sge.joystick.refresh 18 | 19 | .. autofunction:: sge.joystick.get_axis 20 | 21 | .. autofunction:: sge.joystick.get_hat_x 22 | 23 | .. autofunction:: sge.joystick.get_hat_y 24 | 25 | .. autofunction:: sge.joystick.get_pressed 26 | 27 | .. autofunction:: sge.joystick.get_joysticks 28 | 29 | .. autofunction:: sge.joystick.get_name 30 | 31 | .. autofunction:: sge.joystick.get_id 32 | 33 | .. autofunction:: sge.joystick.get_axes 34 | 35 | .. autofunction:: sge.joystick.get_hats 36 | 37 | .. autofunction:: sge.joystick.get_trackballs 38 | 39 | .. autofunction:: sge.joystick.get_buttons 40 | -------------------------------------------------------------------------------- /doc-src/keyboard.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | sge.keyboard 3 | ************ 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.keyboard 13 | 14 | sge.keyboard Functions 15 | ====================== 16 | 17 | .. autofunction:: sge.keyboard.get_pressed 18 | 19 | .. autofunction:: sge.keyboard.get_modifier 20 | 21 | .. autofunction:: sge.keyboard.get_focused 22 | 23 | .. autofunction:: sge.keyboard.set_repeat 24 | 25 | .. autofunction:: sge.keyboard.get_repeat_enabled 26 | 27 | .. autofunction:: sge.keyboard.get_repeat_interval 28 | 29 | .. autofunction:: sge.keyboard.get_repeat_delay 30 | -------------------------------------------------------------------------------- /doc-src/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /doc-src/mouse.rst: -------------------------------------------------------------------------------- 1 | ********* 2 | sge.mouse 3 | ********* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.mouse 13 | 14 | sge.mouse Functions 15 | =================== 16 | 17 | .. autofunction:: sge.mouse.get_pressed 18 | 19 | .. autofunction:: sge.mouse.get_x 20 | 21 | .. autofunction:: sge.mouse.get_y 22 | 23 | .. autofunction:: sge.mouse.set_x 24 | 25 | .. autofunction:: sge.mouse.set_y 26 | -------------------------------------------------------------------------------- /doc-src/s.rst: -------------------------------------------------------------------------------- 1 | ***** 2 | sge.s 3 | ***** 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.s 13 | -------------------------------------------------------------------------------- /doc-src/sge.rst: -------------------------------------------------------------------------------- 1 | **************** 2 | SGE Fundamentals 3 | **************** 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge 13 | -------------------------------------------------------------------------------- /doc-src/snd.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | sge.snd 3 | ******* 4 | 5 | .. This file has been dedicated to the public domain, to the extent 6 | possible under applicable law, via CC0. See 7 | http://creativecommons.org/publicdomain/zero/1.0/ for more 8 | information. This file is offered as-is, without any warranty. 9 | 10 | .. contents:: 11 | 12 | .. automodule:: sge.snd 13 | 14 | sge.snd Classes 15 | =============== 16 | 17 | sge.snd.Sound 18 | ------------- 19 | 20 | .. autoclass:: sge.snd.Sound 21 | 22 | .. automethod:: sge.snd.Sound.__init__ 23 | 24 | sge.snd.Sound Methods 25 | ~~~~~~~~~~~~~~~~~~~~~ 26 | 27 | .. automethod:: sge.snd.Sound.play 28 | 29 | .. automethod:: sge.snd.Sound.stop 30 | 31 | .. automethod:: sge.snd.Sound.pause 32 | 33 | .. automethod:: sge.snd.Sound.unpause 34 | 35 | sge.snd.Music 36 | ------------- 37 | 38 | .. autoclass:: sge.snd.Music 39 | 40 | .. automethod:: sge.snd.Music.__init__ 41 | 42 | sge.snd.Music Methods 43 | ~~~~~~~~~~~~~~~~~~~~~ 44 | 45 | .. automethod:: sge.snd.Music.play 46 | 47 | .. automethod:: sge.snd.Music.queue 48 | 49 | .. automethod:: sge.snd.Music.stop 50 | 51 | .. automethod:: sge.snd.Music.pause 52 | 53 | .. automethod:: sge.snd.Music.unpause 54 | 55 | .. automethod:: sge.snd.Music.clear_queue 56 | 57 | sge.snd Functions 58 | ================= 59 | 60 | .. autofunction:: sge.snd.stop_all 61 | 62 | -------------------------------------------------------------------------------- /examples/cc0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /examples/circle_popper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Circle Popper 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | 23 | 24 | class glob(object): 25 | 26 | circle_sprite = None 27 | circle_pop_sprite = None 28 | font = None 29 | pop_sound = None 30 | music = None 31 | 32 | 33 | class Game(sge.dsp.Game): 34 | 35 | def event_step(self, time_passed, delta_mult): 36 | self.fps_time += time_passed 37 | self.fps_frames += 1 38 | if self.fps_time >= 250: 39 | self.fps_text = str(round((1000 * self.fps_frames) / 40 | self.fps_time)) 41 | self.fps_time = 0 42 | self.fps_frames = 0 43 | 44 | self.project_text(glob.font, self.fps_text, self.width - 8, 8, 45 | color=sge.gfx.Color("#000"), halign="right") 46 | self.project_polyline([(428.2, 328), (424, 460.1), (460, 340.9)], 47 | sge.gfx.Color("red"), thickness=3.1) 48 | 49 | def event_key_press(self, key, char): 50 | if key == 'escape': 51 | self.end() 52 | 53 | def event_close(self): 54 | self.end() 55 | 56 | 57 | class Circle(sge.dsp.Object): 58 | 59 | def __init__(self, x, y): 60 | super(Circle, self).__init__(x, y, 5, sprite=glob.circle_sprite, 61 | collision_precise=True) 62 | 63 | def event_create(self): 64 | self.image_alpha = 200 65 | if self.collision(sge.game.mouse): 66 | self.image_blend = sge.gfx.Color('#ff0000') 67 | else: 68 | self.image_blend = sge.gfx.Color('blue') 69 | 70 | def event_mouse_move(self, x, y): 71 | if self.collision(sge.game.mouse): 72 | self.image_blend = sge.gfx.Color("red") 73 | else: 74 | self.image_blend = sge.gfx.Color((0, 0, 255)) 75 | 76 | def event_mouse_button_press(self, button): 77 | if button == 'left': 78 | if self.collision(sge.game.mouse): 79 | self.destroy() 80 | 81 | def event_destroy(self): 82 | pop = CirclePop(self.x, self.y) 83 | pop.image_blend = self.image_blend 84 | sge.game.current_room.add(pop) 85 | assert glob.pop_sound is not None 86 | glob.pop_sound.play() 87 | 88 | 89 | class CirclePop(sge.dsp.Object): 90 | 91 | def __init__(self, x, y): 92 | super(CirclePop, self).__init__(x, y, 5, sprite=glob.circle_pop_sprite, 93 | tangible=False) 94 | 95 | def event_animation_end(self): 96 | self.destroy() 97 | 98 | def event_destroy(self): 99 | circle = Circle(random.randint(0, sge.game.width), 100 | random.randint(0, sge.game.height)) 101 | sge.game.current_room.add(circle) 102 | 103 | 104 | class Room(sge.dsp.Room): 105 | 106 | def event_room_start(self): 107 | self.shake = 0 108 | self.event_room_resume() 109 | 110 | def event_room_resume(self): 111 | glob.music.play(loops=None) 112 | 113 | def event_step(self, time_passed, delta_mult): 114 | self.project_rectangle(5, 5, 3, 32, 32, fill=sge.gfx.Color("red"), 115 | outline=sge.gfx.Color("green"), 116 | outline_thickness=3.4) 117 | self.project_ellipse(16, 100, 3, 84, 64, fill=sge.gfx.Color("yellow"), 118 | outline=sge.gfx.Color("fuchsia"), 119 | outline_thickness=3.9, anti_alias=True) 120 | self.project_line(64, 64, 78, 100, 3, sge.gfx.Color("black"), 121 | thickness=2.2, anti_alias=True) 122 | self.project_dot(90, 32, 3, sge.gfx.Color("maroon")) 123 | self.project_dot(91, 32, 3, sge.gfx.Color("maroon")) 124 | self.project_dot(92, 32, 3, sge.gfx.Color("maroon")) 125 | self.project_dot(93, 32, 3, sge.gfx.Color("maroon")) 126 | self.project_dot(90, 33, 3, sge.gfx.Color("maroon")) 127 | self.project_dot(91, 33, 3, sge.gfx.Color("maroon")) 128 | self.project_dot(92, 33, 3, sge.gfx.Color("maroon")) 129 | self.project_dot(90, 34, 3, sge.gfx.Color("maroon")) 130 | self.project_dot(91, 34, 3, sge.gfx.Color("maroon")) 131 | self.project_dot(90, 35, 3, sge.gfx.Color("maroon")) 132 | self.project_polygon([(128.2, 128), (124, 160.1), (160, 140.9)], 3, 133 | fill=sge.gfx.Color("gray"), 134 | outline=sge.gfx.Color("red"), 135 | outline_thickness=3, anti_alias=True) 136 | self.project_polyline([(228.2, 128), (224, 260.1), (260, 140.9)], 5, 137 | sge.gfx.Color("red"), thickness=3.1, 138 | anti_alias=True) 139 | self.project_circle(500, 100, 3, 30, 140 | fill=sge.gfx.Color("gray"), 141 | outline=sge.gfx.Color("maroon"), 142 | outline_thickness=10, anti_alias=True) 143 | text = "I am amazing text!\n\nYaaaaaaaaaaay~!" 144 | self.project_text(glob.font, text, 320, 0, 3, 145 | color=sge.gfx.Color("black"), halign="center") 146 | self.project_text(glob.font, text, 320, 80, 3, 147 | color=sge.gfx.Color("white"), halign="center", 148 | outline=sge.gfx.Color("black"), 149 | outline_thickness=30.4) 150 | 151 | if sge.keyboard.get_pressed("left"): 152 | self.views[0].xport -= 1 153 | if sge.keyboard.get_pressed("right"): 154 | self.views[0].xport += 1 155 | if sge.keyboard.get_pressed("up"): 156 | self.views[0].yport -= 1 157 | if sge.keyboard.get_pressed("down"): 158 | self.views[0].yport += 1 159 | 160 | def event_alarm(self, alarm_id): 161 | if alarm_id == "shake": 162 | self.views[0].xport += random.uniform(-2, 2) 163 | self.views[0].yport += random.uniform(-2, 2) 164 | self.shake -= 1 165 | if self.shake > 0: 166 | self.alarms["shake"] = 1 167 | else: 168 | self.views[0].xport = 0 169 | self.views[0].yport = 0 170 | elif alarm_id == "shake_down": 171 | self.views[0].yport = 3 172 | self.alarms["shake_up"] = 1 173 | elif alarm_id == "shake_up": 174 | self.views[0].yport = 0 175 | self.shake -= 1 176 | if self.shake > 0: 177 | self.alarms["shake_down"] = 1 178 | 179 | def event_key_press(self, key, char): 180 | if key in ("ctrl_left", "ctrl_right"): 181 | self.shake = 20 182 | self.event_alarm("shake_down") 183 | elif key in ("shift_left", "shift_right"): 184 | self.shake = 20 185 | self.event_alarm("shake") 186 | 187 | 188 | def invert(x, y, red, green, blue, alpha): 189 | return 255 - red, 255 - green, 255 - blue, alpha 190 | 191 | 192 | def main(): 193 | # Create Game object 194 | game = Game(delta=True, delta_max=4800, collision_events_enabled=False) 195 | 196 | # Load sprites 197 | glob.circle_sprite = sge.gfx.Sprite('circle', DATA, width=64, height=64, 198 | origin_x=32, origin_y=32) 199 | glob.circle_pop_sprite = sge.gfx.Sprite('circle_pop', DATA, width=64, 200 | height=64, origin_x=32, 201 | origin_y=32, fps=60) 202 | fence_sprite = sge.gfx.Sprite('fence', DATA) 203 | fence_sprite.draw_shader(0, 0, fence_sprite.width, fence_sprite.height, 204 | invert) 205 | 206 | # Load backgrounds 207 | layers = [sge.gfx.BackgroundLayer(fence_sprite, 0, 380, 0, 208 | repeat_left=True, repeat_right=True)] 209 | background = sge.gfx.Background(layers, sge.gfx.Color(0xffffff)) 210 | 211 | # Load fonts 212 | glob.font = sge.gfx.Font('Liberation Serif', 20) 213 | 214 | # Load sounds 215 | glob.pop_sound = sge.snd.Sound(os.path.join(DATA, 'pop.ogg')) 216 | 217 | # Load music 218 | glob.music = sge.snd.Music(os.path.join(DATA, 'WhereWasI.ogg')) 219 | 220 | # Create objects 221 | circle = Circle(game.width // 2, game.height // 2) 222 | circle2 = Circle(22, 48) 223 | circle3 = Circle(486, 301) 224 | circle4 = Circle(50, 400) 225 | objects = [circle, circle2, circle3, circle4] 226 | 227 | # Create view 228 | views = [sge.dsp.View(0, 0)] 229 | 230 | # Create rooms 231 | game.start_room = Room(objects, views=views, background=background) 232 | 233 | game.fps_time = 0 234 | game.fps_frames = 0 235 | game.fps_text = "" 236 | 237 | game.start() 238 | 239 | 240 | if __name__ == '__main__': 241 | main() 242 | -------------------------------------------------------------------------------- /examples/data/LICENSES: -------------------------------------------------------------------------------- 1 | This file lists the licenses, authors, and sources for all the 2 | non-functional data files found in this directory. 3 | 4 | This file itself has been dedicated to the public domain, to the extent 5 | possible under applicable law, via CC0. See 6 | http://creativecommons.org/publicdomain/zero/1.0/ for more 7 | information. This file is offered as-is, without any warranty. 8 | 9 | ======================================================================== 10 | 11 | circle.png 12 | circle_pop-0.png 13 | circle_pop-1.png 14 | circle_pop-2.png 15 | circle_pop-3.png 16 | circle_pop-4.png 17 | circle_pop-5.png 18 | circle_pop-6.png 19 | circle_pop-7.png 20 | circle_pop-8.png 21 | circle_pop-9.png 22 | circle_pop-10.png 23 | circle_pop-11.png 24 | circle_pop-12.png 25 | fence.png 26 | light.png 27 | rotator.png 28 | bounce.wav 29 | bounce_wall.wav 30 | score.wav 31 | 32 | Author: Layla Marchant 33 | License: CC0 34 | 35 | ------------------------------------------------------------------------ 36 | 37 | pop.ogg 38 | 39 | Author: kddekadenz 40 | License: CC0 41 | Source: http://opengameart.org/content/stove-switch 42 | 43 | ------------------------------------------------------------------------ 44 | 45 | WhereWasI.ogg 46 | 47 | Author: yd 48 | License: CC0 49 | Source: http://opengameart.org/content/where-was-i 50 | -------------------------------------------------------------------------------- /examples/data/WhereWasI.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/WhereWasI.ogg -------------------------------------------------------------------------------- /examples/data/bounce.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/bounce.wav -------------------------------------------------------------------------------- /examples/data/bounce_wall.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/bounce_wall.wav -------------------------------------------------------------------------------- /examples/data/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle.png -------------------------------------------------------------------------------- /examples/data/circle_pop-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-0.png -------------------------------------------------------------------------------- /examples/data/circle_pop-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-1.png -------------------------------------------------------------------------------- /examples/data/circle_pop-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-10.png -------------------------------------------------------------------------------- /examples/data/circle_pop-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-11.png -------------------------------------------------------------------------------- /examples/data/circle_pop-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-12.png -------------------------------------------------------------------------------- /examples/data/circle_pop-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-2.png -------------------------------------------------------------------------------- /examples/data/circle_pop-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-3.png -------------------------------------------------------------------------------- /examples/data/circle_pop-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-4.png -------------------------------------------------------------------------------- /examples/data/circle_pop-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-5.png -------------------------------------------------------------------------------- /examples/data/circle_pop-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-6.png -------------------------------------------------------------------------------- /examples/data/circle_pop-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-7.png -------------------------------------------------------------------------------- /examples/data/circle_pop-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-8.png -------------------------------------------------------------------------------- /examples/data/circle_pop-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/circle_pop-9.png -------------------------------------------------------------------------------- /examples/data/fence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/fence.png -------------------------------------------------------------------------------- /examples/data/pop.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/pop.ogg -------------------------------------------------------------------------------- /examples/data/rotator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/rotator.png -------------------------------------------------------------------------------- /examples/data/score.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-sge/sge/0baf62526d505506185390a21387e204c1319c50/examples/data/score.wav -------------------------------------------------------------------------------- /examples/jstest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Joystick Tester 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import sge 16 | 17 | TITLE_HEIGHT = 48 18 | UPDATE_DELAY = 10 19 | 20 | 21 | class glob(object): 22 | 23 | js_selection_sprite = None 24 | js_state_sprite = None 25 | name_font = None 26 | state_font = None 27 | 28 | 29 | class Game(sge.dsp.Game): 30 | 31 | def event_key_press(self, key, char): 32 | if key == 'escape': 33 | self.event_close() 34 | 35 | def event_close(self): 36 | self.end() 37 | 38 | 39 | class Room(sge.dsp.Room): 40 | 41 | def set_joystick(self): 42 | self.joystick_axes = [] 43 | for i in range(sge.joystick.get_axes(self.current_joystick)): 44 | self.joystick_axes.append(sge.joystick.get_axis( 45 | self.current_joystick, i)) 46 | 47 | self.joystick_hats = [] 48 | for i in range(sge.joystick.get_hats(self.current_joystick)): 49 | self.joystick_hats.append(( 50 | sge.joystick.get_hat_x(self.current_joystick, i), 51 | sge.joystick.get_hat_y(self.current_joystick, i))) 52 | 53 | self.joystick_balls = [] 54 | for i in range(sge.joystick.get_trackballs(self.current_joystick)): 55 | self.joystick_balls.append(0) 56 | 57 | self.joystick_buttons = [] 58 | for i in range(sge.joystick.get_buttons(self.current_joystick)): 59 | self.joystick_buttons.append(sge.joystick.get_pressed( 60 | self.current_joystick, i)) 61 | 62 | glob.js_selection_sprite.draw_clear() 63 | 64 | title_text = 'Joystick {0} ("{1}")'.format( 65 | sge.joystick.get_id(self.current_joystick), 66 | sge.joystick.get_name(self.current_joystick)) 67 | 68 | x = glob.js_selection_sprite.width / 2 69 | y = glob.js_selection_sprite.height / 2 70 | glob.js_selection_sprite.draw_text( 71 | glob.name_font, title_text, x, y, color=sge.gfx.Color("white"), 72 | halign="center", valign="middle") 73 | 74 | self.print_state() 75 | 76 | def print_state(self): 77 | lines = [] 78 | 79 | for i in range(len(self.joystick_axes)): 80 | lines.append("Axis {0}: {1}".format(i, self.joystick_axes[i])) 81 | 82 | for i in range(len(self.joystick_hats)): 83 | lines.append("HAT {0}: {1}".format( 84 | i, "{0} x {1}".format(*self.joystick_hats[i]))) 85 | 86 | for i in range(len(self.joystick_balls)): 87 | lines.append("Trackball {0}: {1}".format( 88 | i, "{0} x {1}".format(*self.joystick_balls[i]))) 89 | 90 | for i in range(len(self.joystick_buttons)): 91 | lines.append("Button {0}: {1}".format( 92 | i, "Pressed" if self.joystick_buttons[i] else "Released")) 93 | 94 | left_text = '\n'.join([lines[i] for i in range(0, len(lines), 2)]) 95 | right_text = '\n'.join([lines[i] for i in range(1, len(lines), 2)]) 96 | 97 | glob.js_state_sprite.draw_clear() 98 | glob.js_state_sprite.draw_text(glob.state_font, left_text, 0, 0, 99 | color=sge.gfx.Color("white")) 100 | x = glob.js_state_sprite.width / 2 101 | glob.js_state_sprite.draw_text(glob.state_font, right_text, x, 0, 102 | color=sge.gfx.Color("white")) 103 | 104 | def event_room_start(self): 105 | self.current_joystick = 0 106 | self.changed = False 107 | self.ball_nonzero = False 108 | self.set_joystick() 109 | 110 | def event_step(self, time_passed, delta_mult): 111 | if self.changed: 112 | self.changed = False 113 | self.print_state() 114 | 115 | if self.ball_nonzero: 116 | # Reset ball motion to 0 117 | for i in range(len(self.joystick_balls)): 118 | self.joystick_balls[i] = (0, 0) 119 | 120 | self.changed = True 121 | self.ball_nonzero = False 122 | 123 | def event_key_press(self, key, char): 124 | if key == "left": 125 | num = sge.joystick.get_joysticks() 126 | if num: 127 | self.current_joystick -= 1 128 | self.current_joystick %= num 129 | self.set_joystick() 130 | elif key == "right": 131 | num = sge.joystick.get_joysticks() 132 | if num: 133 | self.current_joystick += 1 134 | self.current_joystick %= num 135 | self.set_joystick() 136 | elif key == "space": 137 | sge.joystick.refresh() 138 | self.set_joystick() 139 | 140 | def event_joystick_axis_move(self, js_name, js_id, axis, value): 141 | if (self.current_joystick in (js_name, js_id) and 142 | axis < len(self.joystick_axes)): 143 | self.joystick_axes[axis] = value 144 | 145 | self.changed = True 146 | 147 | def event_joystick_hat_move(self, js_name, js_id, hat, x, y): 148 | if (self.current_joystick in (js_name, js_id) and 149 | hat < len(self.joystick_hats)): 150 | self.joystick_hats[hat] = (x, y) 151 | 152 | self.changed = True 153 | 154 | def event_joystick_trackball_move(self, js_name, js_id, ball, x, y): 155 | if (self.current_joystick in (js_name, js_id) and 156 | ball < len(self.joystick_balls)): 157 | self.joystick_balls[ball] = (x, y) 158 | 159 | self.changed = True 160 | self.ball_nonzero = True 161 | 162 | def event_joystick_button_press(self, js_name, js_id, button): 163 | if (self.current_joystick in (js_name, js_id) and 164 | button < len(self.joystick_buttons)): 165 | self.joystick_buttons[button] = True 166 | 167 | self.changed = True 168 | 169 | def event_joystick_button_release(self, js_name, js_id, button): 170 | if (self.current_joystick in (js_name, js_id) and 171 | button < len(self.joystick_buttons)): 172 | self.joystick_buttons[button] = False 173 | 174 | self.changed = True 175 | 176 | 177 | def main(): 178 | # Create Game object 179 | Game(width=640, height=480) 180 | 181 | # Load sprites 182 | glob.js_selection_sprite = sge.gfx.Sprite(width=sge.game.width, 183 | height=TITLE_HEIGHT) 184 | glob.js_state_sprite = sge.gfx.Sprite( 185 | width=sge.game.width, height=(sge.game.height - TITLE_HEIGHT)) 186 | 187 | # Load fonts 188 | glob.name_font = sge.gfx.Font('Liberation Sans', size=18) 189 | glob.state_font = sge.gfx.Font('Liberation Sans', size=14) 190 | 191 | # Create objects 192 | selection_object = sge.dsp.Object(0, 0, sprite=glob.js_selection_sprite, 193 | tangible=False) 194 | state_object = sge.dsp.Object(0, TITLE_HEIGHT, 195 | sprite=glob.js_state_sprite, 196 | tangible=False) 197 | objects = (selection_object, state_object) 198 | 199 | # Create rooms 200 | sge.game.start_room = Room(objects) 201 | 202 | sge.game.start() 203 | 204 | 205 | if __name__ == '__main__': 206 | main() 207 | -------------------------------------------------------------------------------- /examples/large_room.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Large Room Example 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | 23 | 24 | class Game(sge.dsp.Game): 25 | def event_key_press(self, key, char): 26 | if key == 'escape': 27 | self.end() 28 | elif key == "backspace": 29 | view = self.current_room.views[0] 30 | view.width = view.wport 31 | view.height = view.hport 32 | 33 | def event_close(self): 34 | self.end() 35 | 36 | 37 | class Circle(sge.dsp.Object): 38 | def __init__(self, x, y): 39 | super(Circle, self).__init__(x, y, 1, sprite=circle_sprite, 40 | collision_precise=True, 41 | image_blend=sge.gfx.Color("red"), 42 | image_alpha=128) 43 | 44 | def event_step(self, time_passed, delta_mult): 45 | left_key = 'left' 46 | right_key = 'right' 47 | up_key = 'up' 48 | down_key = 'down' 49 | self.xvelocity = (sge.keyboard.get_pressed(right_key) - 50 | sge.keyboard.get_pressed(left_key)) 51 | self.yvelocity = (sge.keyboard.get_pressed(down_key) - 52 | sge.keyboard.get_pressed(up_key)) 53 | 54 | # Limit the circles to inside the room. 55 | if self.bbox_left < 0: 56 | self.bbox_left = 0 57 | elif self.bbox_right >= sge.game.current_room.width: 58 | self.bbox_right = sge.game.current_room.width - 1 59 | if self.bbox_top < 0: 60 | self.bbox_top = 0 61 | elif self.bbox_bottom >= sge.game.current_room.height: 62 | self.bbox_bottom = sge.game.current_room.height - 1 63 | 64 | # Set view 65 | my_view = sge.game.current_room.views[0] 66 | zoom_add = (sge.keyboard.get_pressed("hyphen") - 67 | sge.keyboard.get_pressed("equals")) 68 | my_view.width += zoom_add 69 | my_view.width = max(48, min(my_view.width, sge.game.current_room.width)) 70 | my_view.height = my_view.width 71 | my_view.x = self.x - (my_view.width // 2) 72 | my_view.y = self.y - (my_view.height // 2) 73 | 74 | 75 | def main(): 76 | global circle_sprite 77 | 78 | # Create Game object 79 | Game(width=240, height=240, scale_method="smooth", 80 | collision_events_enabled=False) 81 | 82 | # Load sprites 83 | circle_sprite = sge.gfx.Sprite('circle', DATA, width=32, height=32, 84 | origin_x=16, origin_y=16) 85 | fence = sge.gfx.Sprite('fence', DATA) 86 | 87 | # Load backgrounds 88 | layers = [sge.gfx.BackgroundLayer(fence, 0, 0, 0, repeat_left=True, 89 | repeat_right=True, repeat_up=True, 90 | repeat_down=True)] 91 | background = sge.gfx.Background(layers, sge.gfx.Color('white')) 92 | 93 | # Create objects 94 | circle = Circle(random.randrange(0, 640), random.randrange(0, 480)) 95 | objects = [circle] 96 | 97 | # Create views 98 | views = [sge.dsp.View(0, 0, 0, 0, 240, 240, 240, 240)] 99 | 100 | # Create rooms 101 | sge.game.start_room = sge.dsp.Room(objects, width=640, height=640, 102 | views=views, background=background) 103 | 104 | sge.game.start() 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | 110 | -------------------------------------------------------------------------------- /examples/pong.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Pong Example 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | PADDLE_XOFFSET = 32 23 | PADDLE_SPEED = 4 24 | PADDLE_VERTICAL_FORCE = 1 / 12 25 | BALL_START_SPEED = 2 26 | BALL_ACCELERATION = 0.2 27 | BALL_MAX_SPEED = 15 28 | POINTS_TO_WIN = 10 29 | TEXT_OFFSET = 16 30 | 31 | game_in_progress = True 32 | 33 | 34 | class Game(sge.dsp.Game): 35 | 36 | def event_step(self, time_passed, delta_mult): 37 | self.project_sprite(hud_sprite, 0, self.width / 2, 0) 38 | 39 | self.fps_time += time_passed 40 | self.fps_frames += 1 41 | if self.fps_time >= 250: 42 | self.fps_text = str(round((1000 * self.fps_frames) / 43 | self.fps_time)) 44 | self.fps_time = 0 45 | self.fps_frames = 0 46 | 47 | self.project_text(hud_font, self.fps_text, 8, 8, 48 | color=sge.gfx.Color("gray")) 49 | 50 | def event_key_press(self, key, char): 51 | global game_in_progress 52 | 53 | if key == 'f8': 54 | sge.gfx.Sprite.from_screenshot().save('screenshot.jpg') 55 | elif key == 'f11': 56 | self.fullscreen = not self.fullscreen 57 | elif key == 'escape': 58 | self.event_close() 59 | elif key in ('p', 'enter'): 60 | if game_in_progress: 61 | self.pause() 62 | else: 63 | game_in_progress = True 64 | create_room().start() 65 | 66 | def event_close(self): 67 | self.end() 68 | 69 | def event_paused_key_press(self, key, char): 70 | if key == 'escape': 71 | # This allows the player to still exit while the game is 72 | # paused, rather than having to unpause first. 73 | self.event_close() 74 | else: 75 | self.unpause() 76 | 77 | def event_paused_close(self): 78 | # This allows the player to still exit while the game is paused, 79 | # rather than having to unpause first. 80 | self.event_close() 81 | 82 | 83 | class Player(sge.dsp.Object): 84 | 85 | score = 0 86 | 87 | def __init__(self, player): 88 | if player == 1: 89 | self.joystick = 0 90 | self.up_key = "w" 91 | self.down_key = "s" 92 | x = PADDLE_XOFFSET 93 | self.hit_direction = 1 94 | else: 95 | self.joystick = 1 96 | self.up_key = "up" 97 | self.down_key = "down" 98 | x = sge.game.width - PADDLE_XOFFSET 99 | self.hit_direction = -1 100 | 101 | y = sge.game.height / 2 102 | super(Player, self).__init__(x, y, sprite=paddle_sprite, 103 | checks_collisions=False) 104 | 105 | def event_create(self): 106 | self.score = 0 107 | refresh_hud() 108 | self.trackball_motion = 0 109 | 110 | def event_step(self, time_passed, delta_mult): 111 | # Movement 112 | key_motion = (sge.keyboard.get_pressed(self.down_key) - 113 | sge.keyboard.get_pressed(self.up_key)) 114 | axis_motion = sge.joystick.get_axis(self.joystick, 1) 115 | 116 | if (abs(axis_motion) > abs(key_motion) and 117 | abs(axis_motion) > abs(self.trackball_motion)): 118 | self.yvelocity = axis_motion * PADDLE_SPEED 119 | elif abs(self.trackball_motion) > abs(key_motion): 120 | self.yvelocity = self.trackball_motion * PADDLE_SPEED 121 | else: 122 | self.yvelocity = key_motion * PADDLE_SPEED 123 | 124 | self.trackball_motion = 0 125 | 126 | # Keep the paddle inside the window 127 | if self.bbox_top < 0: 128 | self.bbox_top = 0 129 | elif self.bbox_bottom > sge.game.current_room.height: 130 | self.bbox_bottom = sge.game.current_room.height 131 | 132 | def event_joystick_trackball_move(self, joystick, ball, x, y): 133 | if joystick == self.joystick: 134 | self.trackball_motion += y 135 | 136 | 137 | class Ball(sge.dsp.Object): 138 | 139 | def __init__(self): 140 | x = sge.game.width / 2 141 | y = sge.game.height / 2 142 | super(Ball, self).__init__(x, y, sprite=ball_sprite) 143 | 144 | def event_create(self): 145 | self.serve() 146 | 147 | def event_step(self, time_passed, delta_mult): 148 | # Scoring 149 | if self.bbox_right < 0: 150 | player2.score += 1 151 | refresh_hud() 152 | score_sound.play() 153 | self.serve(-1) 154 | elif self.bbox_left > sge.game.current_room.width: 155 | player1.score += 1 156 | refresh_hud() 157 | score_sound.play() 158 | self.serve(1) 159 | 160 | # Bouncing off of the edges 161 | if self.bbox_bottom > sge.game.current_room.height: 162 | self.bbox_bottom = sge.game.current_room.height 163 | self.yvelocity = -abs(self.yvelocity) 164 | bounce_wall_sound.play() 165 | elif self.bbox_top < 0: 166 | self.bbox_top = 0 167 | self.yvelocity = abs(self.yvelocity) 168 | bounce_wall_sound.play() 169 | 170 | def event_collision(self, other, xdirection, ydirection): 171 | if isinstance(other, Player): 172 | if other.hit_direction == 1: 173 | self.bbox_left = other.bbox_right + 1 174 | else: 175 | self.bbox_right = other.bbox_left - 1 176 | 177 | self.xvelocity = min(abs(self.xvelocity) + BALL_ACCELERATION, 178 | BALL_MAX_SPEED) * other.hit_direction 179 | self.yvelocity += (self.y - other.y) * PADDLE_VERTICAL_FORCE 180 | bounce_sound.play() 181 | 182 | def serve(self, direction=None): 183 | global game_in_progress 184 | 185 | if direction is None: 186 | direction = random.choice([-1, 1]) 187 | 188 | self.x = self.xstart 189 | self.y = self.ystart 190 | 191 | if (player1.score < POINTS_TO_WIN and 192 | player2.score < POINTS_TO_WIN): 193 | # Next round 194 | self.xvelocity = BALL_START_SPEED * direction 195 | self.yvelocity = 0 196 | else: 197 | # Game Over! 198 | self.xvelocity = 0 199 | self.yvelocity = 0 200 | hud_sprite.draw_clear() 201 | x = hud_sprite.width / 2 202 | p1text = "WIN" if player1.score > player2.score else "LOSE" 203 | p2text = "WIN" if player2.score > player1.score else "LOSE" 204 | hud_sprite.draw_text(hud_font, p1text, x - TEXT_OFFSET, 205 | TEXT_OFFSET, color=sge.gfx.Color("white"), 206 | halign="right", valign="top") 207 | hud_sprite.draw_text(hud_font, p2text, x + TEXT_OFFSET, 208 | TEXT_OFFSET, color=sge.gfx.Color("white"), 209 | halign="left", valign="top") 210 | game_in_progress = False 211 | 212 | 213 | def create_room(): 214 | global player1 215 | global player2 216 | player1 = Player(1) 217 | player2 = Player(2) 218 | ball = Ball() 219 | return sge.dsp.Room([player1, player2, ball], background=background) 220 | 221 | 222 | def refresh_hud(): 223 | # This fixes the HUD sprite so that it displays the correct score. 224 | hud_sprite.draw_clear() 225 | x = hud_sprite.width / 2 226 | hud_sprite.draw_text(hud_font, str(player1.score), x - TEXT_OFFSET, 227 | TEXT_OFFSET, color=sge.gfx.Color("white"), 228 | halign="right", valign="top") 229 | hud_sprite.draw_text(hud_font, str(player2.score), x + TEXT_OFFSET, 230 | TEXT_OFFSET, color=sge.gfx.Color("white"), 231 | halign="left", valign="top") 232 | 233 | 234 | # Create Game object 235 | Game(width=640, height=480, fps=120, window_text="Pong") 236 | 237 | # Load sprites 238 | paddle_sprite = sge.gfx.Sprite(width=8, height=48, origin_x=4, origin_y=24) 239 | ball_sprite = sge.gfx.Sprite(width=8, height=8, origin_x=4, origin_y=4) 240 | paddle_sprite.draw_rectangle(0, 0, paddle_sprite.width, paddle_sprite.height, 241 | fill=sge.gfx.Color("white")) 242 | ball_sprite.draw_rectangle(0, 0, ball_sprite.width, ball_sprite.height, 243 | fill=sge.gfx.Color("white")) 244 | hud_sprite = sge.gfx.Sprite(width=320, height=120, origin_x=160, origin_y=0) 245 | 246 | # Load backgrounds 247 | layers = [sge.gfx.BackgroundLayer(paddle_sprite, sge.game.width / 2, 0, -10000, 248 | repeat_up=True, repeat_down=True)] 249 | background = sge.gfx.Background(layers, sge.gfx.Color("black")) 250 | 251 | # Load fonts 252 | hud_font = sge.gfx.Font("Droid Sans Mono", size=48) 253 | 254 | # Load sounds 255 | bounce_sound = sge.snd.Sound(os.path.join(DATA, 'bounce.wav')) 256 | bounce_wall_sound = sge.snd.Sound(os.path.join(DATA, 'bounce_wall.wav')) 257 | score_sound = sge.snd.Sound(os.path.join(DATA, 'score.wav')) 258 | 259 | # Create rooms 260 | sge.game.start_room = create_room() 261 | 262 | sge.game.mouse.visible = False 263 | sge.game.fps_time = 0 264 | sge.game.fps_frames = 0 265 | sge.game.fps_text = "" 266 | 267 | 268 | if __name__ == '__main__': 269 | sge.game.start() 270 | -------------------------------------------------------------------------------- /examples/rotation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Rotation Example 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | 23 | 24 | class Game(sge.dsp.Game): 25 | def event_key_press(self, key, char): 26 | if key == 'escape': 27 | self.end() 28 | 29 | def event_close(self): 30 | self.end() 31 | 32 | 33 | class Circle(sge.dsp.Object): 34 | def __init__(self, x, y): 35 | super(Circle, self).__init__(x, y, 5, sprite=rotator_sprite, 36 | regulate_origin=True, 37 | collision_precise=True) 38 | 39 | def event_create(self): 40 | self.image_alpha = 200 41 | if self.collision(sge.game.mouse): 42 | self.image_blend = sge.gfx.Color('#ff0000') 43 | else: 44 | self.image_blend = sge.gfx.Color('blue') 45 | 46 | if random.random() < 0.5: 47 | self.image_xscale = 2 48 | self.image_yscale = 2 49 | 50 | def event_step(self, time_passed, delta_mult): 51 | self.image_rotation += delta_mult 52 | sge.game.current_room.project_circle(self.x, self.y, self.z + 1, 8, 53 | outline=sge.gfx.Color("green")) 54 | 55 | if self.collision(sge.game.mouse): 56 | self.image_blend = sge.gfx.Color("red") 57 | else: 58 | self.image_blend = sge.gfx.Color((0, 0, 255)) 59 | 60 | 61 | def main(): 62 | global rotator_sprite 63 | 64 | # Create Game object 65 | game = Game(delta=True, collision_events_enabled=False) 66 | 67 | # Load sprites 68 | rotator_sprite = sge.gfx.Sprite('rotator', DATA) 69 | fence_sprite = sge.gfx.Sprite('fence', DATA) 70 | 71 | # Load backgrounds 72 | layers = [sge.gfx.BackgroundLayer(fence_sprite, 0, 380, 0, 73 | repeat_left=True, repeat_right=True)] 74 | background = sge.gfx.Background(layers, sge.gfx.Color(0xffffff)) 75 | 76 | # Create objects 77 | circle = Circle(game.width // 2, game.height // 2) 78 | circle2 = Circle(22, 48) 79 | circle3 = Circle(486, 301) 80 | circle4 = Circle(50, 400) 81 | objects = (circle, circle2, circle3, circle4) 82 | 83 | # Create view 84 | views = [sge.dsp.View(0, 0)] 85 | 86 | # Create rooms 87 | game.start_room = sge.dsp.Room(objects, views=views, background=background) 88 | 89 | game.start() 90 | 91 | 92 | if __name__ == '__main__': 93 | main() 94 | -------------------------------------------------------------------------------- /examples/splitscreen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Splitscreen Example 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | 23 | circles = [] 24 | 25 | 26 | class Game(sge.dsp.Game): 27 | 28 | def event_step(self, time_passed, delta_mult): 29 | self.project_line(self.width / 2, 0, self.width / 2, self.height, 30 | sge.gfx.Color("black"), thickness=3) 31 | self.project_line(0, self.height / 2, self.width, self.height / 2, 32 | sge.gfx.Color("black"), thickness=3) 33 | 34 | def event_key_press(self, key, char): 35 | if key == 'escape': 36 | self.end() 37 | elif key in ("p", "enter"): 38 | self.pause() 39 | 40 | def event_close(self): 41 | self.end() 42 | 43 | def event_paused_key_press(self, key, char): 44 | self.unpause() 45 | 46 | def event_paused_close(self): 47 | self.event_close() 48 | 49 | 50 | class Circle(sge.dsp.Object): 51 | def __init__(self, x, y, player=0): 52 | super(Circle, self).__init__(x, y, 1, sprite=circle_sprite, 53 | collision_precise=True) 54 | self.player = player 55 | self.normal_image_blend = [sge.gfx.Color('red'), sge.gfx.Color('blue'), 56 | sge.gfx.Color('yellow'), 57 | sge.gfx.Color('green')][self.player] 58 | self.image_alpha = 128 59 | 60 | def set_color(self): 61 | if self.collision(Circle): 62 | self.image_blend = sge.gfx.Color('olive') 63 | else: 64 | self.image_blend = self.normal_image_blend 65 | 66 | def event_create(self): 67 | self.set_color() 68 | 69 | def event_step(self, time_passed, delta_mult): 70 | left_key = ['left', 'a', 'j', 'kp_4'][self.player] 71 | right_key = ['right', 'd', 'l', 'kp_6'][self.player] 72 | up_key = ['up', 'w', 'i', 'kp_8'][self.player] 73 | down_key = ['down', 's', 'k', 'kp_5'][self.player] 74 | self.xvelocity = (sge.keyboard.get_pressed(right_key) - 75 | sge.keyboard.get_pressed(left_key)) 76 | self.yvelocity = (sge.keyboard.get_pressed(down_key) - 77 | sge.keyboard.get_pressed(up_key)) 78 | 79 | # Limit the circles to inside the room. 80 | if self.bbox_left < 0: 81 | self.bbox_left = 0 82 | elif self.bbox_right >= sge.game.current_room.width: 83 | self.bbox_right = sge.game.current_room.width - 1 84 | if self.bbox_top < 0: 85 | self.bbox_top = 0 86 | elif self.bbox_bottom >= sge.game.current_room.height: 87 | self.bbox_bottom = sge.game.current_room.height - 1 88 | 89 | self.set_color() 90 | 91 | # Set view 92 | my_view = sge.game.current_room.views[self.player] 93 | my_view.x = self.x - (my_view.width // 2) 94 | my_view.y = self.y - (my_view.height // 2) 95 | 96 | 97 | def main(): 98 | global circle_sprite 99 | 100 | # Create Game object 101 | Game(width=640, height=480, collision_events_enabled=False) 102 | 103 | # Load sprites 104 | circle_sprite = sge.gfx.Sprite('circle', DATA, width=32, height=32, 105 | origin_x=16, origin_y=16) 106 | fence = sge.gfx.Sprite('fence', DATA) 107 | sge.game.mouse_sprite = circle_sprite 108 | 109 | # Load backgrounds 110 | layers = [sge.gfx.BackgroundLayer(fence, 0, 0, 0, repeat_left=True, 111 | repeat_right=True, repeat_up=True, 112 | repeat_down=True)] 113 | background = sge.gfx.Background(layers, sge.gfx.Color('white')) 114 | 115 | # Create objects 116 | objects = [] 117 | for i in range(4): 118 | circle = Circle(64, 64, i) 119 | objects.append(circle) 120 | 121 | # Create views 122 | views = [] 123 | for x in range(2): 124 | for y in range(2): 125 | views.append(sge.dsp.View(0, 0, 320 * x, 240 * y, 320, 240)) 126 | 127 | # Create rooms 128 | sge.game.start_room = sge.dsp.Room(objects, width=1280, height=1024, 129 | views=views, background=background) 130 | 131 | sge.game.start() 132 | 133 | 134 | if __name__ == '__main__': 135 | main() 136 | 137 | -------------------------------------------------------------------------------- /examples/transitions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Transitions example 4 | # 5 | # To the extent possible under law, the author(s) have dedicated all 6 | # copyright and related and neighboring rights to this software to the 7 | # public domain worldwide. This software is distributed without any 8 | # warranty. 9 | # 10 | # You should have received a copy of the CC0 Public Domain Dedication 11 | # along with this software. If not, see 12 | # . 13 | 14 | 15 | import os 16 | import random 17 | 18 | import sge 19 | 20 | 21 | DATA = os.path.join(os.path.dirname(__file__), "data") 22 | 23 | 24 | class glob(object): 25 | 26 | font = None 27 | pop_sound = None 28 | music = None 29 | rooms = [] 30 | 31 | 32 | class Game(sge.dsp.Game): 33 | 34 | def event_key_press(self, key, char): 35 | if key == 'escape': 36 | self.end() 37 | 38 | def event_close(self): 39 | self.end() 40 | 41 | 42 | class Circle(sge.dsp.Object): 43 | 44 | def __init__(self, x, y): 45 | super(Circle, self).__init__(x, y, 5, sprite=glob.circle_sprite, 46 | collision_precise=True) 47 | 48 | def event_create(self): 49 | self.image_alpha = 200 50 | if self.collision(sge.game.mouse): 51 | self.image_blend = sge.gfx.Color('#ff0000') 52 | else: 53 | self.image_blend = sge.gfx.Color('blue') 54 | 55 | def event_mouse_move(self, x, y): 56 | if self.collision(sge.game.mouse): 57 | self.image_blend = sge.gfx.Color("red") 58 | else: 59 | self.image_blend = sge.gfx.Color((0, 0, 255)) 60 | 61 | def event_mouse_button_press(self, button): 62 | if button == 'left': 63 | if self.collision(sge.game.mouse): 64 | self.destroy() 65 | 66 | def event_destroy(self): 67 | pop = CirclePop(self.x, self.y) 68 | pop.image_blend = self.image_blend 69 | sge.game.current_room.add(pop) 70 | assert glob.pop_sound is not None 71 | glob.pop_sound.play() 72 | 73 | 74 | class CirclePop(sge.dsp.Object): 75 | 76 | def __init__(self, x, y): 77 | super(CirclePop, self).__init__(x, y, 5, sprite=glob.circle_pop_sprite, 78 | tangible=False) 79 | 80 | def event_animation_end(self): 81 | self.destroy() 82 | 83 | def event_destroy(self): 84 | circle = Circle(random.randint(0, sge.game.width), 85 | random.randint(0, sge.game.height)) 86 | sge.game.current_room.add(circle) 87 | 88 | 89 | class Room(sge.dsp.Room): 90 | 91 | def __init__(self, text, objects=(), views=None, background=None): 92 | self.text = text 93 | super(Room, self).__init__(objects, views=views, background=background) 94 | 95 | def event_room_start(self): 96 | self.event_room_resume() 97 | 98 | def event_room_resume(self): 99 | sge.game.window_text = self.text 100 | glob.music.play(loops=None) 101 | 102 | def event_key_press(self, key, char): 103 | next_room = glob.rooms[(glob.rooms.index(self) + 1) % len(glob.rooms)] 104 | if key == "space": 105 | next_room.start() 106 | elif key == "1": 107 | next_room.start(transition="fade") 108 | elif key == "2": 109 | next_room.start(transition="dissolve") 110 | elif key == "3": 111 | next_room.start(transition="pixelate") 112 | elif key == "4": 113 | next_room.start(transition="wipe_left") 114 | elif key == "5": 115 | next_room.start(transition="wipe_right") 116 | elif key == "6": 117 | next_room.start(transition="wipe_up") 118 | elif key == "7": 119 | next_room.start(transition="wipe_down") 120 | elif key == "8": 121 | next_room.start(transition="wipe_upleft") 122 | elif key == "9": 123 | next_room.start(transition="wipe_upright") 124 | elif key == "0": 125 | next_room.start(transition="wipe_downleft") 126 | elif key == "q": 127 | next_room.start(transition="wipe_downright") 128 | elif key == "w": 129 | next_room.start(transition="wipe_matrix") 130 | elif key == "e": 131 | next_room.start(transition="iris_in") 132 | elif key == "r": 133 | next_room.start(transition="iris_out") 134 | 135 | 136 | def main(): 137 | # Create Game object 138 | game = Game(collision_events_enabled=False) 139 | 140 | # Load sprites 141 | glob.circle_sprite = sge.gfx.Sprite('circle', DATA, width=64, height=64, 142 | origin_x=32, origin_y=32) 143 | glob.circle_pop_sprite = sge.gfx.Sprite('circle_pop', DATA, width=64, 144 | height=64, origin_x=32, 145 | origin_y=32, fps=60) 146 | fence_sprite = sge.gfx.Sprite('fence', DATA) 147 | 148 | # Load backgrounds 149 | layers = [sge.gfx.BackgroundLayer(fence_sprite, 0, 380, repeat_left=True, 150 | repeat_right=True)] 151 | layers2 = [sge.gfx.BackgroundLayer(fence_sprite, 0, 0, repeat_left=True, 152 | repeat_right=True, repeat_up=True, 153 | repeat_down=True)] 154 | background = sge.gfx.Background(layers, sge.gfx.Color(0xffffff)) 155 | background2 = sge.gfx.Background(layers2, sge.gfx.Color('white')) 156 | 157 | # Load fonts 158 | glob.font = sge.gfx.Font('Liberation Serif', 20) 159 | 160 | # Load sounds 161 | glob.pop_sound = sge.snd.Sound(os.path.join(DATA, 'pop.ogg')) 162 | 163 | # Load music 164 | glob.music = sge.snd.Music(os.path.join(DATA, 'WhereWasI.ogg')) 165 | 166 | # Create objects 167 | circle = Circle(game.width // 2, game.height // 2) 168 | circle2 = Circle(22, 48) 169 | circle3 = Circle(486, 301) 170 | circle4 = Circle(50, 400) 171 | circle5 = Circle(game.width // 2, game.height // 2) 172 | circle6 = Circle(52, 120) 173 | objects = [circle, circle2, circle3, circle4] 174 | objects2 = [circle5, circle6] 175 | 176 | # Create view 177 | views = [sge.dsp.View(0, 0)] 178 | 179 | # Create rooms 180 | room1 = Room('I am the first room!', objects, views=views, background=background) 181 | room2 = Room('Second room on the house!', objects2, background=background2) 182 | room3 = Room('I am the third room!', objects, views=views, background=background) 183 | room4 = Room('Fourth room on the house!', objects2, background=background2) 184 | room5 = Room('I am the fifth room!', objects, views=views, background=background) 185 | room6 = Room('Sixth room on the house!', objects2, background=background2) 186 | glob.rooms = [room1, room2, room3, room4, room5, room6] 187 | 188 | game.start_room = room1 189 | 190 | game.start() 191 | 192 | 193 | if __name__ == '__main__': 194 | main() 195 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "sge" 3 | version = "2.0.2" 4 | description = "Seclusion Game Engine (Pygame implementation)" 5 | readme = {file = "README", content-type = "text/plain"} 6 | requires-python = ">=3.6" 7 | license = {file = "sge/COPYING.LESSER"} 8 | authors = [ 9 | {name = "Diligent Circle", email = "diligentcircle@riseup.net"}, 10 | ] 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "Intended Audience :: Developers", 14 | "License :: DFSG approved", 15 | "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", 16 | "Natural Language :: English", 17 | "Operating System :: OS Independent", 18 | "Programming Language :: Python :: 3", 19 | "Topic :: Games/Entertainment", 20 | "Topic :: Software Development", 21 | ] 22 | dependencies = [ 23 | "pygame (>=2.0.1)", 24 | "uniseg", 25 | ] 26 | 27 | [project.urls] 28 | homepage = "https://python-sge.github.io" 29 | documentation = "https://python-sge.github.io/doc/sge/index.html" 30 | repository = "https://github.com/python-sge/sge.git" 31 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # This file has been dedicated to the public domain, to the extent 2 | # possible under applicable law, via CC0. See 3 | # http://creativecommons.org/publicdomain/zero/1.0/ for more 4 | # information. This file is offered as-is, without any warranty. 5 | 6 | import sys 7 | from distutils.core import setup 8 | 9 | long_description = """ 10 | The Seclusion Game Engine ("SGE") is a general-purpose 2-D game engine. 11 | It takes care of several details for you so you can focus on the game 12 | itself. This makes more rapid game development possible, and it also 13 | makes the SGE easy to learn. 14 | 15 | This implementation of the SGE uses Pygame as a backend. 16 | """.strip() 17 | 18 | setup(name="sge", 19 | version="2.0.3a0", 20 | description="Seclusion Game Engine (Pygame implementation)", 21 | long_description=long_description, 22 | author="Diligent Circle", 23 | author_email="diligentcircle@riseup.net", 24 | url="https://python-sge.github.io", 25 | classifiers=["Development Status :: 5 - Production/Stable", 26 | "Intended Audience :: Developers", 27 | "License :: DFSG approved", 28 | "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", 29 | "Natural Language :: English", 30 | "Operating System :: OS Independent", 31 | "Programming Language :: Python :: 3", 32 | "Topic :: Games/Entertainment", 33 | "Topic :: Software Development"], 34 | license="GNU Lesser General Public License", 35 | packages=["sge"], 36 | package_dir={"sge": "sge"}, 37 | package_data={"sge": ["COPYING", "COPYING.LESSER"]}, 38 | requires=["pygame (>=2.0.1)", "uniseg"], 39 | provides=["sge"], 40 | ) 41 | -------------------------------------------------------------------------------- /sge/COPYING.LESSER: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /sge/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | The Seclusion Game Engine ("SGE") is a general-purpose 2-D game engine. 18 | It takes care of several details for you so you can focus on the game 19 | itself. This makes more rapid game development possible, and it also 20 | makes the SGE easy to learn. 21 | 22 | The SGE is libre open source software, and the SGE documentation 23 | (including all docstrings) is released to the public domain via CC0. 24 | 25 | Although it isn't required, please consider releasing your games' code 26 | under a libre software license, such as the GNU General Public License 27 | or the Apache License. Doing so is easy and typically does not 28 | negatively affect you. It's also just a real great thing to do. 29 | 30 | SGE Concepts 31 | ============ 32 | 33 | Events 34 | ------ 35 | 36 | The SGE uses an event-based system. When an event occurs, a certain 37 | event method (with a name that begins with ``event_``) is called. To 38 | define actions triggered by events, simply override the appropriate 39 | event method. 40 | 41 | At a lower level, it is possible to read "input events" from 42 | :attr:`sge.game.input_events` and handle them manually. See the 43 | documentation for :mod:`sge.input` for more information. This is not 44 | recommended, however, unless you are running your own loop for some 45 | reason (in which case it is necessary to do this in order to get input 46 | from the user). 47 | 48 | Position 49 | -------- 50 | 51 | In all cases of positioning for the SGE, it is based on a 52 | two-dimensional graph with each unit being a pixel. This graph is not 53 | quite like regular graphs. The horizontal direction, normally called 54 | ``x``, is the same as the x-axis on a regular graph; ``0`` is the 55 | origin, positive numbers are to the right of the origin, and negative 56 | numbers are to the left of the origin. However, in the vertical 57 | direction, normally called ``y``, ``0`` is the origin, positive numbers 58 | are below the origin, and negative numbers are above the origin. While 59 | slightly jarring if you are used to normal graphs, this is in fact 60 | common in 2-D game development and is also how pixels in most image 61 | formats are indexed. 62 | 63 | Except where otherwise specified, the origin is always located at the 64 | top-leftmost position of an object. 65 | 66 | In addition to integers, position variables are allowed by the SGE to be 67 | floating-point numbers. 68 | 69 | Z-Axis 70 | ------ 71 | 72 | The SGE uses a Z-axis to determine where objects are placed in the third 73 | dimension. Objects with a higher Z value are considered to be closer to 74 | the viewer and thus will be placed over objects which have a lower Z 75 | value. Note that the Z-axis does not allow 3-D gameplay or effects; it 76 | is only used to tell the SGE what to do with objects that overlap. For 77 | example, if an object called ``spam`` has a Z value of ``5`` while an 78 | object called ``eggs`` has a Z value of ``2``, ``spam`` will obscure 79 | part or all of ``eggs`` when the two objects overlap. 80 | 81 | If two objects with the same Z-axis value overlap, the object which was 82 | most recently added to the room is placed in front. 83 | 84 | The Game Loop 85 | ------------- 86 | 87 | There can occasionally be times where you want to run your own loop, 88 | independent of the SGE's main loop. This is not recommended in general, 89 | but if you must (to freeze the game, for example), you should know the 90 | general game loop structure:: 91 | 92 | while True: 93 | # Input events 94 | sge.game.pump_input() 95 | while sge.game.input_events: 96 | event = sge.game.input_events.pop(0) 97 | 98 | # Handle event 99 | 100 | # Regulate speed 101 | time_passed = sge.game.regulate_speed() 102 | 103 | # Logic (e.g. collision detection and step events) 104 | 105 | # Refresh 106 | sge.game.refresh() 107 | 108 | :meth:`sge.dsp.Game.pump_input` should be called frequently at all times 109 | regardless of whether or not user input is needed. This means it should 110 | be called every frame. It also means that should any task halt the loop 111 | for any noticeable period of time, arrangements should be made to call 112 | this method frequently during that time. Failing to call this method 113 | for a substantial period of time will cause the queue to build up, but 114 | more importantly, the OS may decide that the program has locked up if 115 | you wait for too long. 116 | 117 | :meth:`sge.dsp.Game.regulate_speed` limits the frame rate of the game 118 | and tells you how much time has passed since the last frame. It is not 119 | technically necessary, but using it is highly recommended; otherwise, 120 | the CPU will be working harder than it needs to and if things are 121 | moving, their speed will be irregular. 122 | 123 | :meth:`sge.dsp.Game.refresh` is necessary for any changes to the screen 124 | to be seen by the user. This includes new objects, removed objects, new 125 | projections, discontinued projections, etc. 126 | 127 | Global Variables and Constants 128 | ============================== 129 | 130 | .. data:: sge.IMPLEMENTATION 131 | 132 | A string indicating the name of the SGE implementation. 133 | 134 | .. data:: sge.SCALE_METHODS 135 | 136 | A list of specific scale methods supported by the SGE implementation. 137 | 138 | .. note:: 139 | 140 | This list does not include the generic scale methods, ``"noblur"`` 141 | and ``"smooth"``. It is also possible for this list to be empty. 142 | 143 | .. data:: sge.BLEND_NORMAL 144 | 145 | Flag indicating normal blending. 146 | 147 | .. data:: sge.BLEND_RGBA_ADD 148 | 149 | Flag indicating RGBA Addition blending: the red, green, blue, and 150 | alpha color values of the source are added to the respective color 151 | values of the destination, to a maximum of 255. 152 | 153 | .. data:: sge.BLEND_RGBA_SUBTRACT 154 | 155 | Flag indicating RGBA Subtract blending: the red, green, blue, and 156 | alpha color values of the source are subtracted from the respective 157 | color values of the destination, to a minimum of 0. 158 | 159 | .. data:: sge.BLEND_RGBA_MULTIPLY 160 | 161 | Flag indicating RGBA Multiply blending: the red, green, blue, 162 | and alpha color values of the source and destination are converted to 163 | values between 0 and 1 (divided by 255), the resulting destination 164 | color values are multiplied by the respective resulting source color 165 | values, and these results are converted back into values between 0 166 | and 255 (multiplied by 255). 167 | 168 | .. data:: sge.BLEND_RGBA_SCREEN 169 | 170 | Flag indicating RGBA Screen blending: the red, green, blue, and alpha 171 | color values of the source and destination are inverted (subtracted 172 | from 255) and converted to values between 0 and 1 (divided by 255), 173 | the resulting destination color values are multiplied by the 174 | respective resulting source color values, and these results are 175 | converted back into values between 0 and 255 (multiplied by 255) and 176 | inverted again (subtracted from 255). 177 | 178 | .. data:: sge.BLEND_RGBA_MINIMUM 179 | 180 | Flag indicating RGBA Minimum (Darken Only) blending: the smallest 181 | respective red, green, blue, and alpha color values out of the source 182 | and destination are used. 183 | 184 | .. data:: sge.BLEND_RGBA_MAXIMUM 185 | 186 | Flag indicating RGBA Maximum (Lighten Only) blending: the largest 187 | respective red, green, blue, and alpha color values out of the source 188 | and destination are used. 189 | 190 | .. data:: sge.BLEND_RGB_ADD 191 | 192 | Flag indicating RGB Addition blending: the same thing as RGBA 193 | Addition blending (see :data:`sge.BLEND_RGBA_ADD`) except the 194 | destination's alpha values are not changed. 195 | 196 | .. data:: sge.BLEND_RGB_SUBTRACT 197 | 198 | Flag indicating RGB Subtract blending: the same thing as RGBA 199 | Subtract blending (see :data:`sge.BLEND_RGBA_SUBTRACT`) except the 200 | destination's alpha values are not changed. 201 | 202 | .. data:: sge.BLEND_RGB_MULTIPLY 203 | 204 | Flag indicating RGB Multiply blending: the same thing as RGBA 205 | Multiply blending (see :data:`sge.BLEND_RGBA_MULTIPLY`) except the 206 | destination's alpha values are not changed. 207 | 208 | .. data:: sge.BLEND_RGB_SCREEN 209 | 210 | Flag indicating RGB Screen blending: the same thing as RGBA Screen 211 | blending (see :data:`sge.BLEND_RGBA_SCREEN`) except the destination's 212 | alpha values are not changed. 213 | 214 | .. data:: sge.BLEND_RGB_MINIMUM 215 | 216 | Flag indicating RGB Minimum (Darken Only) blending: the same thing 217 | as RGBA Minimum blending (see :data:`sge.BLEND_RGBA_MINIMUM`) except 218 | the destination's alpha values are not changed. 219 | 220 | .. data:: sge.BLEND_RGB_MAXIMUM 221 | 222 | Flag indicating RGB Maximum (Lighten Only) blending: the same thing 223 | as RGBA Maximum blending (see :data:`sge.BLEND_RGBA_MAXIMUM`) except 224 | the destination's alpha values are not changed. 225 | 226 | .. data:: sge.game 227 | 228 | Stores the current :class:`sge.dsp.Game` object. If there is no 229 | :class:`sge.dsp.Game` object currently, this variable is set to 230 | ``None``. 231 | """ 232 | 233 | 234 | __version__ = "2.0.3a0" 235 | __all__ = [ 236 | # Modules 237 | "collision", 238 | "gfx", 239 | "input", 240 | "joystick", 241 | "keyboard", 242 | "mouse", 243 | 244 | # Constants 245 | 'IMPLEMENTATION', 246 | 'BLEND_RGBA_ADD', 247 | 'BLEND_RGBA_SUBTRACT', 248 | 'BLEND_RGBA_MULTIPLY', 249 | 'BLEND_RGBA_SCREEN', 250 | 'BLEND_RGBA_MINIMUM', 251 | 'BLEND_RGBA_MAXIMUM', 252 | 'BLEND_RGB_ADD', 253 | 'BLEND_RGB_SUBTRACT', 254 | 'BLEND_RGB_MULTIPLY', 255 | 'BLEND_RGB_SCREEN', 256 | 'BLEND_RGB_MINIMUM', 257 | 'BLEND_RGB_MAXIMUM', 258 | ] 259 | 260 | 261 | import sys 262 | import os 263 | 264 | import pygame 265 | 266 | 267 | # Constants 268 | IMPLEMENTATION = "Pygame SGE" 269 | SCALE_METHODS = ["scale2x"] 270 | 271 | BLEND_NORMAL = None 272 | BLEND_ALPHA = 1 273 | BLEND_RGB_ADD = 2 274 | BLEND_RGB_SUBTRACT = 4 275 | BLEND_RGB_MULTIPLY = 6 276 | BLEND_RGB_SCREEN = 8 277 | BLEND_RGB_MINIMUM = 10 278 | BLEND_RGB_MAXIMUM = 12 279 | 280 | BLEND_RGBA_ADD = BLEND_ALPHA | BLEND_RGB_ADD 281 | BLEND_RGBA_SUBTRACT = BLEND_ALPHA | BLEND_RGB_SUBTRACT 282 | BLEND_RGBA_MULTIPLY = BLEND_ALPHA | BLEND_RGB_MULTIPLY 283 | BLEND_RGBA_SCREEN = BLEND_ALPHA | BLEND_RGB_SCREEN 284 | BLEND_RGBA_MINIMUM = BLEND_ALPHA | BLEND_RGB_MINIMUM 285 | BLEND_RGBA_MAXIMUM = BLEND_ALPHA | BLEND_RGB_MAXIMUM 286 | 287 | MUSIC_END_EVENT = pygame.USEREVENT + 1 288 | MUSIC_END_BLOCK_EVENT = pygame.USEREVENT + 2 289 | 290 | KEYS = {"0": pygame.K_0, "1": pygame.K_1, "2": pygame.K_2, "3": pygame.K_3, 291 | "4": pygame.K_4, "5": pygame.K_5, "6": pygame.K_6, "7": pygame.K_7, 292 | "8": pygame.K_8, "9": pygame.K_9, "a": pygame.K_a, "b": pygame.K_b, 293 | "c": pygame.K_c, "d": pygame.K_d, "e": pygame.K_e, "f": pygame.K_f, 294 | "g": pygame.K_g, "h": pygame.K_h, "i": pygame.K_i, "j": pygame.K_j, 295 | "k": pygame.K_k, "l": pygame.K_l, "m": pygame.K_m, "n": pygame.K_n, 296 | "o": pygame.K_o, "p": pygame.K_p, "q": pygame.K_q, "r": pygame.K_r, 297 | "s": pygame.K_s, "t": pygame.K_t, "u": pygame.K_u, "v": pygame.K_v, 298 | "w": pygame.K_w, "x": pygame.K_x, "y": pygame.K_y, "z": pygame.K_z, 299 | "alt_left": pygame.K_LALT, "alt_right": pygame.K_RALT, 300 | "ampersand": pygame.K_AMPERSAND, "apostrophe": pygame.K_QUOTE, 301 | "asterisk": pygame.K_ASTERISK, "at": pygame.K_AT, 302 | "backslash": pygame.K_BACKSLASH, "backspace": pygame.K_BACKSPACE, 303 | "backtick": pygame.K_BACKQUOTE, "bracket_left": pygame.K_LEFTBRACKET, 304 | "bracket_right": pygame.K_RIGHTBRACKET, "break": pygame.K_BREAK, 305 | "caps_lock": pygame.K_CAPSLOCK, "caret": pygame.K_CARET, 306 | "undef_clear": pygame.K_CLEAR, "colon": pygame.K_COLON, 307 | "comma": pygame.K_COMMA, "ctrl_left": pygame.K_LCTRL, 308 | "ctrl_right": pygame.K_RCTRL, "delete": pygame.K_DELETE, 309 | "dollar": pygame.K_DOLLAR, "down": pygame.K_DOWN, "end": pygame.K_END, 310 | "enter": pygame.K_RETURN, "equals": pygame.K_EQUALS, 311 | "escape": pygame.K_ESCAPE, "euro": pygame.K_EURO, 312 | "exclamation": pygame.K_EXCLAIM, "f1": pygame.K_F1, "f2": pygame.K_F2, 313 | "f3": pygame.K_F3, "f4": pygame.K_F4, "f5": pygame.K_F5, 314 | "f6": pygame.K_F6, "f7": pygame.K_F7, "f8": pygame.K_F8, 315 | "f9": pygame.K_F9, "f10": pygame.K_F10, "f11": pygame.K_F11, 316 | "f12": pygame.K_F12, "greater_than": pygame.K_GREATER, 317 | "hash": pygame.K_HASH, "undef_help": pygame.K_HELP, 318 | "home": pygame.K_HOME, "hyphen": pygame.K_MINUS, 319 | "insert": pygame.K_INSERT, 320 | "kp_0": pygame.K_KP0, "kp_1": pygame.K_KP1, "kp_2": pygame.K_KP2, 321 | "kp_3": pygame.K_KP3, "kp_4": pygame.K_KP4, "kp_5": pygame.K_KP5, 322 | "kp_6": pygame.K_KP6, "kp_7": pygame.K_KP7, "kp_8": pygame.K_KP8, 323 | "kp_9": pygame.K_KP9, "kp_divide": pygame.K_KP_DIVIDE, 324 | "kp_enter": pygame.K_KP_ENTER, "kp_equals": pygame.K_KP_EQUALS, 325 | "kp_minus": pygame.K_KP_MINUS, "kp_multiply": pygame.K_KP_MULTIPLY, 326 | "kp_plus": pygame.K_KP_PLUS, "kp_point": pygame.K_KP_PERIOD, 327 | "left": pygame.K_LEFT, "less_than": pygame.K_LESS, 328 | "menu": pygame.K_MENU, "meta_left": pygame.K_LMETA, 329 | "meta_right": pygame.K_RMETA, "undef_mode": pygame.K_MODE, 330 | "num_lock": pygame.K_NUMLOCK, "pagedown": pygame.K_PAGEDOWN, 331 | "pageup": pygame.K_PAGEUP, "parenthesis_left": pygame.K_LEFTPAREN, 332 | "parenthesis_right": pygame.K_RIGHTPAREN, "pause": pygame.K_PAUSE, 333 | "period": pygame.K_PERIOD, "plus": pygame.K_PLUS, 334 | "undef_power": pygame.K_POWER, "print_screen": pygame.K_PRINT, 335 | "question": pygame.K_QUESTION, "quote": pygame.K_QUOTEDBL, 336 | "right": pygame.K_RIGHT, "scroll_lock": pygame.K_SCROLLOCK, 337 | "semicolon": pygame.K_SEMICOLON, "shift_left": pygame.K_LSHIFT, 338 | "shift_right": pygame.K_RSHIFT, "slash": pygame.K_SLASH, 339 | "space": pygame.K_SPACE, "undef_super_left": pygame.K_LSUPER, 340 | "undef_super_right": pygame.K_RSUPER, "sysrq": pygame.K_SYSREQ, 341 | "tab": pygame.K_TAB, "underscore": pygame.K_UNDERSCORE, 342 | "up": pygame.K_UP} 343 | KEY_NAMES = {} 344 | for pair in KEYS.items(): 345 | KEY_NAMES[pair[1]] = pair[0] 346 | 347 | MODS = {"alt": pygame.KMOD_ALT, "alt_left": pygame.KMOD_LALT, 348 | "alt_right": pygame.KMOD_RALT, "caps_lock": pygame.KMOD_CAPS, 349 | "ctrl": pygame.KMOD_CTRL, "ctrl_left": pygame.KMOD_LCTRL, 350 | "ctrl_right": pygame.KMOD_RCTRL, "meta": pygame.KMOD_META, 351 | "meta_left": pygame.KMOD_LMETA, "meta_right": pygame.KMOD_RMETA, 352 | "mode": pygame.KMOD_MODE, "num_lock": pygame.KMOD_NUM, 353 | "shift": pygame.KMOD_SHIFT, "shift_left": pygame.KMOD_LSHIFT, 354 | "shift_right": pygame.KMOD_RSHIFT} 355 | 356 | MOUSE_BUTTONS = {"left": 1, "right": 3, "middle": 2, "wheel_up": 4, 357 | "wheel_down": 5, "extra1": 6, "extra2": 7} 358 | MOUSE_BUTTON_NAMES = {} 359 | for pair in MOUSE_BUTTONS.items(): 360 | MOUSE_BUTTON_NAMES[pair[1]] = pair[0] 361 | 362 | 363 | from sge import (collision, dsp, gfx, input, joystick, keyboard, mouse, snd, s, 364 | r) 365 | 366 | 367 | # Global variables 368 | game = None 369 | 370 | os.environ['PYGAME_FREETYPE'] = '1' 371 | 372 | # Uncomment this line to tell SDL to center the window. Disabled by 373 | # default because it seems to cause some weird behavior with window 374 | # resizing on at least some systems. 375 | #os.environ['SDL_VIDEO_CENTERED'] = '1' 376 | -------------------------------------------------------------------------------- /sge/collision.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides easy-to-use collision detection functions, from 18 | basic rectangle-based collision detection to shape-based collision 19 | detection. 20 | """ 21 | 22 | 23 | import math 24 | 25 | import sge 26 | from sge import r 27 | from sge.r import s_get_precise_mask 28 | 29 | 30 | __all__ = ["rectangles_collide", "masks_collide", "rectangle", "ellipse", 31 | "circle", "line"] 32 | 33 | 34 | def rectangles_collide(x1, y1, w1, h1, x2, y2, w2, h2): 35 | """ 36 | Return whether or not two rectangles collide. 37 | 38 | Parameters: 39 | 40 | - ``x1`` -- The horizontal position of the first rectangle. 41 | - ``y1`` -- The vertical position of the first rectangle. 42 | - ``w1`` -- The width of the first rectangle. 43 | - ``h1`` -- The height of the first rectangle. 44 | - ``x2`` -- The horizontal position of the second rectangle. 45 | - ``y2`` -- The vertical position of the second rectangle. 46 | - ``w2`` -- The width of the second rectangle. 47 | - ``h2`` -- The height of the second rectangle. 48 | """ 49 | return (x1 < x2 + w2 and x1 + w1 > x2 and y1 < y2 + h2 and y1 + h1 > y2) 50 | 51 | 52 | def masks_collide(x1, y1, mask1, x2, y2, mask2): 53 | """ 54 | Return whether or not two masks collide. 55 | 56 | Parameters: 57 | 58 | - ``x1`` -- The horizontal position of the first mask. 59 | - ``y1`` -- The vertical position of the first mask. 60 | - ``mask1`` -- The first mask (see below). 61 | - ``x2`` -- The horizontal position of the second mask. 62 | - ``y2`` -- The vertical position of the second mask. 63 | - ``mask2`` -- The second mask (see below). 64 | 65 | ``mask1`` and ``mask2`` are both lists of lists of boolean values. 66 | Each value in the mask indicates whether or not a pixel is counted 67 | as a collision; the masks collide if at least one pixel at the same 68 | location is :const:`True` for both masks. 69 | 70 | Masks are indexed as ``mask[x][y]``, where ``x`` is the column and 71 | ``y`` is the row. 72 | """ 73 | if mask1 and mask2 and mask1[0] and mask2[0]: 74 | x1 = round(x1) 75 | y1 = round(y1) 76 | w1 = len(mask1) 77 | h1 = len(mask1[0]) 78 | x2 = round(x2) 79 | y2 = round(y2) 80 | w2 = len(mask2) 81 | h2 = len(mask2[0]) 82 | 83 | if rectangles_collide(x1, y1, w1, h1, x2, y2, w2, h2): 84 | for i in range(max(x1, x2), min(x1 + w1, x2 + w2)): 85 | for j in range(max(y1, y2), min(y1 + h1, y2 + h2)): 86 | if (mask1[i - x1][j - y1] and mask2[i - x2][j - y2]): 87 | return True 88 | 89 | return False 90 | 91 | 92 | def rectangle(x, y, w, h, other=None): 93 | """ 94 | Return a list of objects colliding with a rectangle. 95 | 96 | Parameters: 97 | 98 | - ``x`` -- The horizontal position of the rectangle. 99 | - ``y`` -- The vertical position of the rectangle. 100 | - ``w`` -- The width of the rectangle. 101 | - ``h`` -- The height of the rectangle. 102 | - ``other`` -- What to check for collisions with. See the 103 | documentation for :meth:`sge.dsp.Object.collision` for more 104 | information. 105 | """ 106 | room = sge.game.current_room 107 | others = room.get_objects_at(x, y, w, h) 108 | collisions = [] 109 | mask_id = ("rectangle_masks", x, y, w, h) 110 | 111 | mask = r.cache.get(mask_id) 112 | if mask is None: 113 | mask = [[True for j in range(int(h))] for i in range(int(w))] 114 | 115 | r.cache.add(mask_id, mask) 116 | 117 | for obj in others: 118 | if obj.tangible and r.o_is_other(obj, other): 119 | if obj.collision_precise or obj.collision_ellipse: 120 | if masks_collide(x, y, mask, obj.mask_x, obj.mask_y, obj.mask): 121 | collisions.append(obj) 122 | else: 123 | if rectangles_collide(x, y, w, h, obj.bbox_left, obj.bbox_top, 124 | obj.bbox_width, obj.bbox_height): 125 | collisions.append(obj) 126 | 127 | return collisions 128 | 129 | 130 | def ellipse(x, y, w, h, other=None): 131 | """ 132 | Return a list of objects colliding with an ellipse. 133 | 134 | Parameters: 135 | 136 | - ``x`` -- The horizontal position of the imaginary rectangle 137 | containing the ellipse. 138 | - ``y`` -- The vertical position of the imaginary rectangle 139 | containing the ellipse. 140 | - ``w`` -- The width of the ellipse. 141 | - ``h`` -- The height of the ellipse. 142 | - ``other`` -- What to check for collisions with. See the 143 | documentation for :meth:`sge.dsp.Object.collision` for more 144 | information. 145 | """ 146 | room = sge.game.current_room 147 | others = room.get_objects_at(x, y, w, h) 148 | collisions = [] 149 | mask_id = ("ellipse_masks", x, y, w, h) 150 | 151 | mask = r.cache.get(mask_id) 152 | 153 | if mask is None: 154 | mask = [[False for j in range(int(h))] for i in range(int(w))] 155 | a = len(mask) / 2 156 | b = len(mask[0]) / 2 if mask else 0 157 | 158 | for i, column in enumerate(mask): 159 | for j, row in enumerate(column): 160 | if ((i - a) / a) ** 2 + ((j - b) / b) ** 2 <= 1: 161 | mask[i][j] = True 162 | 163 | r.cache.add(mask_id, mask) 164 | 165 | for obj in others: 166 | if (obj.tangible and r.o_is_other(obj, other) and 167 | masks_collide(x, y, mask, obj.mask_x, obj.mask_y, obj.mask)): 168 | collisions.append(obj) 169 | 170 | return collisions 171 | 172 | 173 | def circle(x, y, radius, other=None): 174 | """ 175 | Return a list of objects colliding with a circle. 176 | 177 | Parameters: 178 | 179 | - ``x`` -- The horizontal position of the center of the circle. 180 | - ``y`` -- The vertical position of the center of the circle. 181 | - ``radius`` -- The radius of the circle. 182 | - ``other`` -- What to check for collisions with. See the 183 | documentation for :meth:`sge.dsp.Object.collision` for more 184 | information. 185 | """ 186 | room = sge.game.current_room 187 | diameter = radius * 2 188 | others = room.get_objects_at(x - radius, y - radius, diameter, diameter) 189 | collisions = [] 190 | mask_id = ("circle_masks", x, y, radius) 191 | 192 | mask = r.cache.get(mask_id) 193 | 194 | if mask is None: 195 | mask = [[False for j in range(int(diameter))] 196 | for i in range(int(diameter))] 197 | 198 | for i, column in enumerate(mask): 199 | for j, row in enumerate(column): 200 | if (i - x) ** 2 + (j - y) ** 2 <= radius ** 2: 201 | mask[i][j] = True 202 | 203 | r.cache.add(mask_id, mask) 204 | 205 | for obj in others: 206 | if (obj.tangible and r.o_is_other(obj, other) and 207 | masks_collide(x - radius, y - radius, mask, obj.mask_x, 208 | obj.mask_y, obj.mask)): 209 | collisions.append(obj) 210 | 211 | return collisions 212 | 213 | 214 | def line(x1, y1, x2, y2, other=None): 215 | """ 216 | Return a list of objects colliding with a line segment. 217 | 218 | Parameters: 219 | 220 | - ``x1`` -- The horizontal position of the first endpoint of the 221 | line segment. 222 | - ``y1`` -- The vertical position of the first endpoint of the line 223 | segment. 224 | - ``x2`` -- The horizontal position of the second endpoint of the 225 | line segment. 226 | - ``y2`` -- The vertical position of the second endpoint of the line 227 | segment. 228 | - ``other`` -- What to check for collisions with. See the 229 | documentation for :meth:`sge.dsp.Object.collision` for more 230 | information. 231 | """ 232 | room = sge.game.current_room 233 | x = min(x1, x2) 234 | y = min(y1, y2) 235 | w = abs(x2 - x1) + 1 236 | h = abs(y2 - y1) + 1 237 | 238 | if w <= 1 or h <= 1: 239 | return rectangle(x, y, w, h) 240 | 241 | others = room.get_objects_at(x, y, w, h) 242 | collisions = [] 243 | mask_id = ("line_masks", x1 - x, y1 - y, x2 - x, y2 - y, w, h) 244 | 245 | mask = r.cache.get(mask_id) 246 | 247 | if mask is None: 248 | sp = sge.gfx.Sprite(width=w, height=h) 249 | sp.draw_line(x1 - x, y1 - y, x2 - x, y2 - y, sge.gfx.Color("white")) 250 | mask = s_get_precise_mask(sp, 0, 1, 1, 0) 251 | 252 | r.cache.add(mask_id, mask) 253 | 254 | for obj in others: 255 | if (obj.tangible and r.o_is_other(obj, other) and 256 | masks_collide(x, y, mask, obj.mask_x, obj.mask_y, obj.mask)): 257 | collisions.append(obj) 258 | 259 | return collisions 260 | -------------------------------------------------------------------------------- /sge/input.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides input event classes. Input event objects are used 18 | to consolidate all necessary information about input events in a clean 19 | way. 20 | 21 | You normally don't need to use input event objects directly. Input 22 | events are handled automatically in each frame of the SGE's main loop. 23 | You only need to use input event objects directly if you take control 24 | away from the SGE's main loop, e.g. to create your own loop. 25 | """ 26 | 27 | 28 | __all__ = ["KeyPress", "KeyRelease", "MouseMove", "MouseButtonPress", 29 | "MouseButtonRelease", "JoystickAxisMove", "JoystickHatMove", 30 | "JoystickTrackballMove", "JoystickButtonPress", 31 | "JoystickButtonRelease", "JoystickEvent", "KeyboardFocusGain", 32 | "KeyboardFocusLose", "MouseFocusGain", "MouseFocusLose", 33 | "QuitRequest"] 34 | 35 | 36 | class KeyPress: 37 | 38 | """ 39 | This input event represents a key on the keyboard being pressed. 40 | 41 | .. attribute:: key 42 | 43 | The identifier string of the key that was pressed. See the 44 | table in the documentation for :mod:`sge.keyboard`. 45 | 46 | .. attribute:: char 47 | 48 | The unicode string associated with the key press, or an empty 49 | unicode string if no text is associated with the key press. 50 | See the table in the documentation for :mod:`sge.keyboard`. 51 | """ 52 | 53 | def __init__(self, key, char): 54 | self.key = key 55 | self.char = char 56 | 57 | 58 | class KeyRelease: 59 | 60 | """ 61 | This input event represents a key on the keyboard being released. 62 | 63 | .. attribute:: key 64 | 65 | The identifier string of the key that was released. See the 66 | table in the documentation for :class:`sge.input.KeyPress`. 67 | """ 68 | 69 | def __init__(self, key): 70 | self.key = key 71 | 72 | 73 | class MouseMove: 74 | 75 | """ 76 | This input event represents the mouse being moved. 77 | 78 | .. attribute:: x 79 | 80 | The horizontal relative movement of the mouse. 81 | 82 | .. attribute:: y 83 | 84 | The vertical relative movement of the mouse. 85 | """ 86 | 87 | def __init__(self, x, y): 88 | self.x = x 89 | self.y = y 90 | 91 | 92 | class MouseButtonPress: 93 | 94 | """ 95 | This input event represents a mouse button being pressed. 96 | 97 | .. attribute:: button 98 | 99 | The identifier string of the mouse button that was pressed. See 100 | the table below. 101 | 102 | ====================== ================= 103 | Mouse Button Name Identifier String 104 | ====================== ================= 105 | Left mouse button ``"left"`` 106 | Right mouse button ``"right"`` 107 | Middle mouse button ``"middle"`` 108 | Extra mouse button 1 ``"extra1"`` 109 | Extra mouse button 2 ``"extra2"`` 110 | ====================== ================= 111 | """ 112 | 113 | def __init__(self, button): 114 | self.button = button 115 | 116 | 117 | class MouseButtonRelease: 118 | 119 | """ 120 | This input event represents a mouse button being released. 121 | 122 | .. attribute:: button 123 | 124 | The identifier string of the mouse button that was released. See 125 | the table in the documentation for 126 | :class:`sge.input.MouseButtonPress`. 127 | """ 128 | 129 | def __init__(self, button): 130 | self.button = button 131 | 132 | 133 | class MouseWheelMove: 134 | 135 | """ 136 | This input event represents a mouse wheel moving. 137 | 138 | .. attribute:: x 139 | 140 | The horizontal scroll amount, where ``-1`` is to the left, ``1`` 141 | is to the right, and ``0`` is no horizontal scrolling. 142 | 143 | .. attribute:: y 144 | 145 | The vertical scroll amount, where ``-1`` is up, ``1`` is down, 146 | and ``0`` is no vertical scrolling. 147 | """ 148 | 149 | def __init__(self, x, y): 150 | self.x = x 151 | self.y = y 152 | 153 | 154 | class JoystickAxisMove: 155 | 156 | """ 157 | This input event represents a joystick axis moving. 158 | 159 | .. attribute:: js_name 160 | 161 | The name of the joystick. 162 | 163 | .. attribute:: js_id 164 | 165 | The number of the joystick, where ``0`` is the first joystick. 166 | 167 | .. attribute:: axis 168 | 169 | The number of the axis that moved, where ``0`` is the first axis 170 | on the joystick. 171 | 172 | .. attribute:: value 173 | 174 | The tilt of the axis as a float from ``-1`` to ``1``, where ``0`` 175 | is centered, ``-1`` is all the way to the left or up, and ``1`` 176 | is all the way to the right or down. 177 | """ 178 | 179 | def __init__(self, js_name, js_id, axis, value): 180 | self.js_name = js_name 181 | self.js_id = js_id 182 | self.axis = axis 183 | self.value = max(-1.0, min(value, 1.0)) 184 | 185 | 186 | class JoystickHatMove: 187 | 188 | """ 189 | This input event represents a joystick hat moving. 190 | 191 | .. attribute:: js_name 192 | 193 | The name of the joystick. 194 | 195 | .. attribute:: js_id 196 | 197 | The number of the joystick, where ``0`` is the first joystick. 198 | 199 | .. attribute:: hat 200 | 201 | The number of the hat that moved, where ``0`` is the first axis 202 | on the joystick. 203 | 204 | .. attribute:: x 205 | 206 | The horizontal position of the hat, where ``0`` is centered, 207 | ``-1`` is left, and ``1`` is right. 208 | 209 | .. attribute:: y 210 | 211 | The vertical position of the hat, where ``0`` is centered, ``-1`` 212 | is up, and ``1`` is down. 213 | """ 214 | 215 | def __init__(self, js_name, js_id, hat, x, y): 216 | self.js_name = js_name 217 | self.js_id = js_id 218 | self.hat = hat 219 | self.x = x 220 | self.y = y 221 | 222 | 223 | class JoystickTrackballMove: 224 | 225 | """ 226 | This input event represents a joystick trackball moving. 227 | 228 | .. attribute:: js_name 229 | 230 | The name of the joystick. 231 | 232 | .. attribute:: js_id 233 | 234 | The number of the joystick, where ``0`` is the first joystick. 235 | 236 | .. attribute:: ball 237 | 238 | The number of the trackball that moved, where ``0`` is the first 239 | trackball on the joystick. 240 | 241 | .. attribute:: x 242 | 243 | The horizontal relative movement of the trackball. 244 | 245 | .. attribute:: y 246 | 247 | The vertical relative movement of the trackball. 248 | """ 249 | 250 | def __init__(self, js_name, js_id, ball, x, y): 251 | self.js_name = js_name 252 | self.js_id = js_id 253 | self.ball = ball 254 | self.x = x 255 | self.y = y 256 | 257 | 258 | class JoystickButtonPress: 259 | 260 | """ 261 | This input event represents a joystick button being pressed. 262 | 263 | .. attribute:: js_name 264 | 265 | The name of the joystick. 266 | 267 | .. attribute:: js_id 268 | 269 | The number of the joystick, where ``0`` is the first joystick. 270 | 271 | .. attribute:: button 272 | 273 | The number of the button that was pressed, where ``0`` is the 274 | first button on the joystick. 275 | """ 276 | 277 | def __init__(self, js_name, js_id, button): 278 | self.js_name = js_name 279 | self.js_id = js_id 280 | self.button = button 281 | 282 | 283 | class JoystickButtonRelease: 284 | 285 | """ 286 | This input event represents a joystick button being released. 287 | 288 | .. attribute:: js_name 289 | 290 | The name of the joystick. 291 | 292 | .. attribute:: js_id 293 | 294 | The number of the joystick, where ``0`` is the first joystick. 295 | 296 | .. attribute:: button 297 | 298 | The number of the button that was released, where ``0`` is the 299 | first button on the joystick. 300 | """ 301 | 302 | def __init__(self, js_name, js_id, button): 303 | self.js_name = js_name 304 | self.js_id = js_id 305 | self.button = button 306 | 307 | 308 | class JoystickEvent: 309 | 310 | """ 311 | This input event represents the movement of any joystick input. 312 | This makes it possible to treat all joystick inputs the same way, 313 | which can be used to simplify things like control customization. 314 | 315 | .. attribute:: js_name 316 | 317 | The name of the joystick. 318 | 319 | .. attribute:: js_id 320 | 321 | The number of the joystick, where ``0`` is the first joystick. 322 | 323 | .. attribute:: input_type 324 | 325 | The type of joystick control that was moved. Can be one of the 326 | following: 327 | 328 | - ``"axis-"`` -- The tilt of a joystick axis to the left or up 329 | changes. 330 | - ``"axis+"`` -- The tilt of a joystick axis to the right or down 331 | changes. 332 | - ``"axis0"`` -- The tilt of a joystick axis changes. 333 | - ``"hat_left"`` -- Whether or not a joystick hat's position is 334 | to the left changes. 335 | - ``"hat_right"`` -- Whether or not a joystick hat's position is 336 | to the right changes. 337 | - ``"hat_center_x"`` -- Whether or not a joystick hat is 338 | horizontally centered changes. 339 | - ``"hat_up"`` -- Whether or not a joystick hat's position is up 340 | changes. 341 | - ``"hat_down"`` -- Whether or not a joystick hat's position is 342 | down changes. 343 | - ``"hat_center_y"`` -- Whether or not a joystick hat is 344 | vertically centered changes. 345 | - ``"trackball_left"`` -- A joystick trackball is moved left. 346 | - ``"trackball_right"`` -- A joystick trackball is moved right. 347 | - ``"trackball_up"`` -- A joystick trackball is moved up. 348 | - ``"trackball_down"`` -- A joystick trackball is moved down. 349 | - ``"button"`` -- Whether or not a joystick button is pressed 350 | changes. 351 | 352 | .. attribute:: input_id 353 | 354 | The number of the joystick control that was moved, where ``0`` is 355 | the first control of its type on the joystick. 356 | 357 | .. attribute:: value 358 | 359 | The value of the event, which is different depending on the value 360 | of :attr:`input_type`. If :attr:`input_type` is 361 | ``"trackball_left"``, ``"trackball_right"``, ``"trackball_up"``, 362 | or ``"trackball_down"``, this is the relative movement of the 363 | trackball in the respective direction. Otherwise, this is the 364 | new value of the respective control. See the documentation for 365 | :func:`sge.joystick.get_value` for more information. 366 | """ 367 | 368 | def __init__(self, js_name, js_id, input_type, input_id, value): 369 | self.js_name = js_name 370 | self.js_id = js_id 371 | self.input_type = input_type 372 | self.input_id = input_id 373 | self.value = value 374 | 375 | 376 | class KeyboardFocusGain: 377 | 378 | """ 379 | This input event represents the game window gaining keyboard focus. 380 | Keyboard focus is normally needed for keyboard input to be received. 381 | 382 | .. note:: 383 | 384 | On some window systems, such as the one used by Windows, no 385 | distinction is made between keyboard and mouse focus, but on 386 | some other window systems, such as the X Window System, a 387 | distinction is made: one window can have keyboard focus while 388 | another has mouse focus. Be careful to observe the 389 | difference; failing to do so may result in annoying bugs, 390 | and you won't notice these bugs if you are testing on a 391 | window manager that doesn't recognize the difference. 392 | """ 393 | 394 | 395 | class KeyboardFocusLose: 396 | 397 | """ 398 | This input event represents the game window losing keyboard focus. 399 | Keyboard focus is normally needed for keyboard input to be received. 400 | 401 | .. note:: 402 | 403 | See the note in the documentation for 404 | :class:`sge.input.KeyboardFocusGain`. 405 | """ 406 | 407 | 408 | class MouseFocusGain: 409 | 410 | """ 411 | This input event represents the game window gaining mouse focus. 412 | Mouse focus is normally needed for mouse input to be received. 413 | 414 | .. note:: 415 | 416 | See the note in the documentation for 417 | :class:`sge.input.KeyboardFocusGain`. 418 | """ 419 | 420 | 421 | class MouseFocusLose: 422 | 423 | """ 424 | This input event represents the game window losing mouse focus. 425 | Mouse focus is normally needed for mouse input to be received. 426 | 427 | .. note:: 428 | 429 | See the note in the documentation for 430 | :class:`sge.input.KeyboardFocusGain`. 431 | """ 432 | 433 | 434 | class WindowResize: 435 | 436 | """ 437 | This input event represents the player resizing the window. 438 | """ 439 | 440 | 441 | class QuitRequest: 442 | 443 | """ 444 | This input event represents the OS requesting for the program to 445 | close (e.g. when the user presses a "close" button on the window 446 | border). 447 | """ 448 | -------------------------------------------------------------------------------- /sge/joystick.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides functions related to joystick input. 18 | """ 19 | 20 | 21 | __all__ = ["refresh", "get_axis", "get_hat_x", "get_hat_y", 22 | "get_pressed", "get_value", "get_joysticks", "get_name", 23 | "get_id", "get_axes", "get_hats", "get_trackballs", "get_buttons"] 24 | 25 | 26 | import pygame 27 | 28 | import sge 29 | from sge import r 30 | 31 | 32 | def refresh(): 33 | """ 34 | Refresh the SGE's knowledge of joysticks. 35 | 36 | Call this method to allow the SGE to use joysticks that were plugged 37 | in while the game was running. 38 | """ 39 | r.game_joysticks = [] 40 | r.game_js_names = {} 41 | r.game_js_ids = {} 42 | pygame.joystick.quit() 43 | pygame.joystick.init() 44 | 45 | if pygame.joystick.get_init(): 46 | for i in range(pygame.joystick.get_count()): 47 | joy = pygame.joystick.Joystick(i) 48 | joy.init() 49 | n = joy.get_name() 50 | r.game_joysticks.append(joy) 51 | r.game_js_names[i] = n 52 | if n not in r.game_js_ids: 53 | r.game_js_ids[n] = i 54 | 55 | 56 | def get_axis(joystick, axis): 57 | """ 58 | Return the position of a joystick axis as a float from ``-1`` to 59 | ``1``, where ``0`` is centered, ``-1`` is all the way to the left or 60 | up, and ``1`` is all the way to the right or down. Return ``0`` if 61 | the requested joystick or axis does not exist. 62 | 63 | Parameters: 64 | 65 | - ``joystick`` -- The number of the joystick to check, where ``0`` 66 | is the first joystick, or the name of the joystick to check. 67 | - ``axis`` -- The number of the axis to check, where ``0`` is the 68 | first axis of the joystick. 69 | """ 70 | joystick = get_id(joystick) 71 | 72 | if (joystick is not None and joystick < len(r.game_joysticks) and 73 | axis < r.game_joysticks[joystick].get_numaxes()): 74 | return max(-1.0, min(r.game_joysticks[joystick].get_axis(axis), 1.0)) 75 | else: 76 | return 0 77 | 78 | 79 | def get_hat_x(joystick, hat): 80 | """ 81 | Return the horizontal position of a joystick hat (d-pad). Can be 82 | ``-1`` (left), ``0`` (centered), or ``1`` (right). Return ``0`` if 83 | the requested joystick or hat does not exist. 84 | 85 | Parameters: 86 | 87 | - ``joystick`` -- The number of the joystick to check, where ``0`` 88 | is the first joystick, or the name of the joystick to check. 89 | - ``hat`` -- The number of the hat to check, where ``0`` is the 90 | first hat of the joystick. 91 | """ 92 | return r._get_hat(get_id(joystick), hat)[0] 93 | 94 | 95 | def get_hat_y(joystick, hat): 96 | """ 97 | Return the vertical position of a joystick hat (d-pad). Can be 98 | ``-1`` (up), ``0`` (centered), or ``1`` (down). Return ``0`` if the 99 | requested joystick or hat does not exist. 100 | 101 | Parameters: 102 | 103 | - ``joystick`` -- The number of the joystick to check, where ``0`` 104 | is the first joystick, or the name of the joystick to check. 105 | - ``hat`` -- The number of the hat to check, where ``0`` is the 106 | first hat of the joystick. 107 | """ 108 | return -r._get_hat(get_id(joystick), hat)[1] 109 | 110 | 111 | def get_pressed(joystick, button): 112 | """ 113 | Return whether or not a joystick button is pressed, or 114 | :const:`False` if the requested joystick or button does not exist. 115 | 116 | Parameters: 117 | 118 | - ``joystick`` -- The number of the joystick to check, where ``0`` 119 | is the first joystick, or the name of the joystick to check. 120 | - ``button`` -- The number of the button to check, where ``0`` is 121 | the first button of the joystick. 122 | """ 123 | joystick = get_id(joystick) 124 | 125 | if (joystick is not None and joystick < len(r.game_joysticks) and 126 | button < r.game_joysticks[joystick].get_numbuttons()): 127 | return r.game_joysticks[joystick].get_button(button) 128 | else: 129 | return False 130 | 131 | 132 | def get_value(joystick, input_type, input_id): 133 | """ 134 | Return the value of any joystick control. This function makes it 135 | possible to treat all joystick inputs the same way, which can be 136 | used to simplify things like control customization. 137 | 138 | Parameters: 139 | 140 | - ``joystick`` -- The number of the joystick to check, where ``0`` 141 | is the first joystick, or the name of the joystick to check. 142 | - ``input_type`` -- The type of joystick control to check. Can be 143 | one of the following: 144 | 145 | - ``"axis-"`` -- The tilt of an axis to the left or up as a float, 146 | where ``0`` is not tilted in this direction at all and ``1`` is 147 | tilted entirely in this direction. 148 | - ``"axis+"`` -- The tilt of an axis to the right or down as a 149 | float, where ``0`` is not tilted in this direction at all and 150 | ``1`` is tilted entirely in this direction. 151 | - ``"axis0"`` -- The distance of the tilt of an axis from the 152 | nearest extreme edge, where ``0`` is tilted entirely in a 153 | direction and ``1`` is completely centered. 154 | - ``"hat_left"`` -- Whether or not the left direction of a 155 | joystick hat (d-pad) is pressed. 156 | - ``"hat_right"`` -- Whether or not the right direction of a 157 | joystick hat (d-pad) is pressed. 158 | - ``"hat_center_x" -- Whether or not a joystick hat (d-pad) is 159 | horizontally centered. 160 | - ``"hat_up"`` -- Whether or not the up direction of a joystick 161 | hat (d-pad) is pressed. 162 | - ``"hat_down"`` -- Whether or not the down direction of a 163 | joystick hat (d-pad) is pressed. 164 | - ``"hat_center_y" -- Whether or not a joystick hat (d-pad) is 165 | vertically centered. 166 | - ``"button"`` -- Whether or not a joystick button is pressed. 167 | 168 | If an invalid type is specified, ``None`` is returned. 169 | 170 | - ``input_id`` -- The number of the control to check, where ``0`` is 171 | the first control of its type on the joystick. 172 | """ 173 | if input_type == "axis-": 174 | return abs(min(0, get_axis(joystick, input_id))) 175 | elif input_type == "axis+": 176 | return abs(max(0, get_axis(joystick, input_id))) 177 | elif input_type == "axis0": 178 | return 1 - abs(get_axis(joystick, input_id)) 179 | elif input_type == "hat_left": 180 | return get_hat_x(joystick, input_id) == -1 181 | elif input_type == "hat_right": 182 | return get_hat_x(joystick, input_id) == 1 183 | elif input_type == "hat_center_x": 184 | return get_hat_x(joystick, input_id) == 0 185 | elif input_type == "hat_up": 186 | return get_hat_y(joystick, input_id) == -1 187 | elif input_type == "hat_down": 188 | return get_hat_y(joystick, input_id) == 1 189 | elif input_type == "hat_center_y": 190 | return get_hat_y(joystick, input_id) == 0 191 | elif input_type == "button": 192 | return get_pressed(joystick, input_id) 193 | else: 194 | return None 195 | 196 | 197 | def get_joysticks(): 198 | """Return the number of joysticks available.""" 199 | return len(r.game_joysticks) 200 | 201 | 202 | def get_name(joystick): 203 | """ 204 | Return the name of a joystick, or ``None`` if the requested joystick 205 | does not exist. 206 | 207 | Parameters: 208 | 209 | - ``joystick`` -- The number of the joystick to check, where ``0`` 210 | is the first joystick, or the name of the joystick to check. 211 | """ 212 | if isinstance(joystick, int): 213 | return r.game_js_names.setdefault(joystick) 214 | elif joystick in r.game_js_names.values(): 215 | return joystick 216 | else: 217 | return None 218 | 219 | 220 | def get_id(joystick): 221 | """ 222 | Return the number of a joystick, where ``0`` is the first joystick, 223 | or ``None`` if the requested joystick does not exist. 224 | 225 | Parameters: 226 | 227 | - ``joystick`` -- The number of the joystick to check, where ``0`` 228 | is the first joystick, or the name of the joystick to check. 229 | """ 230 | if not isinstance(joystick, int): 231 | return r.game_js_ids.setdefault(joystick) 232 | elif joystick in r.game_js_names: 233 | return joystick 234 | else: 235 | return None 236 | 237 | 238 | def get_axes(joystick): 239 | """ 240 | Return the number of axes available on a joystick, or ``0`` if the 241 | requested joystick does not exist. 242 | 243 | Parameters: 244 | 245 | - ``joystick`` -- The number of the joystick to check, where ``0`` 246 | is the first joystick, or the name of the joystick to check. 247 | """ 248 | joystick = get_id(joystick) 249 | 250 | if joystick is not None and joystick < len(r.game_joysticks): 251 | return r.game_joysticks[joystick].get_numaxes() 252 | else: 253 | return 0 254 | 255 | 256 | def get_hats(joystick): 257 | """ 258 | Return the number of hats (d-pads) available on a joystick, or ``0`` 259 | if the requested joystick does not exist. 260 | 261 | Parameters: 262 | 263 | - ``joystick`` -- The number of the joystick to check, where ``0`` 264 | is the first joystick, or the name of the joystick to check. 265 | """ 266 | joystick = get_id(joystick) 267 | 268 | if joystick is not None and joystick < len(r.game_joysticks): 269 | return r.game_joysticks[joystick].get_numhats() 270 | else: 271 | return 0 272 | 273 | 274 | def get_trackballs(joystick): 275 | """ 276 | Return the number of trackballs available on a joystick, or ``0`` if 277 | the requested joystick does not exist. 278 | 279 | Parameters: 280 | 281 | - ``joystick`` -- The number of the joystick to check, where ``0`` 282 | is the first joystick, or the name of the joystick to check. 283 | """ 284 | joystick = get_id(joystick) 285 | 286 | if joystick is not None and joystick < len(r.game_joysticks): 287 | return r.game_joysticks[joystick].get_numballs() 288 | else: 289 | return 0 290 | 291 | 292 | def get_buttons(joystick): 293 | """ 294 | Return the number of buttons available on a joystick, or ``0`` if 295 | the requested joystick does not exist. 296 | 297 | Parameters: 298 | 299 | - ``joystick`` -- The number of the joystick to check, where ``0`` 300 | is the first joystick, or the name of the joystick to check. 301 | """ 302 | joystick = get_id(joystick) 303 | 304 | if joystick is not None and joystick < len(r.game_joysticks): 305 | return r.game_joysticks[joystick].get_numbuttons() 306 | else: 307 | return 0 308 | -------------------------------------------------------------------------------- /sge/keyboard.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides functions related to keyboard input. 18 | 19 | As a general rule, any key press has two strings associated with it: an 20 | identifier string, and a unicode string. The identifier string is a 21 | consistent identifier for what the key is, consisting only of 22 | alphanumeric ASCII text. The unicode string is the text associated with 23 | the key press; typically this is an ASCII character printed on the key, 24 | but in some cases (e.g. when an input method is used), it could be any 25 | kind of text. As a general rule, the unicode string should always be 26 | used for text entry, while the identifier string should be used for all 27 | other purposes. 28 | 29 | The table below lists all standard keys along with their corresponding 30 | identifier and unicode strings. Note that SGE implementations are not 31 | necessarily required to support recognizing all of them, although they 32 | are strongly encouraged to do so. Any key not found on this table, if 33 | detected, will be arbitrarily but consistently assigned an identifier 34 | string beginning with ``"undef_"``. 35 | 36 | ==================== ======================= ============== 37 | Key Name Identifier String Unicode String 38 | ==================== ======================= ============== 39 | 0 ``"0"`` ``"0"`` 40 | 1 ``"1"`` ``"1"`` 41 | 2 ``"2"`` ``"2"`` 42 | 3 ``"3"`` ``"3"`` 43 | 4 ``"4"`` ``"4"`` 44 | 5 ``"5"`` ``"5"`` 45 | 6 ``"6"`` ``"6"`` 46 | 7 ``"7"`` ``"7"`` 47 | 8 ``"8"`` ``"8"`` 48 | 9 ``"9"`` ``"9"`` 49 | A ``"a"`` ``"a"`` 50 | B ``"b"`` ``"b"`` 51 | C ``"c"`` ``"c"`` 52 | D ``"d"`` ``"d"`` 53 | E ``"e"`` ``"e"`` 54 | F ``"f"`` ``"f"`` 55 | G ``"g"`` ``"g"`` 56 | H ``"h"`` ``"h"`` 57 | I ``"i"`` ``"i"`` 58 | J ``"j"`` ``"j"`` 59 | K ``"k"`` ``"k"`` 60 | L ``"l"`` ``"l"`` 61 | M ``"m"`` ``"m"`` 62 | N ``"n"`` ``"n"`` 63 | O ``"o"`` ``"o"`` 64 | P ``"p"`` ``"p"`` 65 | Q ``"q"`` ``"q"`` 66 | R ``"r"`` ``"r"`` 67 | S ``"s"`` ``"s"`` 68 | T ``"t"`` ``"t"`` 69 | U ``"u"`` ``"u"`` 70 | V ``"v"`` ``"v"`` 71 | W ``"w"`` ``"w"`` 72 | X ``"x"`` ``"x"`` 73 | Y ``"y"`` ``"y"`` 74 | Z ``"z"`` ``"z"`` 75 | Period ``"period"`` ``"."`` 76 | Comma ``"comma"`` ``","`` 77 | Less Than ``"less_than"`` ``"<"`` 78 | Greater Than ``"greater_than"`` ``">"`` 79 | Forward Slash ``"slash"`` ``"/"`` 80 | Question Mark ``"question"`` ``"?"`` 81 | Apostrophe ``"apostrophe"`` ``"'"`` 82 | Quotation Mark ``"quote"`` ``'"'`` 83 | Colon ``"colon"`` ``":"`` 84 | Semicolon ``"semicolon"`` ``";"`` 85 | Exclamation Point ``"exclamation"`` ``"!"`` 86 | At ``"at"`` ``"@"`` 87 | Hash ``"hash"`` ``"#"`` 88 | Dollar Sign ``"dollar"`` ``"$"`` 89 | Percent Sign ``"percent"`` ``"%"`` 90 | Carat ``"carat"`` ``"^"`` 91 | Ampersand ``"ampersand"`` ``"&"`` 92 | Asterisk ``"asterisk"`` ``"*"`` 93 | Left Parenthesis ``"parenthesis_left"`` ``"("`` 94 | Right Parenthesis ``"parenthesis_right"`` ``")"`` 95 | Hyphen ``"hyphen"`` ``"-"`` 96 | Underscore ``"underscore"`` ``"_"`` 97 | Plus Sign ``"plus"`` ``"+"`` 98 | Equals Sign ``"equals"`` ``"="`` 99 | Left Bracket ``"bracket_left"`` ``"["`` 100 | Right Bracket ``"bracket_right"`` ``"]"`` 101 | Left Brace ``"brace_left"`` ``"{"`` 102 | Right Brace ``"brace_right"`` ``"}"`` 103 | Backslash ``"backslash"`` ``"\\\\"`` 104 | Backtick ``"backtick"`` ``"`"`` 105 | Euro ``"euro"`` ``"\\u20ac"`` 106 | Keypad 0 ``"kp_0"`` ``"0"`` 107 | Keypad 1 ``"kp_1"`` ``"1"`` 108 | Keypad 2 ``"kp_2"`` ``"2"`` 109 | Keypad 3 ``"kp_3"`` ``"3"`` 110 | Keypad 4 ``"kp_4"`` ``"4"`` 111 | Keypad 5 ``"kp_5"`` ``"5"`` 112 | Keypad 6 ``"kp_6"`` ``"6"`` 113 | Keypad 7 ``"kp_7"`` ``"7"`` 114 | Keypad 8 ``"kp_8"`` ``"8"`` 115 | Keypad 9 ``"kp_9"`` ``"9"`` 116 | Keypad Decimal Point ``"kp_point"`` ``"."`` 117 | Keypad Plus ``"kp_plus"`` ``"+"`` 118 | Keypad Minus ``"kp_minus"`` ``"-"`` 119 | Keypad Multiply ``"kp_multiply"`` ``"*"`` 120 | Keypad Divide ``"kp_divide"`` ``"/"`` 121 | Keypad Equals ``"kp_equals"`` ``"="`` 122 | Keypad Enter ``"kp_enter"`` ``"\\n"`` 123 | Left Arrow ``"left"`` ``""`` 124 | Right Arrow ``"right"`` ``""`` 125 | Up Arrow ``"up"`` ``""`` 126 | Down Arrow ``"down"`` ``""`` 127 | Home ``"home"`` ``""`` 128 | End ``"end"`` ``""`` 129 | Page Up ``"pageup"`` ``""`` 130 | Page Down ``"pagedown"`` ``""`` 131 | Tab ``"tab"`` ``"\\t"`` 132 | Space Bar ``"space"`` ``" "`` 133 | Enter/Return ``"enter"`` ``"\\n"`` 134 | Backspace ``"backspace"`` ``"\\b"`` 135 | Delete ``"delete"`` ``""`` 136 | Left Shift ``"shift_left"`` ``""`` 137 | Right Shift ``"shift_right"`` ``""`` 138 | Left Ctrl ``"ctrl_left"`` ``""`` 139 | Right Ctrl ``"ctrl_right"`` ``""`` 140 | Left Alt ``"alt_left"`` ``""`` 141 | Right Alt ``"alt_right"`` ``""`` 142 | Left Meta ``"meta_left"`` ``""`` 143 | Right Meta ``"meta_right"`` ``""`` 144 | Caps Lock ``"caps_lock"`` ``""`` 145 | Esc ``"escape"`` ``""`` 146 | Num Lock ``"num_lock"`` ``""`` 147 | Scroll Lock ``"scroll_lock"`` ``""`` 148 | Break ``"break"`` ``""`` 149 | Insert ``"insert"`` ``""`` 150 | Pause ``"pause"`` ``""`` 151 | Print Screen ``"print_screen"`` ``""`` 152 | SysRq ``"sysrq"`` ``""`` 153 | F1 ``"f1"`` ``""`` 154 | F2 ``"f2"`` ``""`` 155 | F3 ``"f3"`` ``""`` 156 | F4 ``"f4"`` ``""`` 157 | F5 ``"f5"`` ``""`` 158 | F6 ``"f6"`` ``""`` 159 | F7 ``"f7"`` ``""`` 160 | F8 ``"f8"`` ``""`` 161 | F9 ``"f9"`` ``""`` 162 | F10 ``"f10"`` ``""`` 163 | F11 ``"f11"`` ``""`` 164 | F12 ``"f12"`` ``""`` 165 | ==================== ======================= ============== 166 | """ 167 | 168 | 169 | __all__ = ["get_pressed", "get_modifier", "get_focused", "set_repeat", 170 | "get_repeat_enabled", "get_repeat_interval", "get_repeat_delay"] 171 | 172 | 173 | import pygame 174 | 175 | import sge 176 | 177 | 178 | _repeat_enabled = False 179 | 180 | 181 | def get_pressed(key): 182 | """ 183 | Return whether or not a key is pressed. 184 | 185 | Parameters: 186 | 187 | - ``key`` -- The identifier string of the modifier key to check; see 188 | the table in the documentation for :mod:`sge.keyboard`. 189 | """ 190 | key = key.lower() 191 | if key in sge.KEYS: 192 | return pygame.key.get_pressed()[sge.KEYS[key]] 193 | else: 194 | return False 195 | 196 | 197 | def get_modifier(key): 198 | """ 199 | Return whether or not a modifier key is being held. 200 | 201 | Parameters: 202 | 203 | - ``key`` -- The identifier string of the modifier key to check; see 204 | the table below. 205 | 206 | ================= ================= 207 | Modifier Key Name Identifier String 208 | ================= ================= 209 | Alt ``"alt"`` 210 | Left Alt ``"alt_left"`` 211 | Right Alt ``"alt_right"`` 212 | Ctrl ``"ctrl"`` 213 | Left Ctrl ``"ctrl_left"`` 214 | Right Ctrl ``"ctrl_right"`` 215 | Meta ``"meta"`` 216 | Left Meta ``"meta_left"`` 217 | Right Meta ``"meta_right"`` 218 | Shift ``"shift"`` 219 | Left Shift ``"shift_left"`` 220 | Right Shift ``"shift_right"`` 221 | Mode ``"mode"`` 222 | Caps Lock ``"caps_lock"`` 223 | Num Lock ``"num_lock"`` 224 | ================= ================= 225 | """ 226 | key = key.lower() 227 | if key in sge.MODS: 228 | return pygame.key.get_mods() & sge.MODS[key] 229 | else: 230 | return False 231 | 232 | 233 | def get_focused(): 234 | """Return whether or not the game has keyboard focus.""" 235 | return pygame.key.get_focused() 236 | 237 | 238 | def set_repeat(enabled=True, interval=0, delay=0): 239 | """ 240 | Set repetition of key press events. 241 | 242 | Parameters: 243 | 244 | - ``enabled`` -- Whether or not to enable repetition of key press 245 | events. 246 | - ``interval`` -- The time in milliseconds in between each repeated 247 | key press event. 248 | - ``delay`` -- The time in milliseconds to wait after the first key 249 | press event before repeating key press events. 250 | 251 | If ``enabled`` is set to true, this causes a key being held down to 252 | generate additional key press events as long as it remains held 253 | down. 254 | """ 255 | global _repeat_enabled 256 | _repeat_enabled = enabled 257 | if enabled: 258 | pygame.key.set_repeat(delay, interval) 259 | else: 260 | pygame.key.set_repeat() 261 | 262 | 263 | def get_repeat_enabled(): 264 | """ 265 | Return whether or not repetition of key press events is enabled. 266 | 267 | See the documentation for :func:`sge.keyboard.set_repeat` for more 268 | information. 269 | """ 270 | return _repeat_enabled 271 | 272 | 273 | def get_repeat_interval(): 274 | """ 275 | Return the interval in between each repeated key press event. 276 | 277 | See the documentation for :func:`sge.keyboard.set_repeat` for more 278 | information. 279 | """ 280 | return pygame.key.get_repeat()[1] 281 | 282 | 283 | def get_repeat_delay(): 284 | """ 285 | Return the delay before repeating key press events. 286 | 287 | See the documentation for :func:`sge.keyboard.set_repeat` for more 288 | information. 289 | """ 290 | return pygame.key.get_repeat()[0] 291 | -------------------------------------------------------------------------------- /sge/mouse.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides functions related to the mouse input. 18 | 19 | Some other mouse functionalities are provided through attributes of 20 | :attr:`sge.game.mouse`. These attributes are listed below. 21 | 22 | The mouse can be in either absolute or relative mode. In absolute mode, 23 | the mouse has a position. In relative mode, the mouse only moves. 24 | Which mode the mouse is in depends on the values of 25 | :attr:`sge.game.grab_input` and :attr:`sge.game.mouse.visible`. 26 | 27 | .. attribute:: sge.game.mouse.x 28 | sge.game.mouse.y 29 | 30 | If the mouse is in absolute mode and within a view port, these 31 | attributes indicate the 32 | position of the mouse in the room, based on its proximity to the view 33 | it is in. Otherwise, they will return ``-1``. 34 | 35 | These attributes can be assigned to safely, but doing so will not 36 | have any effect. 37 | 38 | .. attribute:: sge.game.mouse.z 39 | 40 | The Z-axis position of the mouse cursor's projection in relation to 41 | other window projections. The default value is ``10000``. 42 | 43 | .. attribute:: sge.game.mouse.sprite 44 | 45 | Determines what sprite will be used to represent the mouse cursor. 46 | Set to ``None`` for the default mouse cursor. 47 | 48 | .. attribute:: sge.game.mouse.visible 49 | 50 | Controls whether or not the mouse cursor is visible. If this is 51 | :const:`False` and :attr:`sge.game.grab_input` is :const:`True`, the 52 | mouse will be in relative mode. Otherwise, the mouse will be in 53 | absolute mode. 54 | """ 55 | 56 | 57 | __all__ = ["get_pressed", "get_x", "get_y", "set_x", "set_y"] 58 | 59 | 60 | import pygame 61 | 62 | import sge 63 | from sge import r 64 | 65 | 66 | def get_pressed(button): 67 | """ 68 | Return whether or not a mouse button is pressed. 69 | 70 | See the documentation for :class:`sge.input.MouseButtonPress` for 71 | more information. 72 | """ 73 | b = {"left": 0, "middle": 1, "right": 2}.setdefault(button.lower()) 74 | if b is not None: 75 | return pygame.mouse.get_pressed()[b] 76 | else: 77 | return False 78 | 79 | 80 | def get_x(): 81 | """ 82 | Return the horizontal location of the mouse cursor. 83 | 84 | The location returned is relative to the window, excluding any 85 | scaling, pillarboxes, and letterboxes. If the mouse is in 86 | relative mode, this function returns ``None``. 87 | """ 88 | if sge.game.grab_input and not sge.game.mouse.visible: 89 | return None 90 | else: 91 | return (pygame.mouse.get_pos()[0] - r.game_x) / r.game_xscale 92 | 93 | 94 | def get_y(): 95 | """ 96 | Return the vertical location of the mouse cursor. 97 | 98 | The location returned is relative to the window, excluding any 99 | scaling, pillarboxes, and letterboxes. If the mouse is in 100 | relative mode, this function returns ``None``. 101 | """ 102 | if sge.game.grab_input and not sge.game.mouse.visible: 103 | return None 104 | else: 105 | return (pygame.mouse.get_pos()[1] - r.game_y) / r.game_yscale 106 | 107 | 108 | def set_x(value): 109 | """ 110 | Set the horizontal location of the mouse cursor. 111 | 112 | The location returned is relative to the window, excluding any 113 | scaling, pillarboxes, and letterboxes. If the mouse is in 114 | relative mode, this function has no effect. 115 | """ 116 | if not sge.game.grab_input or sge.game.mouse.visible: 117 | pygame.mouse.set_pos(value * r.game_xscale + r.game_x, 118 | pygame.mouse.get_pos()[1]) 119 | 120 | 121 | def set_y(value): 122 | """ 123 | Set the vertical location of the mouse cursor. 124 | 125 | The location returned is relative to the window, excluding any 126 | scaling, pillarboxes, and letterboxes. If the mouse is in 127 | relative mode, this function has no effect. 128 | """ 129 | if not sge.game.grab_input or sge.game.mouse.visible: 130 | pygame.mouse.set_pos(pygame.mouse.get_pos()[0], 131 | value * r.game_yscale + r.game_y) 132 | -------------------------------------------------------------------------------- /sge/s.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides several variables which represent particular 18 | important strings. The purpose of these variables (listed below) is to 19 | make typos more obvious. 20 | 21 | The values of all of these variables are their names as strings. If a 22 | variable name starts with an underscore, its value excludes this 23 | preceding underscore. For example, a variable called ``spam`` would 24 | have a value of ``"spam"``, and a variable called ``_1984`` would have a 25 | value of ``"1984"``. 26 | 27 | This module only contains these variable assignments and nothing else, 28 | so if it is useful to you, you can add assignments to this module as 29 | long as said assignments conform with the specification above. 30 | 31 | .. note:: 32 | 33 | This module does not use the convention of marking "private" members 34 | with a leading underscore; rather, a leading underscore is used to 35 | ensure that strings starting with numeric characters can be 36 | supported. All variables in this module are available for use. 37 | 38 | .. data:: sge.s._0 39 | .. data:: sge.s._1 40 | .. data:: sge.s._2 41 | .. data:: sge.s._3 42 | .. data:: sge.s._4 43 | .. data:: sge.s._5 44 | .. data:: sge.s._6 45 | .. data:: sge.s._7 46 | .. data:: sge.s._8 47 | .. data:: sge.s._9 48 | .. data:: sge.s.a 49 | .. data:: sge.s.b 50 | .. data:: sge.s.c 51 | .. data:: sge.s.d 52 | .. data:: sge.s.e 53 | .. data:: sge.s.f 54 | .. data:: sge.s.g 55 | .. data:: sge.s.h 56 | .. data:: sge.s.i 57 | .. data:: sge.s.j 58 | .. data:: sge.s.k 59 | .. data:: sge.s.l 60 | .. data:: sge.s.m 61 | .. data:: sge.s.n 62 | .. data:: sge.s.o 63 | .. data:: sge.s.p 64 | .. data:: sge.s.q 65 | .. data:: sge.s.r 66 | .. data:: sge.s.s 67 | .. data:: sge.s.t 68 | .. data:: sge.s.u 69 | .. data:: sge.s.v 70 | .. data:: sge.s.w 71 | .. data:: sge.s.x 72 | .. data:: sge.s.y 73 | .. data:: sge.s.z 74 | .. data:: sge.s._break 75 | .. data:: sge.s.alt_left 76 | .. data:: sge.s.alt_right 77 | .. data:: sge.s.ampersand 78 | .. data:: sge.s.apostrophe 79 | .. data:: sge.s.aqua 80 | .. data:: sge.s.asterisk 81 | .. data:: sge.s.at 82 | .. data:: sge.s.axis0 83 | .. data:: sge.s.backslash 84 | .. data:: sge.s.backspace 85 | .. data:: sge.s.backtick 86 | .. data:: sge.s.black 87 | .. data:: sge.s.blue 88 | .. data:: sge.s.bottom 89 | .. data:: sge.s.brace_left 90 | .. data:: sge.s.brace_right 91 | .. data:: sge.s.bracket_left 92 | .. data:: sge.s.bracket_right 93 | .. data:: sge.s.button 94 | .. data:: sge.s.caps_lock 95 | .. data:: sge.s.carat 96 | .. data:: sge.s.center 97 | .. data:: sge.s.colon 98 | .. data:: sge.s.comma 99 | .. data:: sge.s.ctrl_left 100 | .. data:: sge.s.ctrl_right 101 | .. data:: sge.s.delete 102 | .. data:: sge.s.dissolve 103 | .. data:: sge.s.dollar 104 | .. data:: sge.s.down 105 | .. data:: sge.s.end 106 | .. data:: sge.s.enter 107 | .. data:: sge.s.equals 108 | .. data:: sge.s.escape 109 | .. data:: sge.s.euro 110 | .. data:: sge.s.exclamation 111 | .. data:: sge.s.extra1 112 | .. data:: sge.s.extra2 113 | .. data:: sge.s.f0 114 | .. data:: sge.s.f1 115 | .. data:: sge.s.f2 116 | .. data:: sge.s.f3 117 | .. data:: sge.s.f4 118 | .. data:: sge.s.f5 119 | .. data:: sge.s.f6 120 | .. data:: sge.s.f7 121 | .. data:: sge.s.f8 122 | .. data:: sge.s.f9 123 | .. data:: sge.s.f10 124 | .. data:: sge.s.f11 125 | .. data:: sge.s.f12 126 | .. data:: sge.s.fade 127 | .. data:: sge.s.fuchsia 128 | .. data:: sge.s.gray 129 | .. data:: sge.s.greater_than 130 | .. data:: sge.s.green 131 | .. data:: sge.s.hash 132 | .. data:: sge.s.hat_center_x 133 | .. data:: sge.s.hat_center_y 134 | .. data:: sge.s.hat_down 135 | .. data:: sge.s.hat_left 136 | .. data:: sge.s.hat_right 137 | .. data:: sge.s.hat_up 138 | .. data:: sge.s.hexagonal 139 | .. data:: sge.s.home 140 | .. data:: sge.s.hyphen 141 | .. data:: sge.s.insert 142 | .. data:: sge.s.iris_in 143 | .. data:: sge.s.iris_out 144 | .. data:: sge.s.isohex 145 | .. data:: sge.s.isometric 146 | .. data:: sge.s.kp_0 147 | .. data:: sge.s.kp_1 148 | .. data:: sge.s.kp_2 149 | .. data:: sge.s.kp_3 150 | .. data:: sge.s.kp_4 151 | .. data:: sge.s.kp_5 152 | .. data:: sge.s.kp_6 153 | .. data:: sge.s.kp_7 154 | .. data:: sge.s.kp_8 155 | .. data:: sge.s.kp_9 156 | .. data:: sge.s.kp_divide 157 | .. data:: sge.s.kp_enter 158 | .. data:: sge.s.kp_equals 159 | .. data:: sge.s.kp_minus 160 | .. data:: sge.s.kp_multiply 161 | .. data:: sge.s.kp_plus 162 | .. data:: sge.s.kp_point 163 | .. data:: sge.s.left 164 | .. data:: sge.s.less_than 165 | .. data:: sge.s.lime 166 | .. data:: sge.s.maroon 167 | .. data:: sge.s.meta_left 168 | .. data:: sge.s.meta_right 169 | .. data:: sge.s.middle 170 | .. data:: sge.s.navy 171 | .. data:: sge.s.noblur 172 | .. data:: sge.s.num_lock 173 | .. data:: sge.s.olive 174 | .. data:: sge.s.orthogonal 175 | .. data:: sge.s.pagedown 176 | .. data:: sge.s.pageup 177 | .. data:: sge.s.parenthesis_left 178 | .. data:: sge.s.parenthesis_right 179 | .. data:: sge.s.pause 180 | .. data:: sge.s.percent 181 | .. data:: sge.s.period 182 | .. data:: sge.s.pixelate 183 | .. data:: sge.s.plus 184 | .. data:: sge.s.print_screen 185 | .. data:: sge.s.purple 186 | .. data:: sge.s.question 187 | .. data:: sge.s.quote 188 | .. data:: sge.s.red 189 | .. data:: sge.s.right 190 | .. data:: sge.s.scroll_lock 191 | .. data:: sge.s.semicolon 192 | .. data:: sge.s.shift_left 193 | .. data:: sge.s.shift_right 194 | .. data:: sge.s.silver 195 | .. data:: sge.s.slash 196 | .. data:: sge.s.smooth 197 | .. data:: sge.s.space 198 | .. data:: sge.s.sysrq 199 | .. data:: sge.s.tab 200 | .. data:: sge.s.teal 201 | .. data:: sge.s.top 202 | .. data:: sge.s.trackball_down 203 | .. data:: sge.s.trackball_left 204 | .. data:: sge.s.trackball_right 205 | .. data:: sge.s.trackball_up 206 | .. data:: sge.s.underscore 207 | .. data:: sge.s.up 208 | .. data:: sge.s.wheel_down 209 | .. data:: sge.s.wheel_left 210 | .. data:: sge.s.wheel_right 211 | .. data:: sge.s.wheel_up 212 | .. data:: sge.s.white 213 | .. data:: sge.s.wipe_down 214 | .. data:: sge.s.wipe_downleft 215 | .. data:: sge.s.wipe_downright 216 | .. data:: sge.s.wipe_left 217 | .. data:: sge.s.wipe_matrix 218 | .. data:: sge.s.wipe_right 219 | .. data:: sge.s.wipe_up 220 | .. data:: sge.s.wipe_upleft 221 | .. data:: sge.s.wipe_upright 222 | .. data:: sge.s.yellow 223 | """ 224 | 225 | 226 | # Alignment indicators 227 | left = "left" 228 | right = "right" 229 | center = "center" 230 | top = "top" 231 | bottom = "bottom" 232 | middle = "middle" 233 | 234 | # Color names 235 | white = "white" 236 | silver = "silver" 237 | gray = "gray" 238 | black = "black" 239 | red = "red" 240 | lime = "lime" 241 | blue = "blue" 242 | maroon = "maroon" 243 | green = "green" 244 | navy = "navy" 245 | yellow = "yellow" 246 | fuchsia = "fuchsia" 247 | aqua = "aqua" 248 | olive = "olive" 249 | purple = "purple" 250 | teal = "teal" 251 | 252 | # Scale methods 253 | noblur = "noblur" 254 | smooth = "smooth" 255 | 256 | # Key names 257 | _0 = "0" 258 | _1 = "1" 259 | _2 = "2" 260 | _3 = "3" 261 | _4 = "4" 262 | _5 = "5" 263 | _6 = "6" 264 | _7 = "7" 265 | _8 = "8" 266 | _9 = "9" 267 | a = "a" 268 | b = "b" 269 | c = "c" 270 | d = "d" 271 | e = "e" 272 | f = "f" 273 | g = "g" 274 | h = "h" 275 | i = "i" 276 | j = "j" 277 | k = "k" 278 | l = "l" 279 | m = "m" 280 | n = "n" 281 | o = "o" 282 | p = "p" 283 | q = "q" 284 | r = "r" 285 | s = "s" 286 | t = "t" 287 | u = "u" 288 | v = "v" 289 | w = "w" 290 | x = "x" 291 | y = "y" 292 | z = "z" 293 | period = "period" 294 | comma = "comma" 295 | less_than = "less_than" 296 | greater_than = "greater_than" 297 | slash = "slash" 298 | question = "question" 299 | apostrophe = "apostrophe" 300 | quote = "quote" 301 | colon = "colon" 302 | semicolon = "semicolon" 303 | exclamation = "exclamation" 304 | at = "at" 305 | hash = "hash" 306 | dollar = "dollar" 307 | percent = "percent" 308 | carat = "carat" 309 | ampersand = "ampersand" 310 | asterisk = "asterisk" 311 | parenthesis_left = "parenthesis_left" 312 | parenthesis_right = "parenthesis_right" 313 | hyphen = "hyphen" 314 | underscore = "underscore" 315 | plus = "plus" 316 | equals = "equals" 317 | bracket_left = "bracket_left" 318 | bracket_right = "bracket_right" 319 | brace_left = "brace_left" 320 | brace_right = "brace_right" 321 | backslash = "backslash" 322 | backtick = "backtick" 323 | euro = "euro" 324 | kp_0 = "kp_0" 325 | kp_1 = "kp_1" 326 | kp_2 = "kp_2" 327 | kp_3 = "kp_3" 328 | kp_4 = "kp_4" 329 | kp_5 = "kp_5" 330 | kp_6 = "kp_6" 331 | kp_7 = "kp_7" 332 | kp_8 = "kp_8" 333 | kp_9 = "kp_9" 334 | kp_point = "kp_point" 335 | kp_plus = "kp_plus" 336 | kp_minus = "kp_minus" 337 | kp_multiply = "kp_multiply" 338 | kp_divide = "kp_divide" 339 | kp_equals = "kp_equals" 340 | kp_enter = "kp_enter" 341 | left = "left" 342 | right = "right" 343 | up = "up" 344 | down = "down" 345 | home = "home" 346 | end = "end" 347 | pageup = "pageup" 348 | pagedown = "pagedown" 349 | tab = "tab" 350 | space = "space" 351 | enter = "enter" 352 | backspace = "backspace" 353 | delete = "delete" 354 | shift_left = "shift_left" 355 | shift_right = "shift_right" 356 | ctrl_left = "ctrl_left" 357 | ctrl_right = "ctrl_right" 358 | alt_left = "alt_left" 359 | alt_right = "alt_right" 360 | meta_left = "meta_left" 361 | meta_right = "meta_right" 362 | caps_lock = "caps_lock" 363 | escape = "escape" 364 | num_lock = "num_lock" 365 | scroll_lock = "scroll_lock" 366 | _break = "break" 367 | insert = "insert" 368 | pause = "pause" 369 | print_screen = "print_screen" 370 | sysrq = "sysrq" 371 | f0 = "f0" 372 | f1 = "f1" 373 | f2 = "f2" 374 | f3 = "f3" 375 | f4 = "f4" 376 | f5 = "f5" 377 | f6 = "f6" 378 | f7 = "f7" 379 | f8 = "f8" 380 | f9 = "f9" 381 | f10 = "f10" 382 | f11 = "f11" 383 | f12 = "f12" 384 | 385 | # Mouse buttons 386 | left = "left" 387 | right = "right" 388 | middle = "middle" 389 | wheel_up = "wheel_up" 390 | wheel_down = "wheel_down" 391 | wheel_left = "wheel_left" 392 | wheel_right = "wheel_right" 393 | extra1 = "extra1" 394 | extra2 = "extra2" 395 | 396 | # Joystick input types 397 | # "axis+" and "axis-" unfortunately must be excluded. 398 | axis0 = "axis0" 399 | hat_left = "hat_left" 400 | hat_right = "hat_right" 401 | hat_center_x = "hat_center_x" 402 | hat_up = "hat_up" 403 | hat_down = "hat_down" 404 | hat_center_y = "hat_center_y" 405 | trackball_left = "trackball_left" 406 | trackball_right = "trackball_right" 407 | trackball_up = "trackball_up" 408 | trackball_down = "trackball_down" 409 | button = "button" 410 | 411 | # Transitions 412 | fade = "fade" 413 | dissolve = "dissolve" 414 | pixelate = "pixelate" 415 | wipe_left = "wipe_left" 416 | wipe_right = "wipe_right" 417 | wipe_up = "wipe_up" 418 | wipe_down = "wipe_down" 419 | wipe_upleft = "wipe_upleft" 420 | wipe_upright = "wipe_upright" 421 | wipe_downleft = "wipe_downleft" 422 | wipe_downright = "wipe_downright" 423 | wipe_matrix = "wipe_matrix" 424 | iris_in = "iris_in" 425 | iris_out = "iris_out" 426 | 427 | # Tile grids 428 | hexagonal = "hexagonal" 429 | isohex = "isohex" 430 | isometric = "isometric" 431 | orthogonal = "orthogonal" 432 | 433 | -------------------------------------------------------------------------------- /sge/snd.py: -------------------------------------------------------------------------------- 1 | # This file is part of the Pygame SGE. 2 | # 3 | # The Pygame SGE is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # The Pygame SGE is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with the Pygame SGE. If not, see . 15 | 16 | """ 17 | This module provides classes related to the sound system. 18 | """ 19 | 20 | 21 | __all__ = ["Sound", "Music", "stop_all"] 22 | 23 | 24 | import os 25 | import random 26 | import warnings 27 | 28 | import pygame 29 | 30 | import sge 31 | from sge import r 32 | from sge.r import _get_channel, _release_channel 33 | 34 | 35 | class Sound: 36 | 37 | """ 38 | This class stores and plays sound effects. Note that this is 39 | inefficient for large music files; for those, use 40 | :class:`sge.snd.Music` instead. 41 | 42 | What sound formats are supported depends on the implementation of 43 | the SGE, but sound formats that are generally a good choice are Ogg 44 | Vorbis and uncompressed WAV. See the implementation-specific 45 | information for a full list of supported formats. 46 | 47 | .. attribute:: volume 48 | 49 | The volume of the sound as a value from ``0`` to ``1`` (``0`` for 50 | no sound, ``1`` for maximum volume). 51 | 52 | .. attribute:: max_play 53 | 54 | The maximum number of instances of this sound playing permitted. 55 | If a sound is played while this number of the instances of the 56 | same sound are already playing, one of the already playing sounds 57 | will be stopped before playing the new instance. Set to ``None`` 58 | for no limit. 59 | 60 | .. attribute:: parent 61 | 62 | Indicates another sound which is treated as being the same sound 63 | as this one for the purpose of determining whether or not, and 64 | how many times, the sound is playing. Set to ``None`` for no 65 | parent. 66 | 67 | If the sound has a parent, :attr:`max_play` will have no effect 68 | and instead the parent sound's :attr:`max_play` will apply to 69 | both the parent sound and this sound. 70 | 71 | .. warning:: 72 | 73 | It is acceptable for a sound to both be a parent and have a 74 | parent. However, there MUST be a parent at the top which has 75 | no parent. The behavior of circular parenting, such as making 76 | two sounds parents of each other, is undefined. 77 | 78 | .. attribute:: fname 79 | 80 | The file name of the sound given when it was created. 81 | (Read-only) 82 | 83 | .. attribute:: length 84 | 85 | The length of the sound in milliseconds. (Read-only) 86 | 87 | .. attribute:: playing 88 | 89 | The number of instances of this sound playing. (Read-only) 90 | 91 | .. attribute:: rd 92 | 93 | Reserved dictionary for internal use by the SGE. (Read-only) 94 | """ 95 | 96 | @property 97 | def max_play(self): 98 | return len(self.rd["channels"]) if self.rd["channels"] else None 99 | 100 | @max_play.setter 101 | def max_play(self, value): 102 | self.__max_play = value 103 | 104 | if value is None: 105 | value = 0 106 | 107 | if self.__sound is not None and self.parent is None: 108 | value = max(0, value) 109 | while len(self.rd["channels"]) < value: 110 | self.rd["channels"].append(_get_channel()) 111 | while len(self.rd["channels"]) > value: 112 | _release_channel(self.rd["channels"].pop(-1)) 113 | 114 | @property 115 | def parent(self): 116 | return self.__parent 117 | 118 | @parent.setter 119 | def parent(self, value): 120 | if value != self.__parent: 121 | self.__parent = value 122 | if value is None: 123 | self.rd["channels"] = [] 124 | self.max_play = self.__max_play 125 | else: 126 | while self.rd["channels"]: 127 | _release_channel(self.rd["channels"].pop(-1)) 128 | self.rd["channels"] = value.rd["channels"] 129 | 130 | @property 131 | def length(self): 132 | if self.__sound is not None: 133 | return self.__sound.get_length() * 1000 134 | else: 135 | return 0 136 | 137 | @property 138 | def playing(self): 139 | n = 0 140 | for channel in self.rd["channels"] + self.__temp_channels: 141 | if channel.get_busy(): 142 | n += 1 143 | 144 | return n 145 | 146 | def __init__(self, fname, volume=1, max_play=1, parent=None): 147 | """ 148 | Parameters: 149 | 150 | - ``fname`` -- The path to the sound file. If set to 151 | ``None``, this object will not actually play any sound. If 152 | this is neither a valid sound file nor ``None``, 153 | :exc:`FileNotFoundError` is raised. 154 | 155 | All other arguments set the respective initial attributes of the 156 | sound. See the documentation for :class:`sge.snd.Sound` for 157 | more information. 158 | """ 159 | self.rd = {} 160 | errlist = [] 161 | 162 | if fname is not None and pygame.mixer.get_init(): 163 | try: 164 | self.__sound = pygame.mixer.Sound(fname) 165 | except pygame.error as e: 166 | fname = None 167 | self.__sound = None 168 | raise FileNotFoundError(e) 169 | else: 170 | self.__sound = None 171 | 172 | self.__sound_init = r.sound_init 173 | self.rd["channels"] = [] 174 | self.__temp_channels = [] 175 | self.fname = fname 176 | self.volume = volume 177 | self.__parent = None 178 | if parent is None: 179 | self.max_play = max_play 180 | else: 181 | self.__max_play = max_play 182 | self.parent = parent 183 | 184 | def play(self, loops=1, volume=1, balance=0, maxtime=None, 185 | fade_time=None, force=True): 186 | """ 187 | Play the sound. 188 | 189 | Parameters: 190 | 191 | - ``loops`` -- The number of times to play the sound; set to 192 | ``None`` or ``0`` to loop indefinitely. 193 | - ``volume`` -- The volume to play the sound at as a factor 194 | of :attr:`self.volume` (``0`` for no sound, ``1`` for 195 | :attr:`self.volume`). 196 | - ``balance`` -- The balance of the sound effect on stereo 197 | speakers as a float from ``-1`` to ``1``, where ``0`` is 198 | centered (full volume in both speakers), ``1`` is entirely in 199 | the right speaker, and ``-1`` is entirely in the left speaker. 200 | - ``maxtime`` -- The maximum amount of time to play the sound in 201 | milliseconds; set to ``None`` for no limit. 202 | - ``fade_time`` -- The time in milliseconds over which to fade 203 | the sound in; set to ``None`` or ``0`` to immediately play the 204 | sound at full volume. 205 | - ``force`` -- Whether or not the sound should be played even if 206 | it is already playing the maximum number of times. If set to 207 | :const:`True` and the sound is already playing the maximum 208 | number of times, one of the instances of the sound already 209 | playing will be stopped. 210 | """ 211 | if not pygame.mixer.get_init(): 212 | return 213 | 214 | if self.__sound_init < r.sound_init: 215 | if self.fname is not None: 216 | try: 217 | self.__sound = pygame.mixer.Sound(self.fname) 218 | except pygame.error as e: 219 | self.__sound = None 220 | else: 221 | self.__sound = None 222 | 223 | if self.__sound is None: 224 | return 225 | 226 | if not loops: 227 | loops = 0 228 | if maxtime is None: 229 | maxtime = 0 230 | if fade_time is None: 231 | fade_time = 0 232 | 233 | # Adjust for the way Pygame does repeats 234 | loops -= 1 235 | 236 | # Calculate volume for each speaker 237 | left_volume = min(1, self.volume * volume) 238 | right_volume = left_volume 239 | if balance < 0: 240 | right_volume *= 1 - abs(balance) 241 | elif balance > 0: 242 | left_volume *= 1 - abs(balance) 243 | 244 | if self.max_play: 245 | for channel in self.rd["channels"]: 246 | if not channel.get_busy(): 247 | channel.play(self.__sound, loops, maxtime, fade_time) 248 | channel.set_volume(left_volume, right_volume) 249 | break 250 | else: 251 | if force: 252 | channel = random.choice(self.rd["channels"]) 253 | channel.play(self.__sound, loops, maxtime, fade_time) 254 | channel.set_volume(left_volume, right_volume) 255 | else: 256 | channel = _get_channel() 257 | channel.play(self.__sound, loops, maxtime, fade_time) 258 | channel.set_volume(left_volume, right_volume) 259 | self.__temp_channels.append(channel) 260 | 261 | # Clean up old temporary channels 262 | while (self.__temp_channels and 263 | not self.__temp_channels[0].get_busy()): 264 | _release_channel(self.__temp_channels.pop(0)) 265 | 266 | def stop(self, fade_time=None): 267 | """ 268 | Stop the sound. 269 | 270 | Parameters: 271 | 272 | - ``fade_time`` -- The time in milliseconds over which to fade 273 | the sound out before stopping; set to ``None`` or ``0`` to 274 | immediately stop the sound. 275 | """ 276 | if self.__sound is not None and pygame.mixer.get_init(): 277 | self.__sound.stop() 278 | 279 | def pause(self): 280 | """Pause playback of the sound.""" 281 | for channel in self.rd["channels"]: 282 | channel.pause() 283 | 284 | def unpause(self): 285 | """Resume playback of the sound if paused.""" 286 | for channel in self.rd["channels"]: 287 | channel.unpause() 288 | 289 | 290 | class Music: 291 | 292 | """ 293 | This class stores and plays music. Music is very similar to sound 294 | effects, but only one music file can be played at a time and it is 295 | more efficient for larger files than :class:`sge.snd.Sound`. 296 | 297 | What music formats are supported depends on the implementation of 298 | the SGE, but Ogg Vorbis is generally a good choice. See the 299 | implementation-specific information for a full list of supported 300 | formats. 301 | 302 | .. attribute:: volume 303 | 304 | The volume of the music as a value from ``0`` to ``1`` (``0`` for 305 | no sound, ``1`` for maximum volume). 306 | 307 | .. attribute:: fname 308 | 309 | The file name of the music given when it was created. 310 | (Read-only) 311 | 312 | .. attribute:: length 313 | 314 | The length of the music in milliseconds. (Read-only) 315 | 316 | .. attribute:: playing 317 | 318 | Whether or not the music is playing. (Read-only) 319 | 320 | .. attribute:: position 321 | 322 | The current position (time) playback of the music is at in 323 | milliseconds. (Read-only) 324 | 325 | .. attribute:: rd 326 | 327 | Reserved dictionary for internal use by the SGE. (Read-only) 328 | """ 329 | 330 | @property 331 | def volume(self): 332 | return self.__volume 333 | 334 | @volume.setter 335 | def volume(self, value): 336 | self.__volume = min(value, 1) 337 | 338 | if self.playing: 339 | pygame.mixer.music.set_volume(value) 340 | 341 | @property 342 | def length(self): 343 | if self.__length is None: 344 | if self.fname is not None: 345 | snd = pygame.mixer.Sound(self.fname) 346 | self.__length = snd.get_length() * 1000 347 | else: 348 | self.__length = 0 349 | 350 | return self.__length 351 | 352 | @property 353 | def playing(self): 354 | return r.music is self and pygame.mixer.music.get_busy() 355 | 356 | @property 357 | def position(self): 358 | if self.playing: 359 | return self.__start + pygame.mixer.music.get_pos() 360 | else: 361 | return 0 362 | 363 | def __init__(self, fname, volume=1): 364 | """ 365 | Parameters: 366 | 367 | - ``fname`` -- The path to the sound file. If set to ``None``, 368 | this object will not actually play any music. If this is 369 | neither a valid sound file nor ``None``, 370 | :exc:`FileNotFoundError` is raised. 371 | 372 | All other arguments set the respective initial attributes of the 373 | music. See the documentation for :class:`sge.snd.Music` for 374 | more information. 375 | """ 376 | self.rd = {} 377 | if fname is None or os.path.isfile(fname): 378 | self.fname = fname 379 | else: 380 | raise FileNotFoundError('File "{}" not found.'.format(fname)) 381 | self.volume = volume 382 | self.rd["timeout"] = None 383 | self.rd["fade_time"] = None 384 | self.__start = 0 385 | self.__length = None 386 | 387 | def play(self, start=0, loops=1, maxtime=None, fade_time=None): 388 | """ 389 | Play the music. 390 | 391 | Parameters: 392 | 393 | - ``start`` -- The number of milliseconds from the beginning to 394 | start playing at. 395 | 396 | See the documentation for :meth:`sge.snd.Sound.play` for more 397 | information. 398 | """ 399 | if self.fname is None or not pygame.mixer.get_init(): 400 | return 401 | 402 | if not self.playing: 403 | try: 404 | pygame.mixer.music.load(self.fname) 405 | except pygame.error as e: 406 | warnings.warn(str(e)) 407 | return 408 | 409 | if not loops: 410 | loops = -1 411 | 412 | r.music = self 413 | self.rd["timeout"] = maxtime 414 | self.rd["fade_time"] = fade_time 415 | 416 | if fade_time is not None and fade_time > 0: 417 | pygame.mixer.music.set_volume(0) 418 | else: 419 | pygame.mixer.music.set_volume(self.volume) 420 | 421 | if self.fname.lower().endswith(".mod"): 422 | # MOD music is handled differently in Pygame: it uses 423 | # the pattern order number rather than the time to 424 | # indicate the start time. 425 | self._start = 0 426 | pygame.mixer.music.play(loops, start) 427 | else: 428 | self.__start = start 429 | try: 430 | pygame.mixer.music.play(loops, start / 1000) 431 | except NotImplementedError: 432 | pygame.mixer.music.play(loops) 433 | 434 | def queue(self, start=0, loops=1, maxtime=None, fade_time=None): 435 | """ 436 | Queue the music for playback. 437 | 438 | This will cause the music to be added to a list of music to play 439 | in order, after the previous music has finished playing. 440 | 441 | See the documentation for :meth:`sge.snd.Music.play` for more 442 | information. 443 | """ 444 | r.music_queue.append((self, start, loops, maxtime, fade_time)) 445 | 446 | @staticmethod 447 | def stop(fade_time=None): 448 | """ 449 | Stop the currently playing music. 450 | 451 | See the documentation for :meth:`sge.snd.Sound.stop` for more 452 | information. 453 | """ 454 | if not pygame.mixer.get_init(): 455 | return 456 | 457 | if fade_time: 458 | pygame.mixer.music.fadeout(fade_time) 459 | else: 460 | pygame.mixer.music.stop() 461 | 462 | # ``pygame.mixer.music.stop()`` should push 463 | # ``sge.MUSIC_END_EVENT``, which could cause a track to be 464 | # skipped in the music queue if we're starting a new one. so 465 | # if we have an empty queue, block that event. 466 | if not r.music_queue: 467 | block_event = pygame.event.Event(sge.MUSIC_END_BLOCK_EVENT) 468 | pygame.event.post(block_event) 469 | 470 | @staticmethod 471 | def pause(): 472 | """Pause playback of the currently playing music.""" 473 | pygame.mixer.music.pause() 474 | 475 | @staticmethod 476 | def unpause(): 477 | """Resume playback of the currently playing music if paused.""" 478 | pygame.mixer.music.unpause() 479 | 480 | @staticmethod 481 | def clear_queue(): 482 | """Clear the music queue.""" 483 | r.music_queue = [] 484 | 485 | # Block any existing music end events that have been posted, 486 | # just in case a new music queue is started immediately after in 487 | # the same frame. 488 | if pygame.event.peek(sge.MUSIC_END_EVENT): 489 | block_event = pygame.event.Event(sge.MUSIC_END_BLOCK_EVENT) 490 | pygame.event.post(block_event) 491 | 492 | 493 | def stop_all(): 494 | """Stop playback of all sounds.""" 495 | pygame.mixer.stop() 496 | --------------------------------------------------------------------------------