Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAgustin Zubiaga <aguz@sugarlabs.org>2012-04-09 23:56:12 (GMT)
committer Agustin Zubiaga <aguz@sugarlabs.org>2012-04-09 23:56:12 (GMT)
commit8dae1eb84a8c854f67728c985448fba65656569c (patch)
tree0c9e3dd9a6ec7302767fc6154edf61db9e86b535
Initial commitHEADmaster
Signed-off-by: Agustin Zubiaga <aguz@sugarlabs.org>
-rw-r--r--.gitignore4
-rw-r--r--AUTHORS1
-rw-r--r--GFXLIB_LICENSE263
-rw-r--r--INSTALL63
-rw-r--r--LICENSE165
-rw-r--r--README.txt177
-rw-r--r--activity.py98
-rw-r--r--activity/activity.info7
-rw-r--r--activity/pilas-icon.svg52
-rwxr-xr-xbin/pilas36
-rw-r--r--pilas/__init__.py172
-rw-r--r--pilas/actores/__init__.py71
-rw-r--r--pilas/actores/aceituna.py40
-rw-r--r--pilas/actores/actor.py409
-rw-r--r--pilas/actores/animacion.py60
-rw-r--r--pilas/actores/animado.py40
-rw-r--r--pilas/actores/banana.py45
-rw-r--r--pilas/actores/bomba.py37
-rw-r--r--pilas/actores/boton.py142
-rw-r--r--pilas/actores/caja.py23
-rw-r--r--pilas/actores/cooperativista.py105
-rw-r--r--pilas/actores/cursordisparo.py24
-rw-r--r--pilas/actores/dialogo.py72
-rw-r--r--pilas/actores/disparo.py33
-rw-r--r--pilas/actores/ejes.py26
-rw-r--r--pilas/actores/entradadetexto.py53
-rw-r--r--pilas/actores/estrella.py19
-rw-r--r--pilas/actores/explosion.py39
-rw-r--r--pilas/actores/globo.py79
-rw-r--r--pilas/actores/globoelegir.py37
-rw-r--r--pilas/actores/mano.py46
-rw-r--r--pilas/actores/mapa.py130
-rw-r--r--pilas/actores/martian.py151
-rw-r--r--pilas/actores/menu.py115
-rw-r--r--pilas/actores/moneda.py22
-rw-r--r--pilas/actores/mono.py69
-rw-r--r--pilas/actores/nave.py79
-rw-r--r--pilas/actores/opcion.py36
-rw-r--r--pilas/actores/pausa.py17
-rw-r--r--pilas/actores/pelota.py23
-rw-r--r--pilas/actores/piedra.py36
-rw-r--r--pilas/actores/pingu.py104
-rw-r--r--pilas/actores/pizarra.py65
-rw-r--r--pilas/actores/puntaje.py30
-rw-r--r--pilas/actores/temporizador.py66
-rw-r--r--pilas/actores/texto.py60
-rw-r--r--pilas/actores/tortuga.py99
-rw-r--r--pilas/actores/utils.py70
-rw-r--r--pilas/aplicacion.py63
-rw-r--r--pilas/atajos.py17
-rw-r--r--pilas/camara.py39
-rw-r--r--pilas/colisiones.py60
-rw-r--r--pilas/colores.py48
-rw-r--r--pilas/comportamientos.py107
-rw-r--r--pilas/console/__init__.py0
-rw-r--r--pilas/console/console.py58
-rw-r--r--pilas/console/console_widget.py329
-rw-r--r--pilas/console/highlighter.py299
-rw-r--r--pilas/console/lang/python.json81
-rw-r--r--pilas/control.py112
-rw-r--r--pilas/data/aceituna.pngbin0 -> 2977 bytes
-rw-r--r--pilas/data/aceituna_burla.pngbin0 -> 3505 bytes
-rw-r--r--pilas/data/aceituna_grita.pngbin0 -> 2898 bytes
-rw-r--r--pilas/data/aceituna_risa.pngbin0 -> 3006 bytes
-rw-r--r--pilas/data/banana.pngbin0 -> 5752 bytes
-rw-r--r--pilas/data/batalhao.pngbin0 -> 51267 bytes
-rw-r--r--pilas/data/bomba.pngbin0 -> 6196 bytes
-rw-r--r--pilas/data/boton/boton_normal.pngbin0 -> 3864 bytes
-rw-r--r--pilas/data/boton/boton_over.pngbin0 -> 3770 bytes
-rw-r--r--pilas/data/boton/boton_press.pngbin0 -> 3874 bytes
-rw-r--r--pilas/data/boton/tema.pngbin0 -> 959 bytes
-rw-r--r--pilas/data/boton/tema.xcfbin0 -> 3811 bytes
-rw-r--r--pilas/data/caja.pngbin0 -> 5530 bytes
-rw-r--r--pilas/data/cooperativista/alerta.pngbin0 -> 35342 bytes
-rw-r--r--pilas/data/cooperativista/camina.pngbin0 -> 54104 bytes
-rw-r--r--pilas/data/cooperativista/camina_sujeta.pngbin0 -> 52667 bytes
-rw-r--r--pilas/data/cooperativista/ok.pngbin0 -> 22220 bytes
-rw-r--r--pilas/data/cooperativista/parado.pngbin0 -> 19326 bytes
-rw-r--r--pilas/data/cooperativista/parado_sujeta.pngbin0 -> 19979 bytes
-rw-r--r--pilas/data/cooperativista/trabajando.pngbin0 -> 38027 bytes
-rw-r--r--pilas/data/cursordisparo.pngbin0 -> 3688 bytes
-rw-r--r--pilas/data/cursores/arrastrando.pngbin0 -> 692 bytes
-rw-r--r--pilas/data/cursores/dibujar.pngbin0 -> 878 bytes
-rw-r--r--pilas/data/cursores/normal.pngbin0 -> 719 bytes
-rw-r--r--pilas/data/cursores/prohibido.pngbin0 -> 1166 bytes
-rw-r--r--pilas/data/disparo.pngbin0 -> 303 bytes
-rw-r--r--pilas/data/ejes.pngbin0 -> 2945 bytes
-rw-r--r--pilas/data/ejes.svg158
-rw-r--r--pilas/data/ejes.xcfbin0 -> 12108 bytes
-rw-r--r--pilas/data/estrella.pngbin0 -> 12478 bytes
-rw-r--r--pilas/data/explosion.pngbin0 -> 4885 bytes
-rw-r--r--pilas/data/explosion.wavbin0 -> 54868 bytes
-rw-r--r--pilas/data/fondos/blanco.pngbin0 -> 2009 bytes
-rw-r--r--pilas/data/fondos/espacio.jpgbin0 -> 23609 bytes
-rw-r--r--pilas/data/fondos/noche.jpgbin0 -> 13718 bytes
-rw-r--r--pilas/data/fondos/pasto.pngbin0 -> 7163 bytes
-rw-r--r--pilas/data/fondos/selva.jpgbin0 -> 33611 bytes
-rw-r--r--pilas/data/fondos/tarde.jpgbin0 -> 15885 bytes
-rw-r--r--pilas/data/fondos/volley.jpgbin0 -> 23468 bytes
-rw-r--r--pilas/data/globo.pngbin0 -> 3916 bytes
-rw-r--r--pilas/data/globo.xcfbin0 -> 10878 bytes
-rw-r--r--pilas/data/grillas/plataformas_10_10.pngbin0 -> 14270 bytes
-rw-r--r--pilas/data/icono_pausa.pngbin0 -> 625 bytes
-rw-r--r--pilas/data/iconos/borrar.pngbin0 -> 775 bytes
-rw-r--r--pilas/data/iconos/lupa.pngbin0 -> 662 bytes
-rw-r--r--pilas/data/iconos/ok.pngbin0 -> 872 bytes
-rw-r--r--pilas/data/interfaz/barra.pngbin0 -> 484 bytes
-rw-r--r--pilas/data/interfaz/caja.pngbin0 -> 1376 bytes
-rw-r--r--pilas/data/interfaz/deslizador.pngbin0 -> 1158 bytes
-rw-r--r--pilas/data/interfaz/selector.pngbin0 -> 923 bytes
-rw-r--r--pilas/data/interfaz/selector_seleccionado.pngbin0 -> 1553 bytes
-rw-r--r--pilas/data/invisible.pngbin0 -> 184 bytes
-rw-r--r--pilas/data/juegobase/data/logo.pngbin0 -> 14240 bytes
-rw-r--r--pilas/data/juegobase/ejecutar.py9
-rw-r--r--pilas/data/mapa.pngbin0 -> 4208 bytes
-rw-r--r--pilas/data/marcianitos/martian.pngbin0 -> 7193 bytes
-rw-r--r--pilas/data/marcianitos/martian.xcfbin0 -> 55126 bytes
-rw-r--r--pilas/data/marcianitos/martian_64_128.xcfbin0 -> 54442 bytes
-rw-r--r--pilas/data/marcianitos/martian_64_129.xcfbin0 -> 54442 bytes
-rw-r--r--pilas/data/moneda.pngbin0 -> 1451 bytes
-rw-r--r--pilas/data/monkey_normal.pngbin0 -> 30748 bytes
-rw-r--r--pilas/data/monkey_shout.pngbin0 -> 31837 bytes
-rw-r--r--pilas/data/monkey_smile.pngbin0 -> 31269 bytes
-rw-r--r--pilas/data/mono.pngbin0 -> 30748 bytes
-rw-r--r--pilas/data/nave.pngbin0 -> 1275 bytes
-rw-r--r--pilas/data/parque.tmx44
-rw-r--r--pilas/data/patito.pngbin0 -> 10020 bytes
-rw-r--r--pilas/data/patito_desde_arriba.pngbin0 -> 6081 bytes
-rw-r--r--pilas/data/pelota.pngbin0 -> 2940 bytes
-rw-r--r--pilas/data/piedra_chica.pngbin0 -> 374 bytes
-rw-r--r--pilas/data/piedra_grande.pngbin0 -> 1113 bytes
-rw-r--r--pilas/data/piedra_media.pngbin0 -> 643 bytes
-rw-r--r--pilas/data/pilas-logo.pngbin0 -> 13126 bytes
-rw-r--r--pilas/data/pingu.pngbin0 -> 13956 bytes
-rw-r--r--pilas/data/plataformas.pngbin0 -> 14270 bytes
-rw-r--r--pilas/data/prueba.pngbin0 -> 2730 bytes
-rw-r--r--pilas/data/punto.pngbin0 -> 191 bytes
-rw-r--r--pilas/data/shout.wavbin0 -> 22728 bytes
-rw-r--r--pilas/data/sin_imagen.pngbin0 -> 2765 bytes
-rw-r--r--pilas/data/smile.wavbin0 -> 2026 bytes
-rw-r--r--pilas/data/tick.wavbin0 -> 64612 bytes
-rw-r--r--pilas/data/tortuga.pngbin0 -> 968 bytes
-rw-r--r--pilas/data/window.ui34
-rw-r--r--pilas/depurador.py202
-rw-r--r--pilas/dispatch/__init__.py9
-rw-r--r--pilas/dispatch/dispatcher.py243
-rw-r--r--pilas/dispatch/license.txt36
-rw-r--r--pilas/dispatch/saferef.py250
-rw-r--r--pilas/ejemplos/__init__.py7
-rw-r--r--pilas/ejemplos/colisiones.py34
-rw-r--r--pilas/ejemplos/data/fondo_piezas.pngbin0 -> 73137 bytes
-rw-r--r--pilas/ejemplos/data/piezas.pngbin0 -> 177842 bytes
-rw-r--r--pilas/ejemplos/fisica.py26
-rw-r--r--pilas/ejemplos/listaseleccion.py12
-rw-r--r--pilas/ejemplos/piezas.py247
-rw-r--r--pilas/ejemplos/test.py40
-rw-r--r--pilas/escenas.py30
-rw-r--r--pilas/estudiante.py73
-rw-r--r--pilas/eventos.py42
-rw-r--r--pilas/fisica.py452
-rw-r--r--pilas/fondos.py64
-rw-r--r--pilas/fps.py57
-rw-r--r--pilas/grupo.py48
-rw-r--r--pilas/habilidades.py251
-rw-r--r--pilas/imagenes.py77
-rw-r--r--pilas/interfaz/__init__.py14
-rw-r--r--pilas/interfaz/boton.py65
-rw-r--r--pilas/interfaz/deslizador.py87
-rw-r--r--pilas/interfaz/ingreso_de_texto.py86
-rw-r--r--pilas/interfaz/lista_seleccion.py53
-rw-r--r--pilas/interfaz/selector.py64
-rw-r--r--pilas/interpolaciones.py86
-rw-r--r--pilas/lienzo.py6
-rw-r--r--pilas/motores/__init__.py9
-rw-r--r--pilas/motores/motor.py75
-rw-r--r--pilas/motores/motor_qt.py705
-rw-r--r--pilas/mundo.py74
-rw-r--r--pilas/pilasversion.py1
-rw-r--r--pilas/pytweener.py739
-rw-r--r--pilas/red.py41
-rw-r--r--pilas/simbolos.py19
-rw-r--r--pilas/sonidos.py36
-rw-r--r--pilas/tareas.py111
-rw-r--r--pilas/test.html1
-rw-r--r--pilas/test/test.py4
-rw-r--r--pilas/test/test_actores.py73
-rw-r--r--pilas/test/test_anterior.py117
-rw-r--r--pilas/test/test_interface.py30
-rw-r--r--pilas/test/test_posiciones.py246
-rw-r--r--pilas/test/test_ver_codigo.py7
-rw-r--r--pilas/utils.py246
-rw-r--r--pilas/ventana.py16
-rw-r--r--pilas/video/__init__.py18
-rw-r--r--pilas/video/video.py95
-rw-r--r--pilas/video/webcam.py37
-rw-r--r--pilas/window_base.py40
-rw-r--r--pilas/xmlreader.py33
-rw-r--r--pilas_plug.py44
-rw-r--r--qt/PyQt4/Qt.sobin0 -> 3732 bytes
-rw-r--r--qt/PyQt4/QtCore.sobin0 -> 2260532 bytes
-rw-r--r--qt/PyQt4/QtGui.sobin0 -> 7279604 bytes
-rw-r--r--qt/PyQt4/__init__.py0
-rw-r--r--qt/lib/libQtCore.so.4bin0 -> 2711012 bytes
-rw-r--r--qt/lib/libQtGui.so.4bin0 -> 11099576 bytes
-rw-r--r--qt/sip.sobin0 -> 91640 bytes
-rw-r--r--setup.py55
-rwxr-xr-xsetup_xo.py21
-rw-r--r--utils/actualizar_pypi.py15
-rw-r--r--utils/generar_resumen_api.py59
-rw-r--r--utils/resumen_api.pdfbin0 -> 58838 bytes
-rw-r--r--utils/resumen_api.rst1183
211 files changed, 11879 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..33d985e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.mo
+*.xo
+*.pyc
+*.pyo
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7bc4d39
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Hugo Ruscitti <hugoruscitti@gmail.com>
diff --git a/GFXLIB_LICENSE b/GFXLIB_LICENSE
new file mode 100644
index 0000000..3cb8184
--- /dev/null
+++ b/GFXLIB_LICENSE
@@ -0,0 +1,263 @@
+ __ _ _ _
+ / _| | (_) |
+ __ _| |___ _| |_| |__
+ / _` | _\ \/ / | | '_ \
+| (_| | | > <| | | |_) |
+ \__, |_| /_/\_\_|_|_.__/
+ __/ |
+ |___/
+----------------------------------------------------------------------
+Product : gfxlib-fuzed.zip
+Website : http://www.spicypixel.net
+Author : Marc Russell
+Released: 10th January 2008
+----------------------------------------------------------------------
+
+What is this?
+-------------
+gfxlib-fuzed is a package of free art assets to be used under the terms of this document. It is available to game developers and hobbyists alike.
+
+Contents
+--------
+The contents of the gfxlib-fuzed ZIP file are as follows.
+
+Directories
+ * Backgrounds - 4 Graphic tilesets, level data and level mockup.
+ * Sprites - Ingame Sprite animations
+ * GUI - Work In Progress menu artwork and icons
+
+Usage License & Restrictions
+----------------------------
+gfxlib is distributed under the "Common Public License Version 1.0."
+The terms of which are given below. If you do not understand the terms of the license please refer to a solicitor. It should however, be relatively clear how this package can be used.
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON
+PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
+THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a) in the case of the initial Contributor, the initial code and
+ documentation distributed under this Agreement, and
+
+ b) in the case of each subsequent Contributor:
+
+ i) changes to the Program, and
+
+ ii) additions to the Program;
+
+ where such changes and/or additions to the Program originate from
+ and are distributed by that particular Contributor. A Contribution
+ 'originates' from a Contributor if it was added to the Program by
+ such Contributor itself or anyone acting on such Contributor's
+ behalf. Contributions do not include additions to the Program which:
+ (i) are separate modules of software distributed in conjunction with
+ the Program under their own license agreement, and (ii) are not
+ derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+ b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in source code and object code form. This patent license
+ shall apply to the combination of the Contribution and the Program
+ if, at the time the Contribution is added by the Contributor, such
+ addition of the Contribution causes such combination to be covered
+ by the Licensed Patents. The patent license shall not apply to any
+ other combinations which include the Contribution. No hardware per
+ se is licensed hereunder.
+
+ c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity.
+ Each Contributor disclaims any liability to Recipient for claims
+ brought by any other entity based on infringement of intellectual
+ property rights or otherwise. As a condition to exercising the
+ rights and licenses granted hereunder, each Recipient hereby assumes
+ sole responsibility to secure any other intellectual property rights
+ needed, if any. For example, if a third party patent license is
+ required to allow Recipient to distribute the Program, it is
+ Recipient's responsibility to acquire that license before
+ distributing the Program.
+
+ d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form
+under its own license agreement, provided that:
+
+ a) it complies with the terms and conditions of this Agreement; and
+
+ b) its license agreement:
+
+ i) effectively disclaims on behalf of all Contributors all
+ warranties and conditions, express and implied, including warranties
+ or conditions of title and non-infringement, and implied warranties
+ or conditions of merchantability and fitness for a particular
+ purpose;
+
+ ii) effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits;
+
+ iii) states that any provisions which differ from this Agreement are
+ offered by that Contributor alone and not by any other party; and
+
+ iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable
+ manner on or through a medium customarily used for software
+ exchange.
+
+When the Program is made available in source code form:
+
+ a) it must be made available under this Agreement; and
+
+ b) a copy of this Agreement must be included with each copy of the
+ Program.
+
+Contributors may not remove or alter any copyright notices contained
+within the Program.
+
+Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program, the
+Contributor who includes the Program in a commercial product offering
+should do so in a manner which does not create potential liability for
+other Contributors. Therefore, if a Contributor includes the Program in
+a commercial product offering, such Contributor ("Commercial
+Contributor") hereby agrees to defend and indemnify every other
+Contributor ("Indemnified Contributor") against any losses, damages and
+costs (collectively "Losses") arising from claims, lawsuits and other
+legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the
+Program in a commercial product offering. The obligations in this
+section do not apply to any claims or Losses relating to any actual or
+alleged intellectual property infringement. In order to qualify, an
+Indemnified Contributor must: a) promptly notify the Commercial
+Contributor in writing of such claim, and b) allow the Commercial
+Contributor to control, and cooperate with the Commercial Contributor
+in, the defense and any related settlement negotiations. The Indemnified
+Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
+ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
+OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
+A PARTICULAR PURPOSE. Each Recipient is solely responsible for
+determining the appropriateness of using and distributing the Program
+and assumes all risks associated with its exercise of rights under this
+Agreement, including but not limited to the risks and costs of program
+errors, compliance with applicable laws, damage to or loss of data,
+programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as of
+the date such litigation is filed. In addition, if Recipient institutes
+patent litigation against any entity (including a cross-claim or
+counterclaim in a lawsuit) alleging that the Program itself (excluding
+combinations of the Program with other software or hardware) infringes
+such Recipient's patent(s), then such Recipient's rights granted under
+Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails
+to comply with any of the material terms or conditions of this Agreement
+and does not cure such failure in a reasonable period of time after
+becoming aware of such noncompliance. If all Recipient's rights under
+this Agreement terminate, Recipient agrees to cease use and distribution
+of the Program as soon as reasonably practicable. However, Recipient's
+obligations under this Agreement and any licenses granted by Recipient
+relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and may
+only be modified in the following manner. The Agreement Steward reserves
+the right to publish new versions (including revisions) of this
+Agreement from time to time. No one other than the Agreement Steward has
+the right to modify this Agreement. IBM is the initial Agreement
+Steward. IBM may assign the responsibility to serve as the Agreement
+Steward to a suitable separate entity. Each new version of the Agreement
+will be given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version
+of the Agreement is published, Contributor may elect to distribute the
+Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+rights or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under this
+Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to
+this Agreement will bring a legal action under this Agreement more than
+one year after the cause of action arose. Each party waives its rights
+to a jury trial in any resulting litigation.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..acebb6e
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,63 @@
+Instalación
+===========
+
+Existen 3 formas de instalar la biblioteca, así
+que veremos cada una por separado.
+
+Instalación fácil, para cada sistema operativo
+----------------------------------------------
+
+La forma mas sencilla de instalar pilas es
+siguiendo alguno de los tutoriales del sitio
+web de pilas:
+
+http://www.pilas-engine.com.ar/documentacion
+
+
+Instalación para distribuir con un juego
+----------------------------------------
+
+Si quieres que la biblioteca esté en un juego pero
+no quieres instalarla en cada equipo, puedes
+simplemente copiar todo el contenido del directorio
+``pilas`` dentro del directorio del juego.
+
+Entonces, cuando ejecutes una sentencia
+como ``import pilas``, la biblioteca se
+leerá desde el directorio.
+
+
+Instalación luego de la descarga
+--------------------------------
+
+Si realizas videojuegos, pero no quieres tener la biblioteca
+como directorio físico en todos y cada uno de los juegos
+que realices, una buena idea es instalar la biblioteca en
+tu sistema.
+
+Primero tienes que instalar ``setuptools`` usando el
+siguiente comando::
+
+ sudo apt-get install python-setuptools
+
+Luego, ingresa en el directorio en donde se encuentra
+tu copia de pilas y ejecuta un comando cómo::
+
+ sudo python setup.py install
+
+o bien::
+
+ sudo python setup.py develop
+
+En el primer caso la biblioteca se instala y pasa
+a estar completamente instalada en tu sistema. No importa
+que borres el directorio que has descargado, la biblioteca
+quedará instalada en un directorio del sistema para bibliotecas
+de python.
+
+El segundo caso es el favorito por los desarrolladores, porque
+te permite actualizar mas fácilmente la biblioteca. Ya que
+no está complemente instalada en el equipo, si no que hace
+referencia al directorio en donde se ha descargado pilas. Si actualizas
+el directorio origen, los cambios se reflejarán en
+tu sistema sin necesidad de repetir el comando de instalación.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..29d2c08
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,177 @@
+============
+Pilas Engine
+============
+
+Pilas es un motor para realizar videojuegos de manera
+rápida y sencilla.
+
+Es una herramienta orientada a programadores casuales
+o principiantes, que quiera comenzar a realizar sus
+primeros videojuegos.
+
+
+¿Cómo empezar?
+==============
+
+Una buena forma de comenzar con pilas es instalar todo
+el kit de desarrollo siguiendo las intrucciones de
+nuestra web: http://www.pilas-engine.com.ar
+
+
+Y una vez instalada la biblioteca, se puede invocar
+al comando ``pilas -e`` para ver una lista completa
+de ejemplos y minijuegos.
+
+
+¿Cómo generar la documentación del proyecto?
+============================================
+
+Para generar la documentación del proyecto
+usamos ``Sphinx``, el sistema de documentación
+mas utilizado en los proyectos de python.
+
+Para generar los archivos PDF o HTML usamos el comando
+``make`` dentro del directorio ``doc``. El archivo que
+dispara todas las acciones que sphinx sabe hacer están
+definidas en el archivo ``Makefile``.
+
+Recuerda instalar ``sphinx`` antes de continuar, si solo
+quieres generara la documentación HTML alcanza con
+ejecutar este comando::
+
+ apt-get install python-sphinx
+
+y en caso de que quieras generar la documentación PDF::
+
+ apt-get install texlive-lang-spanish
+
+
+Otros recursos en la web
+========================
+
+Existen varios sitios web importantes dentro
+de proyecto pilas, la web principal
+es:
+
+ - http://www.pilas-engine.com.ar
+
+y los sitios para desarrolladores, donde se encuentran
+las tareas y el repositorio de código son:
+
+ - http://www.dev-losersjuegos.com.ar
+ - http://bitbucket.org/hugoruscitti/pilas
+
+
+También tenemos una lista de correo en
+la siguiente dirección:
+
+ - http://groups.google.com/group/pilas-engine
+
+Dependencias
+============
+
+- pygame
+- pyqt4
+- pybox2d
+- python
+
+
+Licencia
+========
+
+Pilas es software libre, y se distribuye bajo la
+licencia GPLv3.
+
+
+Test de unidad
+==============
+
+Para realizar pruebas de unidad estamos usando ``py.test``. Para
+ello tienes que instalar una nueva dependencia con el
+comando::
+
+ sudo easy_install pytest
+
+y luego correr todas las pruebas con los siguientes comandos::
+
+ make test
+
+Créditos y agradecimientos
+==========================
+
+Pilas está principalmente desarrollada por Hugo Ruscitti, y
+cuenta con varios dibujos realizados por Walter Velazquez. Ambos
+fundadores de Losersjuegos (http://www.losersjuegos.com.ar).
+
+A continuación se incluye una lista de otros desarrolladores
+y proyectos que sumaron a pilas:
+
+PyTweener
+---------
+
+Las interpolaciones dentro del motor se realizan
+mediante la biblioteca pyTweener, distribuida
+bajo la licencia M.I.T. por Ben Harling.
+
+Dispatch
+--------
+
+El modulo ``pilas.dispatch`` se adoptó del
+core de Django, y estába basado en el proyecto pydispatch.
+
+
+PyQt4
+-----
+
+Todo el manejo multimedia se realiza gracias a la biblioteca
+Qt4:
+
+- http://qt.nokia.com/
+
+
+pygame
+------
+
+Como biblioteca multimedia secundaria usamos
+pygame, que además nos permite seleccionarla
+en casos donde no hay soporte para OpenGL (como
+en los equipos OLPC por ejemplo).
+
+Box2d
+-----
+
+Para el manejo multimedia estamos usando
+la biblioteca box2d:
+
+- http://code.google.com/p/pybox2d/
+
+GFXLib
+------
+
+He utilizado los siguientes gráficos del proyecto
+gfxlib:
+
+- pilas/data/moneda.png
+
+Estos gráficos se distribuyen bajo la licencia "Common Public License", y
+puedes obtener mas información en: http://www.spicypixel.net
+
+¡ Gracias Marc !
+
+
+DANC, de lostgarden.com
+-----------------------
+
+En pilas utilizamos algunos gráficos que DANC publicó
+en su sitio web:
+
+- www.lostgarden.com
+
+
+a "arboris"
+-----------
+
+Por sus gráficos de naves que se pueden
+descargar desde la siguiente web:
+
+- http://arboris.deviantart.com/art/Spaceship-sprites-43030167
diff --git a/activity.py b/activity.py
new file mode 100644
index 0000000..35933cc
--- /dev/null
+++ b/activity.py
@@ -0,0 +1,98 @@
+# Copyright 2009 Simon Schampijer
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Pilas Activity. Live interpreter to learn programming with games."""
+
+import gtk
+import logging
+
+from gettext import gettext as _
+
+from subprocess import Popen
+
+from sugar.activity import activity
+from sugar.graphics.toolbarbox import ToolbarBox
+from sugar.activity.widgets import ActivityButton
+from sugar.activity.widgets import ActivityToolbox
+from sugar.activity.widgets import TitleEntry
+from sugar.activity.widgets import StopButton
+from sugar.activity.widgets import ShareButton
+
+import os
+import sys
+import copy
+
+base = os.environ['SUGAR_BUNDLE_PATH']
+os.chdir(base)
+
+qtlib = os.path.join(base, 'qt/lib/')
+new_env = copy.copy(os.environ)
+new_env['LD_LIBRARY_PATH'] = qtlib
+
+class PilasActivity(activity.Activity):
+ """Pilas class as specified in activity.info"""
+
+ def __init__(self, handle):
+ """Set up the Pilas activity."""
+ activity.Activity.__init__(self, handle)
+
+ # we do not have collaboration features,
+ # make the share option insensitive
+ self.max_participants = 1
+
+ # toolbar with the new toolbar redesign
+ toolbar_box = ToolbarBox()
+
+ activity_button = ActivityButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ title_entry = TitleEntry(self)
+ toolbar_box.toolbar.insert(title_entry, -1)
+ title_entry.show()
+
+ share_button = ShareButton(self)
+ toolbar_box.toolbar.insert(share_button, -1)
+ share_button.show()
+
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.show()
+
+ stop_button = StopButton(self)
+ toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ self.set_toolbar_box(toolbar_box)
+ toolbar_box.show()
+
+ socket = gtk.Socket()
+ socket.connect("plug-added", self._on_plugged_event)
+ socket.set_flags(gtk.CAN_FOCUS)
+ self.set_canvas(socket)
+ self.set_focus(socket)
+ socket.show()
+
+ screen_width = gtk.gdk.screen_width()
+ screen_height = gtk.gdk.screen_height()
+
+ Popen(["python", "pilas_plug.py", str(socket.get_id()),
+ str(screen_width), str(screen_height)], env=new_env)
+
+ def _on_plugged_event(self, widget):
+ logging.info("Plug inserted")
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..a5fe296
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,7 @@
+[Activity]
+name = Pilas
+activity_version = 1
+bundle_id = org.sugarlabs.Pilas
+exec = sugar-activity activity.PilasActivity
+icon = pilas-icon
+license = LGPLv3
diff --git a/activity/pilas-icon.svg b/activity/pilas-icon.svg
new file mode 100644
index 0000000..b450739
--- /dev/null
+++ b/activity/pilas-icon.svg
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#FFFFFF">
+ <!ENTITY stroke_color "#010101">
+]>
+<svg
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="55"
+ viewBox="0 0 55 55"
+ id="svg2"
+ xml:space="preserve"><g
+ id="layer1"
+ style="fill:&fill_color;"><path
+ d="m 36.912473,6.3835716 c 2.857769,8.3410014 6.875787,31.4726254 5.581886,44.2949674 l -29.710039,0 C 20.166815,34.473063 18.906389,19.888135 18.186145,14.846431 24.725277,13.722202 31.33032,11.814205 36.912473,6.3835716 z"
+ id="path3788"
+ style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+ d="m 23.768031,13.225884 -2.16073,-5.2217647 9.363164,-3.7812778 2.16073,4.8616429"
+ id="path3790"
+ style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></g><path
+ d="m 33.775168,31.191275 a 12.550336,12.550336 0 1 1 -25.1006713,0 12.550336,12.550336 0 1 1 25.1006713,0 z"
+ transform="matrix(0.38328879,0,0,0.38328879,13.089592,19.236009)"
+ id="path2997"
+ style="fill:&stroke_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:3.91349816;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /><path
+ d="m 33.775168,31.191275 a 12.550336,12.550336 0 1 1 -25.1006713,0 12.550336,12.550336 0 1 1 25.1006713,0 z"
+ transform="matrix(0.50432689,0,0,0.50432689,21.613927,13.799602)"
+ id="path3767"
+ style="fill:&stroke_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:2.97426128;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /><path
+ d="m 33.959732,39.127517 c 3.583343,5.667946 7.1788,3.809105 9.411681,2.739568 3.741459,-1.792136 2.150081,-8.800841 -1.290876,-9.752991"
+ id="path3787"
+ style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+ d="M 13.104027,36.174497 C 23.624175,42.080534 37.18096,43.42958 44.66443,27.315436"
+ id="path3781"
+ style="fill:none;stroke:&stroke_color;;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+ d="m 33.775168,31.191275 a 12.550336,12.550336 0 1 1 -25.1006713,0 12.550336,12.550336 0 1 1 25.1006713,0 z"
+ transform="matrix(0.12827016,0,0,0.12827016,18.50232,27.190365)"
+ id="path3783"
+ style="fill:&fill_color;;fill-opacity:1;stroke:none" /><path
+ d="m 33.775168,31.191275 a 12.550336,12.550336 0 1 1 -25.1006713,0 12.550336,12.550336 0 1 1 25.1006713,0 z"
+ transform="matrix(0.12827016,0,0,0.12827016,29.391582,25.529291)"
+ id="path3785"
+ style="fill:&fill_color;;fill-opacity:1;stroke:none" /><path
+ d="m 38.573826,36.174497 c 2.583892,-0.369128 3.875838,1.47651 3.875838,1.47651"
+ id="path3789"
+ style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+ d="m 15.891195,24.121374 6.251039,-2.184098"
+ id="path3806"
+ style="fill:none;stroke:&stroke_color;;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path
+ d="m 32.234273,17.343139 7.36247,2.40247"
+ id="path3808"
+ style="fill:none;stroke:&stroke_color;;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /></svg> \ No newline at end of file
diff --git a/bin/pilas b/bin/pilas
new file mode 100755
index 0000000..6dbf703
--- /dev/null
+++ b/bin/pilas
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+import sys
+from optparse import OptionParser
+import pilas
+
+analizador = OptionParser()
+
+analizador.add_option("-j", "--crearjuego", dest="crearjuego",
+ action="store_true", default=False,
+ help="Genera un directorio de videojuego vacio.")
+
+analizador.add_option("-e", "--ejemplos", dest="ejemplos",
+ action="store_true", default=False,
+ help="Mustra varios ejemplos sencillos de pilas")
+
+analizador.add_option("-t", "--test", dest="test",
+ action="store_true", default=False,
+ help="Invoca varias pruebas verificar el funcionamiento de pilas")
+
+(opciones, argumentos) = analizador.parse_args()
+
+
+if opciones.crearjuego:
+ pilas.utils.crear_juego()
+ sys.exit(0)
+elif opciones.ejemplos:
+ pilas.abrir_cargador()
+ sys.exit(0)
+elif opciones.test:
+ pilas.utils.realizar_pruebas()
+ sys.exit(0)
+
+print "Error, no has indicado un parametro para iniciar pilas."
+print "Puedes ejecutar el comando 'pilas --help' para ver instrucciones."
+sys.exit(1)
+
diff --git a/pilas/__init__.py b/pilas/__init__.py
new file mode 100644
index 0000000..87c2700
--- /dev/null
+++ b/pilas/__init__.py
@@ -0,0 +1,172 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+mundo = None
+bg = None
+
+import sys
+import utils
+from mundo import Mundo
+import actores
+import fondos
+import habilidades
+import eventos
+import sonidos
+import colores
+import atajos
+import escenas
+import ejemplos
+import interfaz
+
+__doc__ = """
+Módulo pilas
+============
+
+Pilas es una biblioteca para facilitar el desarrollo
+de videojuegos. Es útil para programadores
+principiantes o para el desarrollo de juegos casuales.
+
+Este módulo contiene las funciones principales
+para iniciar y ejecutar la biblioteca.
+"""
+
+if utils.esta_en_sesion_interactiva():
+ utils.cargar_autocompletado()
+
+def iniciar(ancho=640, alto=480, titulo='Pilas', usar_motor='qtgl',
+ rendimiento=60, modo='detectar', economico=True,
+ gravedad=(0, -90), pantalla_completa=False):
+ """
+ Inicia la ventana principal del juego con algunos detalles de funcionamiento.
+
+ Ejemplo de invocación:
+
+ >>> pilas.iniciar(ancho=320, alto=240)
+
+ .. image:: images/iniciar_320_240.png
+
+ Parámetros:
+
+ :ancho: el tamaño en pixels para la ventana.
+ :alto: el tamaño en pixels para la ventana.
+ :titulo: el titulo a mostrar en la ventana.
+ :usar_motor: el motor multimedia a utilizar, puede ser 'qt' o 'qtgl'.
+ :rendimiento: cantidad de cuadros por segundo a mostrar.
+ :modo: si se utiliza modo interactivo o no.
+ :economico: si tiene que evitar consumir muchos recursos de procesador
+ :gravedad: el vector de aceleracion para la simulacion de fisica.
+ :pantalla_completa: si debe usar pantalla completa o no.
+
+ """
+
+ global mundo
+
+ motor = __crear_motor(usar_motor)
+ mundo = Mundo(motor, ancho, alto, titulo, rendimiento, economico, gravedad, pantalla_completa)
+ escenas.Normal(colores.grisclaro)
+
+
+def ejecutar(ignorar_errores=False):
+ """Pone en funcionamiento las actualizaciones y dibujado.
+
+ Esta función es necesaria cuando se crea un juego
+ en modo ``no-interactivo``."""
+ mundo.ejecutar_bucle_principal(ignorar_errores)
+
+def terminar():
+ """Finaliza la ejecución de pilas y cierra la ventana principal."""
+ mundo.terminar()
+
+def ver(objeto, imprimir=True, retornar=False):
+ """Imprime en pantalla el codigo fuente asociado a un objeto o elemento de pilas."""
+ import inspect
+
+ try:
+ codigo = inspect.getsource(objeto.__class__)
+ except TypeError:
+ codigo = inspect.getsource(objeto)
+
+ if imprimir:
+ print codigo
+
+ if retornar:
+ return codigo
+
+def version():
+ """Retorna el número de version de pilas."""
+ import pilasversion
+
+ return pilasversion.VERSION
+
+def __crear_motor(usar_motor):
+ """Genera instancia del motor multimedia en base a un nombre.
+
+ Esta es una función interna y no debe ser ejecutada
+ excepto por el mismo motor pilas."""
+
+ if usar_motor == 'qt':
+ from motores import motor_qt
+ motor = motor_qt.Qt()
+ elif usar_motor == 'qtgl':
+ from motores import motor_qt
+ motor = motor_qt.QtGL()
+ else:
+ print "El motor multimedia seleccionado (%s) no esta disponible" %(usar_motor)
+ print "Las opciones de motores que puedes probar son 'qt' y 'qtgl'."
+ sys.exit(1)
+
+ return motor
+
+def reiniciar():
+ """Elimina todos los actores y vuelve al estado inicial."""
+ actores.utils.eliminar_a_todos()
+ mundo.reiniciar()
+
+anterior_texto = None
+
+def avisar(mensaje):
+ """Emite un mensaje en la ventana principal.
+
+ Este mensaje aparecerá en la parte inferior de la pantalla, por
+ ejemplo:
+
+ >>> pilas.avisar("Use la tecla <esc> para terminar el programa")
+ """
+ global anterior_texto
+ izquierda, derecha, arriba, abajo = utils.obtener_bordes()
+
+ if anterior_texto:
+ anterior_texto.eliminar()
+
+ texto = actores.Texto(mensaje)
+ texto.magnitud = 17
+ texto.centro = ("centro", "centro")
+ texto.izquierda = izquierda + 10
+ texto.color = colores.blanco
+ texto.abajo = abajo + 10
+ anterior_texto = texto
+
+def abrir_cargador():
+ """Abre un cargador de ejemplos con varios códigos de prueba.
+
+ Ejemplo:
+
+ >>> pilas.abrir_cargador()
+
+ El cargador de ejemplos se ve de esta forma:
+
+ .. image:: images/cargador.png
+ """
+ try:
+ import cargador
+
+ cargador.ejecutar()
+ except ImportError:
+ print "Lo siento, no tienes instalada la extesion de ejemplos."
+ print "Instale el paquete 'pilas-examples' para continuar."
+ return []
diff --git a/pilas/actores/__init__.py b/pilas/actores/__init__.py
new file mode 100644
index 0000000..da29e16
--- /dev/null
+++ b/pilas/actores/__init__.py
@@ -0,0 +1,71 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+
+import math
+
+import pilas
+import utils
+from actor import Actor
+
+todos = []
+
+__doc__ = """
+Módulo pilas.actores
+====================
+
+El módulo actores contiene una serie de clases
+para representar personajes de videojuegos.
+
+Para crear actores en una escena del juego simplemente
+se tiene que crear un nuevo objeto a partir de una
+clase.
+
+Por ejemplo, para crear un pongüino podríamos
+escribir la siguiente sentencia:
+
+ >>> p = pilas.actores.Pingu()
+
+"""
+
+
+
+from mono import Mono
+from ejes import Ejes
+from animado import Animado
+from animacion import Animacion
+from explosion import Explosion
+from bomba import Bomba
+from pingu import Pingu
+from banana import Banana
+from texto import Texto
+from temporizador import Temporizador
+from moneda import Moneda
+from pizarra import Pizarra
+from pelota import Pelota
+from puntaje import Puntaje
+from estrella import Estrella
+from caja import Caja
+from nave import Nave
+from disparo import Disparo
+from cursordisparo import CursorDisparo
+from piedra import Piedra
+from menu import Menu
+from opcion import Opcion
+from tortuga import Tortuga
+from mapa import Mapa
+from martian import Martian
+from boton import Boton
+from entradadetexto import EntradaDeTexto
+from aceituna import Aceituna
+from globo import Globo
+from dialogo import Dialogo
+from globoelegir import GloboElegir
+from pausa import Pausa
+from mano import CursorMano
+from cooperativista import Cooperativista
diff --git a/pilas/actores/aceituna.py b/pilas/actores/aceituna.py
new file mode 100644
index 0000000..1e49384
--- /dev/null
+++ b/pilas/actores/aceituna.py
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+class Aceituna(Actor):
+
+ def __init__(self, x=0, y=0):
+ self.cuadro_normal = pilas.imagenes.cargar("aceituna.png")
+ self.cuadro_reir = pilas.imagenes.cargar("aceituna_risa.png")
+ self.cuadro_burla = pilas.imagenes.cargar("aceituna_burla.png")
+ self.cuadro_grita = pilas.imagenes.cargar("aceituna_grita.png")
+
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = self.cuadro_normal
+ self.centro = ('centro', 'centro')
+ self.radio_de_colision = 18
+
+ def normal(self):
+ self.imagen = self.cuadro_normal
+
+ def reir(self):
+ self.imagen = self.cuadro_reir
+
+ def burlarse(self):
+ self.imagen = self.cuadro_burla
+
+ burlar = burlarse
+
+ def gritar(self):
+ self.imagen = self.cuadro_grita
+
+ def saltar(self):
+ self.hacer(pilas.comportamientos.Saltar())
diff --git a/pilas/actores/actor.py b/pilas/actores/actor.py
new file mode 100644
index 0000000..9b1cbc2
--- /dev/null
+++ b/pilas/actores/actor.py
@@ -0,0 +1,409 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.estudiante import Estudiante
+
+IZQUIERDA = ["izquierda"]
+DERECHA = ["derecha"]
+ARRIBA = ["arriba", "superior"]
+CENTRO = ["centro", "centrado", "medio", "arriba"]
+ABAJO = ["abajo", "inferior", "debajo"]
+
+
+class Actor(object, Estudiante):
+ """Representa un objeto visible en pantalla, algo que se ve y tiene posicion.
+
+ .. image:: images/actores/actor.png
+
+ Un objeto Actor se tiene que crear siempre indicando una imagen. Si no
+ se especifica una imagen, se verá una pila de color gris cómo la que
+ está mas arriba.
+
+ Una forma de crear el actor con una imagen es:
+
+ >>> protagonista = Actor("protagonista_de_frente.png")
+
+ incluso, es equivalente hacer lo siguiente:
+
+ >>> imagen = pilas.imagenes.cargar("protagonista_de_frente.png")
+ >>> protagonista = Actor(imagen)
+
+ Luego, una vez que ha sido ejecutada la sentencia aparecerá
+ el nuevo actor para que puedas manipularlo. Por ejemplo
+ alterando sus propiedades:
+
+ >>> protagonista.x = 100
+ >>> protagonista.escala = 2
+ >>> protagonista.rotacion = 30
+
+ Estas propiedades también se pueden manipular mediante
+ interpolaciones. Por ejemplo, para aumentar el tamaño del
+ personaje de 1 a 5 en 7 segundos:
+
+ >>> protagonista.escala = 1
+ >>> protagonista.escala = [5], 7
+
+ Si quieres que el actor sea invisible, un truco es crearlo
+ con la imagen ``invisible.png``:
+
+ >>> invisible = pilas.actores.Actor('invisible.png')
+ """
+
+ def __init__(self, imagen="sin_imagen.png", x=0, y=0):
+ if not pilas.mundo:
+ mensaje = "Tiene que invocar a la funcion ``pilas.iniciar()`` para comenzar."
+ print mensaje
+ raise Exception(mensaje)
+
+ Estudiante.__init__(self)
+ self._actor = pilas.mundo.motor.obtener_actor(imagen, x=x, y=y)
+ self.centro = ('centro', 'centro')
+
+ self.x = x
+ self.y = y
+ self.transparencia = 0
+
+ # Define el nivel de lejanía respecto del observador.
+ self.z = 0
+ self._espejado = False
+ self.radio_de_colision = 10
+ pilas.actores.utils.insertar_como_nuevo_actor(self)
+ self._transparencia = 0
+ self.anexados = []
+
+ def definir_centro(self, (x, y)):
+ if type(x) == str:
+ if x not in IZQUIERDA + CENTRO + DERECHA:
+ raise Exception("No puedes definir '%s' como eje horizontal." %(x))
+ x = self._interpretar_y_convertir_posicion(x, self.obtener_ancho())
+ if type(y) == str:
+ if y not in ARRIBA + CENTRO + ABAJO:
+ raise Exception("No puedes definir '%s' como eje vertical." %(y))
+ y = self._interpretar_y_convertir_posicion(y, self.obtener_alto())
+
+ self._centro = (x, y)
+ self._actor.definir_centro(x, y)
+
+ def _interpretar_y_convertir_posicion(self, posicion, maximo_valor):
+ if posicion in IZQUIERDA + ARRIBA:
+ return 0
+ elif posicion in CENTRO:
+ return int(maximo_valor / 2.0)
+ elif posicion in DERECHA + ABAJO:
+ return maximo_valor
+ else:
+ raise Exception("El valor '%s' no corresponde a una posicion, use numeros o valores como 'izquierda', 'arriba' etc." %(posicion))
+
+ def obtener_centro(self):
+ return self._centro
+
+ centro = property(obtener_centro, definir_centro, doc="""
+ Cambia la posición del punto (x, y) dentro de actor.
+
+ Inicialmente, cuando tomamos un actor y definimos sus
+ atributos (x, y). Ese punto, será el que representa el centro
+ del personaje.
+
+ Eso hace que las rotaciones sean siempre sobre el centro
+ del personajes, igual que los cambios de escala y la posición.
+
+ En algunas ocasiones, queremos que el punto (x, y) sea otra
+ parte del actor. Por ejemplo sus pies. En esos casos
+ es útil definir el centro del actor.
+
+ Por ejemplo, si queremos mover el centro del actor podemos
+ usar sentencias cómo estas:
+
+ >>> actor.centro = ("izquierda", "abajo")
+ >>> actor.centro = ("centro", "arriba")
+
+ Pulsa la tecla **F8** para ver el centro del los actores
+ dentro de pilas. Es aconsejable pulsar la tecla **+** para
+ que el punto del modo **F8** se vea bien.
+ """)
+
+ def definir_posicion(self, x, y):
+ self._actor.definir_posicion(x, y)
+
+ def obtener_posicion(self):
+ return self._actor.obtener_posicion()
+
+ def dibujar(self, aplicacion):
+ self._actor.dibujar(aplicacion)
+
+ def get_x(self):
+ x, y = self.obtener_posicion()
+ return x
+
+ @pilas.utils.interpolable
+ def set_x(self, x):
+ self.definir_posicion(x, self.y)
+
+ def get_z(self):
+ return self._z
+
+ @pilas.utils.interpolable
+ def set_z(self, z):
+ self._z = z
+ pilas.actores.utils.ordenar_actores_por_valor_z()
+
+ @pilas.utils.interpolable
+ def set_y(self, y):
+ self.definir_posicion(self.x, y)
+
+ def get_y(self):
+ x, y = self.obtener_posicion()
+ return y
+
+ @pilas.utils.interpolable
+ def set_scale(self, s):
+ if s < 0:
+ return
+
+ ultima_escala = self.obtener_escala()
+
+ # Se hace la siguiente regla de 3 simple:
+ #
+ # ultima_escala self.radio_de_colision
+ # s ?
+
+ self.definir_escala(s)
+ self.radio_de_colision = (s * self.radio_de_colision) / max(ultima_escala, 0.0001)
+
+ @pilas.utils.interpolable
+ def set_scale_x(self, s):
+ if s < 0:
+ return
+ self._actor.definir_escala_x(s)
+
+ @pilas.utils.interpolable
+ def set_scale_y(self, s):
+ if s < 0:
+ return
+ self._actor.definir_escala_y(s)
+
+
+
+ def get_scale(self):
+ return self.obtener_escala()
+
+ def get_scale_x(self):
+ return self._actor._escala_x
+
+ def get_scale_y(self):
+ return self._actor._escala_y
+
+ def get_rotation(self):
+ return self.obtener_rotacion()
+
+ @pilas.utils.interpolable
+ def set_rotation(self, x):
+ self.definir_rotacion(x)
+
+ def get_espejado(self):
+ return self._espejado
+
+ def set_espejado(self, nuevo_valor):
+ if self._espejado != nuevo_valor:
+ self._espejado = nuevo_valor
+ self._actor.set_espejado(nuevo_valor)
+
+ @pilas.utils.interpolable
+ def set_transparencia(self, nuevo_valor):
+ self._transparencia = nuevo_valor
+ self.definir_transparencia(nuevo_valor)
+
+ def get_transparencia(self):
+ return self._transparencia
+
+ def get_imagen(self):
+ return self.obtener_imagen()
+
+ def set_imagen(self, imagen):
+ if isinstance(imagen, str):
+ imagen = pilas.imagenes.cargar(imagen)
+
+ self.definir_imagen(imagen)
+
+ def get_fijo(self):
+ return self._actor.fijo
+
+ def set_fijo(self, fijo):
+ self._actor.fijo = fijo
+
+
+ espejado = property(get_espejado, set_espejado, doc="Indica si se tiene que invertir horizonaltamente la imagen del actor.")
+ z = property(get_z, set_z, doc="Define lejania respecto del observador.")
+ x = property(get_x, set_x, doc="Define la posición horizontal.")
+ y = property(get_y, set_y, doc="Define la posición vertical.")
+ rotacion = property(get_rotation, set_rotation, doc="Angulo de rotación (en grados, de 0 a 360)")
+ escala = property(get_scale, set_scale, doc="Escala de tamaño, 1 es normal, 2 al doble de tamaño etc...)")
+ escala_x = property(get_scale_x, set_scale_x, doc="Escala de tamaño horizontal, 1 es normal, 2 al doble de tamaño etc...)")
+ escala_y = property(get_scale_y, set_scale_y, doc="Escala de tamaño vertical, 1 es normal, 2 al doble de tamaño etc...)")
+ transparencia = property(get_transparencia, set_transparencia, doc="Define el nivel de transparencia, 0 indica opaco y 100 la maxima transparencia.")
+ imagen = property(get_imagen, set_imagen, doc="Define la imagen a mostrar.")
+ fijo = property(get_fijo, set_fijo, doc="Indica si el actor debe ser independiente a la camara.")
+
+ def eliminar(self):
+ """Elimina el actor de la lista de actores que se imprimen en pantalla."""
+ self.destruir()
+ self._eliminar_anexados()
+
+ def destruir(self):
+ """Elimina a un actor pero de manera inmediata."""
+ pilas.actores.utils.eliminar_un_actor(self)
+ self.eliminar_habilidades()
+ self.eliminar_comportamientos()
+
+ def actualizar(self):
+ """Actualiza el estado del actor.
+
+ Este metodo se llama una vez por frame, y generalmente se suele
+ usar para implementar el comportamiento del actor.
+
+ Si estás haciendo una subclase de Actor, es aconsejable que re-definas
+ este método."""
+
+ def pre_actualizar(self):
+ """Actualiza comportamiento y habilidades antes de la actualización."""
+ self.actualizar_comportamientos()
+ self.actualizar_habilidades()
+
+ def __cmp__(self, otro_actor):
+ """Compara dos actores para determinar cual esta mas cerca de la camara.
+
+ Este metodo se utiliza para ordenar los actores antes de imprimirlos
+ en pantalla. De modo tal que un usuario pueda seleccionar que
+ actores se ven mas arriba de otros cambiando los valores de
+ los atributos `z`."""
+
+ if otro_actor.z >= self.z:
+ return 1
+ else:
+ return -1
+
+ def get_izquierda(self):
+ return self.x - (self.centro[0] * self.escala)
+
+ @pilas.utils.interpolable
+ def set_izquierda(self, x):
+ self.x = x + (self.centro[0] * self.escala)
+
+ izquierda = property(get_izquierda, set_izquierda)
+
+ def get_derecha(self):
+ return self.izquierda + self.obtener_ancho()
+
+ @pilas.utils.interpolable
+ def set_derecha(self, x):
+ self.set_izquierda(x - self.ancho)
+
+ derecha = property(get_derecha, set_derecha)
+
+ def get_abajo(self):
+ return self.get_arriba() - self.alto
+
+ @pilas.utils.interpolable
+ def set_abajo(self, y):
+ self.set_arriba(y + self.alto)
+
+ abajo = property(get_abajo, set_abajo)
+
+ def get_arriba(self):
+ return self.y + (self.centro[1] * self.escala)
+
+ @pilas.utils.interpolable
+ def set_arriba(self, y):
+ self.y = y - (self.centro[1] * self.escala)
+
+ arriba = property(get_arriba, set_arriba)
+
+
+ def colisiona_con_un_punto(self, x, y):
+ """Determina si un punto colisiona con el area del actor.
+
+ Todos los actores tienen un area rectangular, pulsa la
+ tecla **F10** para ver el area de colision.
+ """
+ return self.izquierda <= x <= self.derecha and self.abajo <= y <= self.arriba
+
+ def obtener_rotacion(self):
+ return self._actor.obtener_rotacion()
+
+ def definir_rotacion(self, r):
+ r = r % 360
+ self._actor.definir_rotacion(r)
+
+ def definir_color(self, c):
+ self._actor.definir_color(c)
+
+ def obtener_imagen(self):
+ return self._actor.obtener_imagen()
+
+ def definir_imagen(self, imagen):
+ self._actor.definir_imagen(imagen)
+ self.centro = ('centro', 'centro')
+
+ def duplicar(self, **kv):
+ duplicado = self.__class__()
+
+ for clave in kv:
+ setattr(duplicado, clave, kv[clave])
+
+ return duplicado
+
+ def obtener_ancho(self):
+ return self.imagen.ancho()
+
+ def obtener_alto(self):
+ return self.imagen.alto()
+
+ ancho = property(obtener_ancho)
+ alto = property(obtener_alto)
+
+ def __mul__(self, cantidad):
+ if type(cantidad) is not int or cantidad < 1:
+ raise TypeError("Solo puede multiplicar por numeros enteros mayores a 1.")
+
+ grupo = pilas.atajos.fabricar(self.__class__, cantidad - 1)
+ grupo.append(self)
+ return grupo
+
+ def __str__(self):
+ return "<%s en (%d, %d)>" %(self.__class__.__name__, self.x, self.y)
+
+ def obtener_escala(self):
+ return self._actor.obtener_escala()
+
+ def definir_escala(self, escala):
+ self._actor.definir_escala(escala)
+
+ def definir_transparencia(self, valor):
+ self._actor.definir_transparencia(valor)
+
+ def imitar(self, otro_actor_o_figura):
+ self.aprender(pilas.habilidades.Imitar, otro_actor_o_figura)
+
+ def esta_fuera_de_la_pantalla(self):
+ # TODO: detectar area de la pantalla con las funciones que exporta el motor.
+ if self.derecha < -320 or self.izquierda > 320 or self.arriba < -240 or self.abajo > 240:
+ return True
+
+ def decir(self, mensaje, autoeliminar=True):
+ """Emite un mensaje usando un globo similar al de los commics"""
+ nuevo_actor = pilas.actores.Globo(mensaje, self.x, self.y, autoeliminar=autoeliminar)
+ nuevo_actor.z = self.z - 1
+ self.anexar(nuevo_actor)
+
+ def anexar(self, otro_actor):
+ self.anexados.append(otro_actor)
+
+ def _eliminar_anexados(self):
+ for x in self.anexados:
+ x.eliminar()
diff --git a/pilas/actores/animacion.py b/pilas/actores/animacion.py
new file mode 100644
index 0000000..0eb34e4
--- /dev/null
+++ b/pilas/actores/animacion.py
@@ -0,0 +1,60 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animado
+import copy
+
+VELOCIDAD = 10
+
+
+class Animacion(Animado):
+ """Representa una animacion de una grilla de imagenes.
+
+ Este actor toma una grilla de cuadros de animacion
+ y los reproduce hasta que la animacion termina. Cuando
+ la animacion termina se elimina a si mismo.
+
+ El constructor tiene algunos parámetros de utilidad:
+
+ - El parámetro ``ciclica`` permite hacer animaciones infinitas, que se repiten siempre, por defecto vale ``False`` que significa que la animación terminará y no se repetirá.
+ - El parámetro ``velocidad`` tiene que ser un número que indicará la cantidad de cuadros por segundo que se tienen que mostrar en la animación.
+
+ Por ejemplo, para mostrar una explosión infinita podrías
+ escribir:
+
+ >>> grilla = pilas.imagenes.cargar_grilla("explosion.png", 7)
+ >>> animacion = pilas.actores.Animacion(grilla, ciclica=True, velocidad=1)
+
+ .. image:: images/actores/explosion.png
+ """
+
+ def __init__(self, grilla, ciclica=False, x=0, y=0, velocidad=VELOCIDAD):
+ Animado.__init__(self, grilla, x=x, y=y)
+ self.tick = 0
+ self.ciclica = ciclica
+ self.definir_velocidad_de_animacion(velocidad)
+
+ def definir_velocidad_de_animacion(self, velocidad_de_animacion):
+ self._velocidad_de_animacion = (1000.0 / 60) * velocidad_de_animacion
+
+ def obtener_velocidad_de_animacion(self):
+ return self._velocidad_de_animacion
+
+ velocidad_de_animacion = property(obtener_velocidad_de_animacion, definir_velocidad_de_animacion, doc="Es la cantidad de cuadros por segundo a mostrar")
+
+ def actualizar(self):
+ self.tick += self.velocidad_de_animacion
+
+ if self.tick > 1000.0:
+ self.tick -= 1000.0
+ ha_reiniciado = self.imagen.avanzar()
+
+ # Si la animacion ha terminado se elimina de la pantalla.
+ if ha_reiniciado and not self.ciclica:
+ self.eliminar()
diff --git a/pilas/actores/animado.py b/pilas/actores/animado.py
new file mode 100644
index 0000000..024bbae
--- /dev/null
+++ b/pilas/actores/animado.py
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+import copy
+
+class Animado(Actor):
+ """Representa un actor que tiene asociada una grilla con cuadros de animacion.
+
+ Una de las variantes que introduce este actor es el
+ método 'definir_cuadro', que facilita la animación de personajes.
+
+ Por ejemplo, si tenemos una grilla con un pongüino, podríamos
+ mostrarlo usando este código:
+
+ >>> grilla = pilas.imagenes.cargar_grilla("pingu.png", 10)
+ >>> actor = Animado(grilla)
+ >>> actor.definir_cuadro(2)
+ >>> actor.definir_cuadro(5)
+
+
+ .. image:: images/actores/pingu.png
+ """
+
+ def __init__(self, grilla, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = copy.copy(grilla)
+ self.definir_cuadro(0)
+
+ def definir_cuadro(self, indice):
+ "Permite cambiar el cuadro de animación a mostrar"
+ self.imagen.definir_cuadro(indice)
+ # FIX: Esta sentencia es muy ambigua, porque no todos actores se deben centrar en ese punto.
+ self.centro = ('centro', 'centro')
diff --git a/pilas/actores/banana.py b/pilas/actores/banana.py
new file mode 100644
index 0000000..f283510
--- /dev/null
+++ b/pilas/actores/banana.py
@@ -0,0 +1,45 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+class Banana(Actor):
+ """Muestra una banana que se combina (temáticamente) con el actor Mono.
+
+ .. image:: images/actores/banana.png
+
+
+ Este actor se podría usar cómo alimento o bonus para otros
+ actores.
+
+ Este actor tiene solo dos cuadros de animación que se pueden
+ mostrar con los métodos ``abrir`` y ``cerrar``:
+
+ >>> banana = pilas.actores.Banana()
+ >>> banana.abrir()
+ >>> banana.cerrar()
+
+
+ """
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = pilas.imagenes.cargar_grilla("banana.png", 2)
+ self.definir_cuadro(0)
+
+ def definir_cuadro(self, indice):
+ self.imagen.definir_cuadro(indice)
+
+ def abrir(self):
+ """Muestra el gráfico de la banana abierta con menos cáscara."""
+ self.definir_cuadro(1)
+
+ def cerrar(self):
+ """Muestra el gráfico de banana normal (con cáscara)"""
+ self.definir_cuadro(0)
diff --git a/pilas/actores/bomba.py b/pilas/actores/bomba.py
new file mode 100644
index 0000000..71d5a11
--- /dev/null
+++ b/pilas/actores/bomba.py
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animacion
+from pilas.actores import Explosion
+
+class Bomba(Animacion):
+ """Representa una explosion para una bomba, dinamita etc...
+
+ .. image:: images/actores/bomba.png
+
+ La bomba adquiere la habilidad explotar al momento de crearse, así
+ que puedes invocar a su método "explotar" y la bomba hará un
+ explosión con sonido en pantalla.
+
+ Este es un ejemplo de uso del actor:
+
+ >>> bomba = pilas.actores.Bomba()
+ >>> bomba.explotar()
+ """
+
+
+ def __init__(self, x=0, y=0):
+ grilla = pilas.imagenes.cargar_grilla("bomba.png", 2)
+ Animacion.__init__(self, grilla, ciclica=True, x=x, y=y)
+ self.radio_de_colision = 25
+ self.aprender(pilas.habilidades.PuedeExplotar)
+
+ def explotar(self):
+ "Hace explotar a la bomba y la elimina de la pantalla."
+ self.eliminar()
diff --git a/pilas/actores/boton.py b/pilas/actores/boton.py
new file mode 100644
index 0000000..a57abb5
--- /dev/null
+++ b/pilas/actores/boton.py
@@ -0,0 +1,142 @@
+# -*- encoding: utf-8 -*-
+# For Pilas engine - A video game framework.
+#
+# Copyright 2011 - Pablo Garrido
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+#
+
+
+from pilas.actores import Actor
+import pilas
+
+class Boton(Actor):
+ """Representa un boton que reacciona al ser presionado."""
+
+ def __init__(self, x=0, y=0,
+ ruta_normal = 'boton/boton_normal.png',
+ ruta_press = 'boton/boton_press.png',
+ ruta_over = 'boton/boton_over.png',
+ ):
+
+ self.ruta_normal = ruta_normal
+ self.ruta_press = ruta_press
+ self.ruta_over = ruta_over
+
+ self.funciones_normal = []
+ self.funciones_press = []
+ self.funciones_over = []
+
+ self.estado = True
+
+ Actor.__init__(self, ruta_normal, x=x, y=y)
+ self._cargar_imagenes(self.ruta_normal, self.ruta_press, self.ruta_over)
+
+ pilas.eventos.mueve_mouse.conectar(self.detection_move_mouse)
+ pilas.eventos.click_de_mouse.conectar(self.detection_click_mouse)
+ pilas.eventos.termina_click.conectar(self.detection_end_click_mouse)
+
+ def _cargar_imagenes(self, ruta_normal, ruta_press, ruta_over):
+ self.ruta_normal = ruta_normal
+ self.ruta_press = ruta_press
+ self.ruta_over = ruta_over
+
+ self.imagen_over = pilas.imagenes.cargar(ruta_over)
+ self.imagen_normal = pilas.imagenes.cargar(ruta_normal)
+ self.imagen_press = pilas.imagenes.cargar(ruta_press)
+
+
+ #funciones que conectan evento(press, over, normal) a funciones
+ def conectar_normal(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_normal.append(t)
+
+ def conectar_presionado(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_press.append(t)
+
+ def conectar_sobre(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_over.append(t)
+
+ def desconectar_normal_todo(self):
+ self.funciones_normal = []
+
+ def desconectar_presionado_todo(self):
+ self.funciones_press = []
+
+ def desconectar_sobre_todo(self):
+ self.funciones_over = []
+
+ def desconectar_normal(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_normal.remove(t)
+
+ def desconectar_presionado(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_press.remove(t)
+
+ def desconectar_sobre(self, funcion, arg = "null"):
+ t = (funcion, arg)
+ self.funciones_over.remove(t)
+
+ def ejecutar_funciones_normal(self):
+ if self.estado == True:
+ for i in self.funciones_normal:
+ if i[1] == "null":
+ i[0]()
+ else:
+ i[0](i[1])
+
+ def ejecutar_funciones_press(self):
+ if self.estado == True:
+ for i in self.funciones_press:
+ if i[1] == "null":
+ i[0]()
+ else:
+ i[0](i[1])
+
+
+ def ejecutar_funciones_over(self):
+ if self.estado == True:
+ for i in self.funciones_over:
+ if i[1] == "null":
+ i[0]()
+ else:
+ i[0](i[1])
+
+ # funciones para inactivar o activar las funciones conectadas
+ def activar(self):
+ self.estado = True
+
+ def desactivar(self):
+ self.estado = False
+
+ # funciones que cambian la imagen del boton
+ def pintar_normal(self):
+ self.definir_imagen(self.imagen_normal)
+
+ def pintar_presionado(self, ruta_press = "null"):
+ if ruta_press == "null":
+ self.imagen_press = pilas.imagenes.cargar(self.ruta_press)
+ else:
+ self.imagen_press = pilas.imagenes.cargar(ruta_press)
+
+ self.definir_imagen(self.imagen_press)
+
+ def pintar_sobre(self):
+ self.definir_imagen(self.imagen_over)
+
+ def detection_move_mouse(self, evento):
+ if self.colisiona_con_un_punto(evento.x, evento.y):
+ self.ejecutar_funciones_over()
+ else:
+ self.ejecutar_funciones_normal()
+
+ def detection_click_mouse(self, click):
+ if self.colisiona_con_un_punto(click.x, click.y):
+ self.ejecutar_funciones_press()
+
+ def detection_end_click_mouse(self, end_click):
+ pass
diff --git a/pilas/actores/caja.py b/pilas/actores/caja.py
new file mode 100644
index 0000000..c038ea6
--- /dev/null
+++ b/pilas/actores/caja.py
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class Caja(Actor):
+ "Representa un bloque que tiene fisica como una caja."
+
+ def __init__(self, x=0, y=0):
+ imagen = pilas.imagenes.cargar('caja.png')
+ Actor.__init__(self, imagen)
+ self.rotacion = 0
+ self.x = x
+ self.y = y
+ self.radio_de_colision = 25
+
+ self.aprender(pilas.habilidades.RebotarComoCaja)
diff --git a/pilas/actores/cooperativista.py b/pilas/actores/cooperativista.py
new file mode 100644
index 0000000..5f00421
--- /dev/null
+++ b/pilas/actores/cooperativista.py
@@ -0,0 +1,105 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+from pilas.comportamientos import Comportamiento
+
+VELOCIDAD = 4
+
+class Cooperativista(Actor):
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self._cargar_animaciones()
+ self.hacer(Esperando())
+ self.radio_de_colision = 30
+
+ def _cargar_animaciones(self):
+ cargar = pilas.imagenes.cargar_grilla
+ self.animaciones = {
+ "ok": cargar("cooperativista/ok.png", 1),
+ "parado": cargar("cooperativista/parado.png", 1),
+ "camina": cargar("cooperativista/camina.png", 4),
+ # las siguientes estan sin usar...
+ "alerta": cargar("cooperativista/alerta.png", 2),
+ "trabajando": cargar("cooperativista/trabajando.png", 1),
+ "parado_sujeta": cargar("cooperativista/parado_sujeta.png", 1),
+ "camina_sujeta": cargar("cooperativista/camina_sujeta.png", 4),
+ }
+
+ def definir_cuadro(self, indice):
+ self.imagen.definir_cuadro(indice)
+
+ def cambiar_animacion(self, nombre):
+ self.imagen = self.animaciones[nombre]
+ self.centro = ("centro", "abajo")
+
+class Esperando(Comportamiento):
+ "Un actor en posicion normal o esperando a que el usuario pulse alguna tecla."
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.cambiar_animacion("parado")
+ self.receptor.definir_cuadro(0)
+
+ def actualizar(self):
+ if pilas.mundo.control.izquierda:
+ self.receptor.hacer(Caminando())
+ elif pilas.mundo.control.derecha:
+ self.receptor.hacer(Caminando())
+
+ if pilas.mundo.control.arriba:
+ self.receptor.hacer(DecirOk())
+
+
+class Caminando(Comportamiento):
+
+ def __init__(self):
+ self.cuadros = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]
+ self.paso = 0
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.cambiar_animacion("camina")
+
+ def actualizar(self):
+ self.avanzar_animacion()
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.x -= VELOCIDAD
+ self.receptor.espejado = False
+ elif pilas.mundo.control.derecha:
+ self.receptor.x += VELOCIDAD
+ self.receptor.espejado = True
+ else:
+ self.receptor.hacer(Esperando())
+
+ def avanzar_animacion(self):
+ self.paso += 1
+
+ if self.paso >= len(self.cuadros):
+ self.paso = 0
+
+ self.receptor.definir_cuadro(self.cuadros[self.paso])
+
+
+class DecirOk(Comportamiento):
+
+ def __init__(self):
+ self.paso = 0
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.cambiar_animacion("ok")
+
+ def actualizar(self):
+ self.paso += 1
+
+ if self.paso > 50:
+ self.receptor.hacer(Esperando())
diff --git a/pilas/actores/cursordisparo.py b/pilas/actores/cursordisparo.py
new file mode 100644
index 0000000..96c9cca
--- /dev/null
+++ b/pilas/actores/cursordisparo.py
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class CursorDisparo(Actor):
+ "Representa un bloque que tiene fisica como una caja."
+
+ def __init__(self, x=0, y=0):
+ imagen = pilas.imagenes.cargar('cursordisparo.png')
+ Actor.__init__(self, imagen)
+ self.rotacion = 0
+ self.x = x
+ self.y = y
+ self.radio_de_colision = 25
+
+ self.aprender(pilas.habilidades.SeguirAlMouse)
+ pilas.mundo.motor.ocultar_puntero_del_mouse()
diff --git a/pilas/actores/dialogo.py b/pilas/actores/dialogo.py
new file mode 100644
index 0000000..d6089ad
--- /dev/null
+++ b/pilas/actores/dialogo.py
@@ -0,0 +1,72 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class Dialogo:
+ "Representa una secuencia de mensajes entre varios actores."
+
+ def __init__(self, modo_automatico=True):
+ self.dialogo = []
+ self.dialogo_actual = None
+ self.modo_automatico = modo_automatico
+
+ def decir(self, actor, texto):
+ self.dialogo.append((actor, texto))
+
+ def decir_inmediatamente(self, actor, texto):
+ self.dialogo = []
+ self._eliminar_dialogo_actual()
+ self.decir(actor, texto)
+ siguiente = self.obtener_siguiente_dialogo_o_funcion()
+ self._mostrar_o_ejecutar_siguiente(siguiente)
+
+ def elegir(self, actor, texto, opciones, funcion_a_invocar):
+ self.dialogo.append((actor, texto, opciones, funcion_a_invocar))
+
+ def ejecutar(self, funcion):
+ self.dialogo.append(funcion)
+
+ def iniciar(self):
+ self.avanzar_al_siguiente_dialogo()
+
+ def obtener_siguiente_dialogo_o_funcion(self):
+ if self.dialogo:
+ return self.dialogo.pop(0)
+
+ def _eliminar_dialogo_actual(self):
+ if self.dialogo_actual:
+ self.dialogo_actual.eliminar()
+ self.dialogo_actual = None
+
+ def _mostrar_o_ejecutar_siguiente(self, siguiente):
+ if isinstance(siguiente, tuple):
+ # Es un mensaje de dialogo simple
+ if len(siguiente) == 2:
+ actor, texto = siguiente
+ self.dialogo_actual = pilas.actores.Globo(texto, dialogo=self, avance_con_clicks=self.modo_automatico)
+ else:
+ # Es un mensaje con seleccion.
+ actor, texto, opciones, funcion_a_invocar = siguiente
+ self.dialogo_actual = pilas.actores.GloboElegir(texto, opciones, funcion_a_invocar, dialogo=self)
+
+ self.dialogo_actual.colocar_origen_del_globo(actor.x, actor.arriba)
+ else:
+ siguiente()
+ self.avanzar_al_siguiente_dialogo()
+
+ def avanzar_al_siguiente_dialogo(self, evento=None):
+ self._eliminar_dialogo_actual()
+ siguiente = self.obtener_siguiente_dialogo_o_funcion()
+
+ if siguiente:
+ self._mostrar_o_ejecutar_siguiente(siguiente)
+ else:
+ return False
+
+ return True
diff --git a/pilas/actores/disparo.py b/pilas/actores/disparo.py
new file mode 100644
index 0000000..ccd19c0
--- /dev/null
+++ b/pilas/actores/disparo.py
@@ -0,0 +1,33 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animacion
+import math
+
+class Disparo(Animacion):
+ "Representa un disparo que avanza."
+
+ def __init__(self, x=0, y=0, rotacion=0, velocidad=2):
+ self.velocidad = velocidad
+ grilla = pilas.imagenes.cargar_grilla("disparo.png", 2)
+ Animacion.__init__(self, grilla, ciclica=True, x=x, y=y)
+ self.radio_de_colision = 10
+ self.rotacion = rotacion
+
+ def actualizar(self):
+ Animacion.actualizar(self)
+ self.avanzar()
+
+ def avanzar(self):
+ "Hace avanzar la nave en direccion a su angulo."
+ rotacion_en_radianes = math.radians(-self.rotacion + 90)
+ dx = math.cos(rotacion_en_radianes) * self.velocidad
+ dy = math.sin(rotacion_en_radianes) * self.velocidad
+ self.x += dx
+ self.y += dy
diff --git a/pilas/actores/ejes.py b/pilas/actores/ejes.py
new file mode 100644
index 0000000..c62ed52
--- /dev/null
+++ b/pilas/actores/ejes.py
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+
+class Ejes(Actor):
+ """Representa el eje de coordenadas tomado como sistema de referencia.
+
+ Este actor es útil para mostrar que la ventana
+ de pilas tiene una referencia, y que las posiciones
+ responden a este modelo.
+
+ Para crear el eje podrías ejecutar:
+
+ >>> eje = pilas.actore.Eje()
+
+ """
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, "ejes.png", x=x, y=y)
+ self.z = 100
diff --git a/pilas/actores/entradadetexto.py b/pilas/actores/entradadetexto.py
new file mode 100644
index 0000000..8902b08
--- /dev/null
+++ b/pilas/actores/entradadetexto.py
@@ -0,0 +1,53 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class EntradaDeTexto(Actor):
+ """Representa una caja de texto que puede servir para ingresar texto.
+
+ Este actor, en la mayoria de los casos, se utiliza para solicitarle
+ el nombre a un usuario. Por ejemplo, cuando completa un record
+ de puntaje."""
+
+ def __init__(self, x=0, y=0, color=pilas.colores.negro, limite=10, tamano=32, fuente='Arial', cursor_intermitente=True):
+ self.cursor = "|"
+ self.texto = ""
+ self.limite = limite
+ imagen = pilas.imagenes.cargar_superficie(640, 480)
+ Actor.__init__(self, imagen)
+ pilas.eventos.pulsa_tecla.conectar(self.cuando_pulsa_una_tecla)
+ self._actualizar_imagen()
+
+ if cursor_intermitente:
+ pilas.mundo.agregar_tarea_siempre(0.25, self._actualizar_cursor)
+
+ def _actualizar_cursor(self):
+ if self.cursor == "":
+ self.cursor = "|"
+ else:
+ self.cursor = ""
+
+ self._actualizar_imagen()
+ return True
+
+
+ def cuando_pulsa_una_tecla(self, evento):
+ if evento.codigo == '\x08':
+ # Indica que se quiere borrar un caracter
+ self.texto = self.texto[:-1]
+ else:
+ if len(self.texto) < self.limite:
+ self.texto = self.texto + evento.texto
+
+ self._actualizar_imagen()
+
+ def _actualizar_imagen(self):
+ self.imagen.pintar(pilas.colores.blanco)
+ self.imagen.texto(self.texto + self.cursor, 100, 100)
diff --git a/pilas/actores/estrella.py b/pilas/actores/estrella.py
new file mode 100644
index 0000000..019b57e
--- /dev/null
+++ b/pilas/actores/estrella.py
@@ -0,0 +1,19 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+
+class Estrella(Actor):
+ "Representa una estrella de color amarillo."
+
+ def __init__(self, x=0, y=0):
+ imagen = pilas.imagenes.cargar('estrella.png')
+ Actor.__init__(self, imagen, x=x, y=y)
+ self.rotacion = 0
diff --git a/pilas/actores/explosion.py b/pilas/actores/explosion.py
new file mode 100644
index 0000000..930d7b3
--- /dev/null
+++ b/pilas/actores/explosion.py
@@ -0,0 +1,39 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animacion
+
+class Explosion(Animacion):
+ """Representa una explosion para una bomba, dinamita etc...
+
+ El actor simplemente aparece reproduciendo un sonido y
+ haciendo una animación:
+
+ >>> actor = pilas.actores.Bomba()
+
+ .. image:: images/actores/explosion.png
+
+ y una vez que termina se elimina a sí mismo.
+
+ Este actor también se puede anexar a cualquier
+ otro para producir explosiones. Cuando enseñamos a un
+ actor a explotar (por ejemplo un pingüino), el actor
+ ``Explosion`` aparece cuando se elimina al actor::
+
+ >>> actor = pilas.actores.Pingu()
+ >>> actor.aprender(pilas.habilidades.PuedeExplotar)
+ >>> actor.eliminar()
+ """
+
+
+ def __init__(self, x=0, y=0):
+ grilla = pilas.imagenes.cargar_grilla("explosion.png", 7)
+ Animacion.__init__(self, grilla, x=x, y=y)
+ self.sonido_explosion = pilas.sonidos.cargar("explosion.wav")
+ self.sonido_explosion.reproducir()
diff --git a/pilas/actores/globo.py b/pilas/actores/globo.py
new file mode 100644
index 0000000..744798e
--- /dev/null
+++ b/pilas/actores/globo.py
@@ -0,0 +1,79 @@
+# -*- encoding: utf-8 -*-
+import pilas
+from pilas.actores import Actor
+
+class Globo(Actor):
+ "Representa un cuadro de dialogo estilo historietas."
+
+ def __init__(self, texto, x=0, y=0, dialogo=None, avance_con_clicks=True, autoeliminar=False):
+ self.dialogo = dialogo
+ Actor.__init__(self, imagen='invisible.png', x=x, y=y)
+
+ ancho, alto = pilas.utils.obtener_area_de_texto(texto)
+
+ ancho = int((ancho + 12) - (ancho % 12))
+ alto = int((alto + 12) - alto % 12)
+
+ self.imagen = pilas.imagenes.cargar_superficie(ancho + 36, alto + 24 + 35)
+
+ self._pintar_globo(ancho, alto)
+ self.imagen.texto(texto, 17, 30)
+ self.centro = ("derecha", "abajo")
+ self.escala = 0.1
+ self.escala = [1], 0.2
+
+ if avance_con_clicks:
+ pilas.eventos.click_de_mouse.conectar(self.cuando_quieren_avanzar)
+
+ if autoeliminar:
+ pilas.mundo.tareas.una_vez(3, self.eliminar)
+
+ def colocar_origen_del_globo(self, x, y):
+ "Cambia la posicion del globo para que el punto de donde se emite el globo sea (x, y)."
+ self.x = x
+ self.y = y
+
+
+ def cuando_quieren_avanzar(self, *k):
+ if self.dialogo:
+ self.dialogo.avanzar_al_siguiente_dialogo()
+ else:
+ self.eliminar()
+
+ def _pintar_globo(self, ancho, alto):
+ imagen = pilas.imagenes.cargar("globo.png")
+
+ # esquina sup-izq
+ self.imagen.pintar_parte_de_imagen(imagen, 0, 0, 12, 12, 0, 0)
+
+ # borde superior
+ for x in range(0, int(ancho) + 12, 12):
+ self.imagen.pintar_parte_de_imagen(imagen, 12, 0, 12, 12, 12 + x, 0)
+
+ # esquina sup-der
+ self.imagen.pintar_parte_de_imagen(imagen, 100, 0, 12, 12, 12 + int(ancho) + 12, 0)
+
+ # centro del dialogo
+ for y in range(0, int(alto) + 12, 12):
+ # borde izquierdo
+ self.imagen.pintar_parte_de_imagen(imagen, 0, 12, 12, 12, 0, 12 + y)
+ # linea horizontal blanca, para el centro del dialogo.
+ for x in range(0, int(ancho) + 12, 12):
+ self.imagen.pintar_parte_de_imagen(imagen, 12, 12, 12, 12, 12 + x, 12 + y)
+
+ # borde derecho
+ self.imagen.pintar_parte_de_imagen(imagen, 100, 12, 12, 12, 12 + int(ancho) + 12, 12 + y)
+
+ # parte inferior
+ self.imagen.pintar_parte_de_imagen(imagen, 0, 35, 12, 12, 0, 0 + int(alto) + 12 + 12)
+
+ # linea horizontal de la parte inferior
+ for x in range(0, int(ancho) + 12, 12):
+ self.imagen.pintar_parte_de_imagen(imagen, 12, 35, 12, 12, 12 + x, 0 + int(alto) + 12 + 12)
+
+ self.imagen.pintar_parte_de_imagen(imagen, 100, 35, 12, 12, 12 + int(ancho) + 12, 0 + int(alto) + 12 + 12)
+ # Pico de la parte de abajo
+ self.imagen.pintar_parte_de_imagen(imagen, 67, 35, 33, 25, int(ancho) - 12, 0 + int(alto) + 12 + 12)
+
+ def eliminar(self):
+ Actor.eliminar(self)
diff --git a/pilas/actores/globoelegir.py b/pilas/actores/globoelegir.py
new file mode 100644
index 0000000..0340dfc
--- /dev/null
+++ b/pilas/actores/globoelegir.py
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+import pilas
+from pilas.actores.globo import Globo
+
+class GloboElegir(Globo):
+
+ def __init__(self, texto, opciones, funcion_a_invocar, x=0, y=0, dialogo=None):
+ self.dialogo = dialogo
+ self.opciones = opciones
+ self.funcion_a_invocar = funcion_a_invocar
+ espacio = "\n" * (len(opciones) +1) # representa un espacio en blanco para poner la seleccion
+ Globo.__init__(self, texto + espacio, x, y, dialogo=dialogo)
+
+ self.lista_seleccion = pilas.interfaz.ListaSeleccion(opciones, self._cuando_selecciona_opcion, x, y)
+ self.lista_seleccion.escala = 0.1
+ self.lista_seleccion.escala = [1], 0.2
+
+ def colocar_origen_del_globo(self, x, y):
+ self.lista_seleccion.centro = ("derecha", "abajo")
+ self.lista_seleccion.x = x - 10
+ self.lista_seleccion.y = y - 10
+
+ def _obtener_area_para_el_texto(self, texto):
+ ancho, alto = self.lienzo.obtener_area_de_texto(texto, tamano=14)
+ opciones_ancho, opciones_alto = self.lienzo.obtener_area_para_lista_de_texto(self.opciones, tamano=14)
+ return ancho + opciones_ancho, alto + opciones_alto
+
+ def _escribir_texto(self, texto):
+ self.lienzo.escribir(texto, 12, 25, tamano=14)
+
+ def cuando_quieren_avanzar(self, *k):
+ pass
+
+ def _cuando_selecciona_opcion(self, opcion):
+ self.funcion_a_invocar(opcion)
+ Globo.cuando_quieren_avanzar(self)
+ self.lista_seleccion.eliminar()
diff --git a/pilas/actores/mano.py b/pilas/actores/mano.py
new file mode 100644
index 0000000..4d79e5f
--- /dev/null
+++ b/pilas/actores/mano.py
@@ -0,0 +1,46 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class CursorMano(Actor):
+ "Representa un bloque que tiene fisica como una caja."
+
+ def __init__(self, x=0, y=0):
+ self._cargar_imagenes()
+ Actor.__init__(self, self.imagen_normal)
+ self.x = x
+ self.y = y
+
+ self.aprender(pilas.habilidades.SeguirAlMouse)
+ pilas.mundo.motor.ocultar_puntero_del_mouse()
+ self.z = -200
+ self.pulsado = False
+
+ self.centro = ("izquierda", "arriba")
+
+ pilas.eventos.mueve_mouse.conectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.click_de_mouse.conectar(self.cuando_pulsa_el_mouse)
+ pilas.eventos.termina_click.conectar(self.cuando_suelta_el_mouse)
+
+ def _cargar_imagenes(self):
+ self.imagen_normal = pilas.imagenes.cargar("cursores/normal.png")
+ self.imagen_arrastrando = pilas.imagenes.cargar("cursores/arrastrando.png")
+
+ def cuando_pulsa_el_mouse(self, evento):
+ self.pulsado = True
+
+ def cuando_mueve_el_mouse(self, evento):
+ if self.pulsado:
+ self.imagen = self.imagen_arrastrando
+
+ def cuando_suelta_el_mouse(self, evento):
+ if self.pulsado:
+ self.imagen = self.imagen_normal
+ self.pulsado = False
diff --git a/pilas/actores/mapa.py b/pilas/actores/mapa.py
new file mode 100644
index 0000000..aedec79
--- /dev/null
+++ b/pilas/actores/mapa.py
@@ -0,0 +1,130 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+
+class Mapa(Actor):
+ """Representa mapas creados a partir de imagenes mas pequeñas.
+
+ Este actor te permite crear escenarios tipo ``tiles``, una técnica
+ de contrucción de escenarios muy popular en los videojuegos.
+
+ Puedes crear un actor a partir de una grilla, e indicando cada
+ uno los bloques o simplemente usando un programa externo llamado
+ **tiled** (ver http://www.mapeditor.org).
+
+ Por ejemplo, para crear un mapa desde un archivo del programa
+ **tiled** puedes escribir:
+
+ >>> mapa = pilas.actores.Mapa('untitled2.tmx')
+ """
+
+ def __init__(self, grilla_o_mapa=None, x=0, y=0, restitucion=0.56):
+ Actor.__init__(self, 'invisible.png', x, y)
+ self.restitucion = restitucion
+ self.figuras = []
+ self.bloques = []
+
+ if not grilla_o_mapa:
+ grilla_o_mapa = grilla = pilas.imagenes.cargar_grilla("grillas/plataformas_10_10.png", 10, 10)
+
+ self.grilla_o_mapa = grilla_o_mapa
+
+ if isinstance(grilla_o_mapa, str):
+ self._cargar_mapa(grilla_o_mapa)
+ else:
+ self.grilla = grilla_o_mapa
+ self._ancho_cuadro = grilla_o_mapa.cuadro_ancho
+ self._alto_cuadro = grilla_o_mapa.cuadro_alto
+
+ def _cargar_mapa(self, archivo):
+ "Carga el escenario desde un archivo .tmz (del programa tiled)."
+
+ archivo = pilas.utils.obtener_ruta_al_recurso(archivo)
+
+ # Carga los nodos principales.
+ nodo = pilas.utils.xmlreader.makeRootNode(archivo)
+ nodo_mapa = nodo.getChild('map')
+ nodo_tileset = nodo_mapa.getChild('tileset')
+
+ # Cantidad de bloques en el mapa.
+ self.columnas = int(nodo_mapa.getAttributeValue('width'))
+ self.filas = int(nodo_mapa.getAttributeValue('height'))
+
+ # Atributos de la imagen asociada al mapa.
+ self._ruta = nodo_tileset.getChild('image').getAttributeValue('source')
+ self._ruta = pilas.utils.obtener_ruta_al_recurso(self._ruta)
+
+ self._ancho_imagen = int(nodo_tileset.getChild('image').getAttributeValue('width'))
+ self._alto_imagen = int(nodo_tileset.getChild('image').getAttributeValue('height'))
+ self._ancho_cuadro = int(nodo_tileset.getAttributeValue('tilewidth'))
+ self._alto_cuadro = int(nodo_tileset.getAttributeValue('tileheight'))
+
+ # Carga la grilla de imagenes desde el mapa.
+ self.grilla = pilas.imagenes.cargar_grilla(self._ruta,
+ self._ancho_imagen / self._ancho_cuadro,
+ self._alto_imagen / self._alto_cuadro)
+
+ # Carga las capas del mapa.
+ layers = nodo.getChild('map').getChildren('layer')
+
+ if len(layers) == 0:
+ raise Exception("Debe tener al menos una capa (layer).")
+
+ # La capa 0 (inferior) define los bloques no-solidos.
+ self._crear_bloques(layers[0], solidos=False)
+
+ # El resto de las capas definen bloques solidos
+ for layer in layers[1:]:
+ self._crear_bloques(layer, solidos=True)
+
+ def _crear_bloques(self, capa, solidos):
+ "Genera actores que representan los bloques del escenario."
+ datos = capa.getChild('data').getData()
+
+ # Convierte todo el mapa en una matriz de numeros.
+ bloques = [[int(x) for x in x.split(',') if x] for x in datos.split()]
+
+ for (y, fila) in enumerate(bloques):
+ for (x, bloque) in enumerate(fila):
+ if bloque:
+ self.pintar_bloque(y, x, bloque -1, solidos)
+
+ def pintar_bloque(self, fila, columna, indice, es_bloque_solido=False):
+ nuevo_bloque = pilas.actores.Actor('invisible.png')
+ nuevo_bloque.imagen = self.grilla
+ nuevo_bloque.imagen.definir_cuadro(indice)
+ nuevo_bloque.izquierda = columna * self._ancho_cuadro - 320
+ nuevo_bloque.arriba = -fila * self._alto_cuadro + 240
+ self.bloques.append(nuevo_bloque)
+
+ if es_bloque_solido:
+ figura = pilas.fisica.Rectangulo(nuevo_bloque.izquierda + self._ancho_cuadro / 2,
+ nuevo_bloque.arriba - self._alto_cuadro / 2,
+ self._ancho_cuadro, self._alto_cuadro, dinamica=False,
+ restitucion=self.restitucion)
+ self.figuras.append(figura)
+
+
+ def reiniciar(self):
+ self._eliminar_bloques()
+
+ if isinstance(self.grilla_o_mapa, str):
+ self._cargar_mapa(self.grilla_o_mapa)
+
+ def eliminar(self):
+ self._eliminar_bloques()
+
+ def _eliminar_bloques(self):
+ for b in self.bloques:
+ b.eliminar()
+
+ for f in self.figuras:
+ f.eliminar()
diff --git a/pilas/actores/martian.py b/pilas/actores/martian.py
new file mode 100644
index 0000000..00076ef
--- /dev/null
+++ b/pilas/actores/martian.py
@@ -0,0 +1,151 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+from pilas.comportamientos import Comportamiento
+
+VELOCIDAD = 100
+
+
+class Martian(Actor):
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = pilas.imagenes.cargar_grilla("marcianitos/martian.png", 12)
+ self.definir_cuadro(0)
+ self.figura = pilas.fisica.Circulo(0, 0, 18, friccion=0, amortiguacion=0, restitucion=0.1)
+ self.hacer(Esperando())
+
+ def definir_cuadro(self, indice):
+ self.imagen.definir_cuadro(indice)
+ self.definir_centro((32, 123))
+
+ def actualizar(self):
+ "Sigue el movimiento de la figura."
+ self.x = self.figura.x
+ self.y = self.figura.y - 15
+ self.figura.rotacion = 0
+
+ def crear_disparo(self):
+ if self.espejado:
+ rotacion = -90
+ else:
+ rotacion = 90
+
+ disparo = pilas.actores.Disparo(x=self.x, y=self.y+20, rotacion=rotacion, velocidad=10)
+
+ def puede_saltar(self):
+ dx, dy = self.figura.obtener_velocidad_lineal()
+ return -2 < dy < 2
+
+class Esperando(Comportamiento):
+ "Un actor en posicion normal o esperando a que el usuario pulse alguna tecla."
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.definir_cuadro(0)
+
+ def actualizar(self):
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.hacer(Caminando())
+ elif pilas.mundo.control.derecha:
+ self.receptor.hacer(Caminando())
+
+ if pilas.mundo.control.arriba and self.receptor.puede_saltar():
+ self.receptor.hacer(Saltando())
+
+ if pilas.mundo.control.boton:
+ self.receptor.hacer(Disparar(self.receptor))
+
+class Caminando(Comportamiento):
+
+ def __init__(self):
+ self.cuadros = [1, 1, 1, 2, 2, 2]
+ self.paso = 0
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+
+ def actualizar(self):
+ self.avanzar_animacion()
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.figura.definir_velocidad_lineal(-VELOCIDAD)
+ self.receptor.espejado = True
+ elif pilas.mundo.control.derecha:
+ self.receptor.figura.definir_velocidad_lineal(+VELOCIDAD)
+ self.receptor.espejado = False
+ else:
+ self.receptor.figura.definir_velocidad_lineal(0)
+ self.receptor.hacer(Esperando())
+
+ if pilas.mundo.control.arriba:
+ self.receptor.hacer(Saltando())
+
+ def avanzar_animacion(self):
+ self.paso += 1
+
+ if self.paso >= len(self.cuadros):
+ self.paso = 0
+
+ self.receptor.definir_cuadro(self.cuadros[self.paso])
+
+class Saltando(Comportamiento):
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.definir_cuadro(3)
+ self.esta_bajando = False
+ self.receptor.figura.definir_velocidad_lineal(None, 300)
+
+
+ def actualizar(self):
+
+ # obtiene la velocidad del personaje para detectar cuando
+ # toca el suelo.
+ vx, vy = self.receptor.figura.obtener_velocidad_lineal()
+
+ if vy < 0:
+ self.esta_bajando = True
+
+ if self.esta_bajando and -2 < vy < 2:
+ self.receptor.figura.definir_velocidad_lineal(0,0)
+ self.receptor.hacer(Esperando())
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.espejado = True
+ self.receptor.figura.definir_velocidad_lineal(-VELOCIDAD)
+ elif pilas.mundo.control.derecha:
+ self.receptor.espejado = False
+ self.receptor.figura.definir_velocidad_lineal(VELOCIDAD)
+ else:
+ self.receptor.figura.definir_velocidad_lineal(0)
+
+class Disparar(Comportamiento):
+
+ def __init__(self, receptor):
+ self.cuadros = [6, 6, 7, 7, 8, 8]
+ self.paso = 0
+ receptor.crear_disparo()
+
+ def actualizar(self):
+ termina = self.avanzar_animacion()
+
+ if termina:
+ self.receptor.hacer(Esperando())
+
+ def avanzar_animacion(self):
+ self.paso += 1
+
+ if self.paso >= len(self.cuadros):
+ self.paso = 0
+ return True
+
+ self.receptor.definir_cuadro(self.cuadros[self.paso])
diff --git a/pilas/actores/menu.py b/pilas/actores/menu.py
new file mode 100644
index 0000000..d488118
--- /dev/null
+++ b/pilas/actores/menu.py
@@ -0,0 +1,115 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+from pilas.grupo import Grupo
+import pilas
+
+DEMORA = 14
+
+class Menu(Actor):
+ "Representa un bloque que tiene fisica como una caja."
+
+ def __init__(self, opciones, x=0, y=0):
+ self.opciones_como_actores = []
+ self.demora_al_responder = 0
+ Actor.__init__(self, "invisible.png", x=x, y=y)
+ self._verificar_opciones(opciones)
+ self.crear_texto_de_las_opciones(opciones)
+ self.opciones = opciones
+ self.seleccionar_primer_opcion()
+ self.opcion_actual = 0
+ # contador para evitar la repeticion de teclas
+ self.activar()
+
+ def activar(self):
+ pilas.eventos.mueve_mouse.conectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.click_de_mouse.conectar(self.cuando_hace_click_con_el_mouse)
+
+ def desactivar(self):
+ pilas.eventos.mueve_mouse.desconectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.click_de_mouse.desconectar(self.cuando_hace_click_con_el_mouse)
+
+ def crear_texto_de_las_opciones(self, opciones):
+ "Genera un actor por cada opcion del menu."
+
+ for indice, (texto, funcion) in enumerate(opciones):
+ y = self.y - indice * 50
+ opciones = pilas.actores.Opcion(texto, x=0, y=y, funcion_a_invocar=funcion)
+
+ self.opciones_como_actores.append(opciones)
+
+ def seleccionar_primer_opcion(self):
+ if self.opciones_como_actores:
+ self.opciones_como_actores[0].resaltar()
+
+ def _verificar_opciones(self, opciones):
+ "Se asegura de que la lista este bien definida."
+
+ for x in opciones:
+ if not isinstance(x, tuple) or len(x) != 2:
+ raise Exception("Opciones incorrectas, cada opcion tiene que ser una tupla.")
+
+ def actualizar(self):
+ "Se ejecuta de manera periodica."
+
+ if self.demora_al_responder < 0:
+ if pilas.mundo.control.boton:
+ self.seleccionar_opcion_actual()
+ self.demora_al_responder = DEMORA
+
+ if pilas.mundo.control.abajo:
+ self.mover_cursor(1)
+ self.demora_al_responder = DEMORA
+ elif pilas.mundo.control.arriba:
+ self.mover_cursor(-1)
+ self.demora_al_responder = DEMORA
+
+ self.demora_al_responder -= 1
+
+ def seleccionar_opcion_actual(self):
+ opcion = self.opciones_como_actores[self.opcion_actual]
+ opcion.seleccionar()
+
+ def mover_cursor(self, delta):
+ # Deja como no-seleccionada la opcion actual.
+ self._deshabilitar_opcion_actual()
+
+ # Se asegura que las opciones esten entre 0 y 'cantidad de opciones'.
+ self.opcion_actual += delta
+ self.opcion_actual %= len(self.opciones_como_actores)
+
+ # Selecciona la opcion nueva.
+ self.opciones_como_actores[self.opcion_actual].resaltar()
+
+ def __setattr__(self, atributo, valor):
+ # Intenta propagar la accion a los actores del grupo.
+ try:
+ for x in self.opciones_como_actores:
+ setattr(x, atributo, valor)
+ except AttributeError:
+ pass
+
+ Actor.__setattr__(self, atributo, valor)
+
+ def cuando_mueve_el_mouse(self, evento):
+ "Permite cambiar la opcion actual moviendo el mouse. Retorna True si el mouse esta sobre alguna opcion."
+ for indice, opcion in enumerate(self.opciones_como_actores):
+ if opcion.colisiona_con_un_punto(evento.x, evento.y):
+ if indice != self.opcion_actual:
+ self._deshabilitar_opcion_actual()
+ self.opcion_actual = indice
+ self.opciones_como_actores[indice].resaltar()
+ return True
+
+ def _deshabilitar_opcion_actual(self):
+ self.opciones_como_actores[self.opcion_actual].resaltar(False)
+
+ def cuando_hace_click_con_el_mouse(self, evento):
+ if self.cuando_mueve_el_mouse(evento):
+ self.seleccionar_opcion_actual()
diff --git a/pilas/actores/moneda.py b/pilas/actores/moneda.py
new file mode 100644
index 0000000..b4079b0
--- /dev/null
+++ b/pilas/actores/moneda.py
@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animacion
+
+class Moneda(Animacion):
+ """Representa una moneda con animación.
+
+ >>> moneda = pilas.actores.Moneda()
+
+ .. image:: images/actores/moneda.png
+
+ """
+
+ def __init__(self, x=0, y=0):
+ Animacion.__init__(self, pilas.imagenes.cargar_grilla("moneda.png", 8), ciclica=True, x=x, y=y)
diff --git a/pilas/actores/mono.py b/pilas/actores/mono.py
new file mode 100644
index 0000000..012ab74
--- /dev/null
+++ b/pilas/actores/mono.py
@@ -0,0 +1,69 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class Mono(Actor):
+ """Representa la cara de un mono de color marrón.
+
+ .. image:: images/actores/mono.png
+
+ Este personaje se usa como ejemplo básico de un actor. Por
+ ejemplo, esta es una forma de usar al actor:
+
+ >>> mono = pilas.actores.Mono()
+ >>> mono.decir("Hola!!!")
+ >>> mono.gritar()
+ """
+
+ def __init__(self, x=0, y=0):
+ # carga las imagenes adicionales.
+ self.image_normal = pilas.imagenes.cargar('monkey_normal.png')
+ self.image_smile = pilas.imagenes.cargar('monkey_smile.png')
+ self.image_shout = pilas.imagenes.cargar('monkey_shout.png')
+
+ self.sound_shout = pilas.sonidos.cargar('shout.wav')
+ self.sound_smile = pilas.sonidos.cargar('smile.wav')
+
+ # Inicializa el actor.
+ Actor.__init__(self, self.image_normal, x=x, y=y)
+ self.radio_de_colision = 50
+
+ def sonreir(self):
+ """Hace que el mono sonria y emita un sonido."""
+ self.definir_imagen(self.image_smile)
+ # Luego de un segundo regresa a la normalidad
+ pilas.mundo.agregar_tarea_una_vez(0.5, self.normal)
+ self.sound_smile.reproducir()
+
+ def gritar(self):
+ """Hace que el mono grite emitiendo un sonido."""
+ self.definir_imagen(self.image_shout)
+ # Luego de un segundo regresa a la normalidad
+ pilas.mundo.agregar_tarea_una_vez(1, self.normal)
+ self.sound_shout.reproducir()
+
+ def normal(self):
+ """Restaura la expresión del mono.
+
+ Este función se suele ejecutar por si misma, unos
+ segundos después de haber gritado y sonreir."""
+ self.definir_imagen(self.image_normal)
+
+ def decir(self, mensaje):
+ """Emite un mensaje y además sonrie mientras habla.
+
+ Por ejemplo:
+
+ >>> mono.decir("Estoy hablando!!!")
+
+ .. image:: images/actores/mono_dice.png
+ """
+ self.sonreir()
+ Actor.decir(self, mensaje)
diff --git a/pilas/actores/nave.py b/pilas/actores/nave.py
new file mode 100644
index 0000000..b2ac3bb
--- /dev/null
+++ b/pilas/actores/nave.py
@@ -0,0 +1,79 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Animacion
+import math
+
+class Nave(Animacion):
+ "Representa una nave que puede disparar."
+
+ def __init__(self, x=0, y=0, velocidad=2):
+ self.velocidad = velocidad
+ grilla = pilas.imagenes.cargar_grilla("nave.png", 2)
+ Animacion.__init__(self, grilla, ciclica=True, x=x, y=y)
+ self.radio_de_colision = 20
+ self.aprender(pilas.habilidades.PuedeExplotar)
+ self.contador_frecuencia_disparo = 0
+ self.disparos = []
+
+ def actualizar(self):
+ Animacion.actualizar(self)
+
+ if pilas.mundo.control.izquierda:
+ self.rotacion -= self.velocidad
+ elif pilas.mundo.control.derecha:
+ self.rotacion += self.velocidad
+
+ if pilas.mundo.control.arriba:
+ self.avanzar()
+
+ self.contador_frecuencia_disparo += 1
+
+ if pilas.mundo.control.boton:
+ if self.contador_frecuencia_disparo > 10:
+ self.contador_frecuencia_disparo = 0
+ self.disparar()
+
+ self.eliminar_disparos_innecesarios()
+
+ def eliminar_disparos_innecesarios(self):
+ for d in list(self.disparos):
+ if d.x < -320 or d.x > 320 or d.y < -240 or d.y > 240:
+ d.eliminar()
+ self.disparos.remove(d)
+
+
+ def disparar(self):
+ "Hace que la nave dispare."
+ disparo_nuevo = pilas.actores.Disparo(self.x, self.y, self.rotacion, 4)
+ self.disparos.append(disparo_nuevo)
+
+ def avanzar(self):
+ "Hace avanzar la nave en direccion a su angulo."
+ rotacion_en_radianes = math.radians(-self.rotacion + 90)
+ dx = math.cos(rotacion_en_radianes) * self.velocidad
+ dy = math.sin(rotacion_en_radianes) * self.velocidad
+ self.x += dx
+ self.y += dy
+
+ def definir_enemigos(self, grupo, cuando_elimina_enemigo=None):
+ """hace que una nave tenga como enemigos a todos los actores del grupo.
+
+ El argumento cuando_elimina_enemigo tiene que ser una funcion que
+ se ejecutara cuando se produzca la colision."""
+ self.cuando_elimina_enemigo = cuando_elimina_enemigo
+ pilas.mundo.colisiones.agregar(self.disparos, grupo, self.hacer_explotar_al_enemigo)
+
+ def hacer_explotar_al_enemigo(self, mi_disparo, el_enemigo):
+ "Es el método que se invoca cuando se produce una colisión 'tiro <-> enemigo'"
+ mi_disparo.eliminar()
+ el_enemigo.eliminar()
+
+ if self.cuando_elimina_enemigo:
+ self.cuando_elimina_enemigo()
diff --git a/pilas/actores/opcion.py b/pilas/actores/opcion.py
new file mode 100644
index 0000000..2813525
--- /dev/null
+++ b/pilas/actores/opcion.py
@@ -0,0 +1,36 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Texto
+
+class Opcion(Texto):
+
+ def __init__(self, texto, x=0, y=0, funcion_a_invocar=None):
+ Texto.__init__(self, texto, x=x, y=y)
+ self.magnitud = 20
+ self.funcion_a_invocar = funcion_a_invocar
+ self.color = pilas.colores.gris
+ self.z = -300
+ self.centro = ("centro", "centro")
+
+ def resaltar(self, estado=True):
+ "Pinta la opcion actual de un color mas claro."
+
+ if estado:
+ self.color = pilas.colores.blanco
+ else:
+ self.color = pilas.colores.gris
+
+ def seleccionar(self):
+ "Invoca a la funcion que tiene asociada para ejecutar."
+
+ if self.funcion_a_invocar:
+ self.funcion_a_invocar()
+ else:
+ print "Cuidado, la opcion", self, "no tiene funcion asociada."
diff --git a/pilas/actores/pausa.py b/pilas/actores/pausa.py
new file mode 100644
index 0000000..6339058
--- /dev/null
+++ b/pilas/actores/pausa.py
@@ -0,0 +1,17 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+class Pausa(Actor):
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.centro = ('centro', 'centro')
+ self.imagen = "icono_pausa.png"
diff --git a/pilas/actores/pelota.py b/pilas/actores/pelota.py
new file mode 100644
index 0000000..88cee25
--- /dev/null
+++ b/pilas/actores/pelota.py
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class Pelota(Actor):
+ "Representa una pelota de Volley."
+
+ def __init__(self, x=0, y=0):
+ imagen = pilas.imagenes.cargar('pelota.png')
+ Actor.__init__(self, imagen)
+ self.rotacion = 0
+ self.x = x
+ self.y = y
+ self.radio_de_colision = 25
+
+ self.aprender(pilas.habilidades.RebotarComoPelota)
diff --git a/pilas/actores/piedra.py b/pilas/actores/piedra.py
new file mode 100644
index 0000000..9a2d2d1
--- /dev/null
+++ b/pilas/actores/piedra.py
@@ -0,0 +1,36 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+from pilas.actores import Actor
+import pilas
+
+class Piedra(Actor):
+ "Representa un bloque que tiene fisica como una caja."
+
+ def __init__(self, x=0, y=0, tamano="grande", dx=0, dy=0):
+ imagen = pilas.imagenes.cargar('piedra_' + tamano + '.png')
+ Actor.__init__(self, imagen)
+ self.rotacion = 0
+ self.x = x
+ self.y = y
+ self.dx = dx
+ self.dy = dy
+
+ radios = {
+ 'grande': 25,
+ 'media': 20,
+ 'chica': 10,
+ }
+
+ self.radio_de_colision = radios[tamano]
+ self.aprender(pilas.habilidades.SeMantieneEnPantalla)
+
+ def actualizar(self):
+ self.rotacion += 1
+ self.x += self.dx
+ self.y += self.dy
diff --git a/pilas/actores/pingu.py b/pilas/actores/pingu.py
new file mode 100644
index 0000000..1b9edcb
--- /dev/null
+++ b/pilas/actores/pingu.py
@@ -0,0 +1,104 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+from pilas.comportamientos import Comportamiento
+
+VELOCIDAD = 4
+
+class Pingu(Actor):
+ """Muestra a un pingüino que sabe caminar con el teclado.
+
+ .. image:: images/actores/pingu.png
+
+ Este actor responde al teclado, así que podremos
+ usar los direccionales del teclado ``izquierda``, ``arriba``
+ y ``derecha``:
+
+ >>> pingu = pilas.actores.Pingu()
+ """
+
+ def __init__(self, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = pilas.imagenes.cargar_grilla("pingu.png", 10)
+ self.definir_cuadro(4)
+ self.hacer(Esperando())
+ self.radio_de_colision = 30
+ self.centro = ("centro", "abajo")
+
+ def definir_cuadro(self, indice):
+ self.imagen.definir_cuadro(indice)
+
+
+class Esperando(Comportamiento):
+ "Un actor en posicion normal o esperando a que el usuario pulse alguna tecla."
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.definir_cuadro(4)
+
+ def actualizar(self):
+ if pilas.mundo.control.izquierda:
+ self.receptor.hacer(Caminando())
+ elif pilas.mundo.control.derecha:
+ self.receptor.hacer(Caminando())
+
+ if pilas.mundo.control.arriba:
+ self.receptor.hacer(Saltando())
+
+
+class Caminando(Comportamiento):
+
+ def __init__(self):
+ self.cuadros = [5, 5, 6, 6, 7, 7, 8, 8, 9, 9]
+ self.paso = 0
+
+ def actualizar(self):
+ self.avanzar_animacion()
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.x -= VELOCIDAD
+ elif pilas.mundo.control.derecha:
+ self.receptor.x += VELOCIDAD
+ else:
+ self.receptor.hacer(Esperando())
+
+ if pilas.mundo.control.arriba:
+ self.receptor.hacer(Saltando())
+
+ def avanzar_animacion(self):
+ self.paso += 1
+
+ if self.paso >= len(self.cuadros):
+ self.paso = 0
+
+ self.receptor.definir_cuadro(self.cuadros[self.paso])
+
+class Saltando(Comportamiento):
+
+ def __init__(self):
+ self.dy = 10
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.receptor.definir_cuadro(0)
+ self.origen = self.receptor.y
+
+ def actualizar(self):
+ self.receptor.y += self.dy
+ self.dy -= 0.3
+
+ if self.receptor.y < self.origen:
+ self.receptor.y = self.origen
+ self.receptor.hacer(Esperando())
+
+ if pilas.mundo.control.izquierda:
+ self.receptor.x -= VELOCIDAD
+ elif pilas.mundo.control.derecha:
+ self.receptor.x += VELOCIDAD
diff --git a/pilas/actores/pizarra.py b/pilas/actores/pizarra.py
new file mode 100644
index 0000000..8fea8ba
--- /dev/null
+++ b/pilas/actores/pizarra.py
@@ -0,0 +1,65 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+from pilas import colores
+
+class Pizarra(Actor):
+ """Representa una superficie de dibujo inicialmente transparente.
+
+ Puedes pintar sobre esta pizarra usando métodos que simulan
+ un lapiz, que se puede mover sobre una superficie.
+ """
+
+ def __init__(self, x=0, y=0, ancho=None, alto=None):
+ # Si no define area de la pizarra toma el tamano de la ventana.
+ if not ancho or not alto:
+ ancho, alto = pilas.mundo.motor.obtener_area()
+
+ Actor.__init__(self, x=x, y=y)
+ self.imagen = pilas.imagenes.cargar_superficie(ancho, alto)
+
+ def dibujar_punto(self, x, y, color=colores.negro):
+ x, y = self.obtener_coordenada_fisica(x, y)
+ self.imagen.dibujar_punto(x, y, color=color)
+
+ def obtener_coordenada_fisica(self, x, y):
+ x = (self.imagen.ancho()/2) + x
+ y = (self.imagen.alto()/2) - y
+ return x, y
+
+ def pintar_imagen(self, imagen, x, y):
+ self.pintar_parte_de_imagen(imagen, 0, 0, imagen.ancho(), imagen.alto(), x, y)
+
+ def pintar_parte_de_imagen(self, imagen, origen_x, origen_y, ancho, alto, x, y):
+ x, y = self.obtener_coordenada_fisica(x, y)
+ self.imagen.pintar_parte_de_imagen(imagen, origen_x, origen_y, ancho, alto, x, y)
+
+ def pintar_grilla(self, grilla, x, y):
+ grilla.dibujarse_sobre_una_pizarra(self, x, y)
+
+ def pintar(self, color):
+ self.imagen.pintar(color)
+
+ def linea(self, x, y, x2, y2, color=colores.negro, grosor=1):
+ x, y = self.obtener_coordenada_fisica(x, y)
+ x2, y2 = self.obtener_coordenada_fisica(x2, y2)
+ self.imagen.linea(x, y, x2, y2, color, grosor)
+
+ def rectangulo(self, x, y, ancho, alto, color=colores.negro, relleno=False, grosor=1):
+ x, y = self.obtener_coordenada_fisica(x, y)
+ self.imagen.rectangulo(x, y, ancho, alto, color, relleno, grosor)
+
+ def texto(self, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
+ x, y = self.obtener_coordenada_fisica(x, y)
+ self.imagen.texto(cadena, x, y, magnitud, fuente, color)
+
+ def poligono(self, puntos, color=pilas.colores.negro, grosor=1):
+ puntos = [self.obtener_coordenada_fisica(*p) for p in puntos]
+ self.imagen.poligono(puntos, color, grosor)
diff --git a/pilas/actores/puntaje.py b/pilas/actores/puntaje.py
new file mode 100644
index 0000000..6be73e6
--- /dev/null
+++ b/pilas/actores/puntaje.py
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+# For Pilas engine - A video game framework.
+#
+# Copyright 2010 - Pablo Garrido
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+#
+
+
+import pilas
+from pilas.actores import Texto
+
+class Puntaje(Texto):
+ """Representa un contador de Puntaje"""
+
+ def __init__(self, texto='0', x=0, y=0, color=pilas.colores.negro):
+ Texto.__init__(self, texto, x=x, y=y)
+ self.color = color
+
+ def definir(self, puntaje_variable = '0'):
+ self.puntaje_texto = str(puntaje_variable)
+ self.texto = self.puntaje_texto
+
+ def aumentar(self, cantidad=1):
+ self.definir(int(self.texto) + int(cantidad))
+
+ def obtener(self):
+ return int(self.texto)
+
diff --git a/pilas/actores/temporizador.py b/pilas/actores/temporizador.py
new file mode 100644
index 0000000..88606f0
--- /dev/null
+++ b/pilas/actores/temporizador.py
@@ -0,0 +1,66 @@
+# -*- encoding: utf-8 -*-
+# For Pilas engine - A video game framework.
+#
+# Copyright 2010 - Pablo Garrido
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+#
+
+import pilas
+from pilas.actores import Texto
+from pilas import colores
+
+class Temporizador(Texto):
+ """Representa un contador de tiempo con cuenta regresiva.
+
+ Por ejemplo:
+
+ >>> t = pilas.actores.Temporizador()
+ >>> def hola_mundo():
+ ... pilas.avisar("Hola mundo, pasaron 10 segundos...")
+ ...
+ >>> t.ajustar(10, hola_mundo)
+ >>> t.iniciar()
+
+ """
+ def __init__(self, x=0, y=0, color=colores.negro):
+ Texto.__init__(self, '0', x=x, y=y)
+ self.ajustar(1, self.funcion_vacia)
+ self.color = color
+
+ # funcion cuando no se ajusta temporizador
+ def funcion_vacia(self):
+ pass
+
+ def definir_tiempo_texto(self, variable):
+ self.texto = str(variable)
+
+ # con la funcion ajustar manipulamos el tiempo y la
+ # funcion queremos ejecutar
+ def ajustar(self, tiempo=1, funcion=None):
+ """Indica una funcion para ser invocada en el tiempo indicado.
+
+ La función no tiene que recibir parámetros, y luego de
+ ser indicada se tiene que iniciar el temporizador.
+ """
+
+ self.tiempo = tiempo
+ self.definir_tiempo_texto(self.tiempo)
+
+ if funcion == None:
+ self.funcion = self.funcion_vacia()
+ else:
+ self.funcion = funcion
+
+ def _restar_a_contador(self):
+ if self.tiempo != 0:
+ self.tiempo -= 1
+ self.definir_tiempo_texto(self.tiempo)
+ return True
+
+ def iniciar(self):
+ """Inicia el contador de tiempo con la función indicada."""
+ pilas.mundo.agregar_tarea_una_vez(self.tiempo, self.funcion)
+ pilas.mundo.agregar_tarea_siempre(1, self._restar_a_contador)
+
diff --git a/pilas/actores/texto.py b/pilas/actores/texto.py
new file mode 100644
index 0000000..3fbe193
--- /dev/null
+++ b/pilas/actores/texto.py
@@ -0,0 +1,60 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from actor import Actor
+
+class Texto(Actor):
+ """Representa un texto en pantalla.
+
+ El texto tiene atributos como ``texto``, ``magnitud`` y ``color``, por
+ ejemplo para crear un mensaje de saludo podríamos escribir:
+
+ >>> saludo = pilas.actores.Texto("Hola mundo!")
+
+
+ """
+
+ def __init__(self, texto="None", x=0, y=0, magnitud=20):
+ imagen = pilas.mundo.motor.obtener_texto(texto, magnitud)
+ self._definir_area_de_texto(texto, magnitud)
+ Actor.__init__(self, imagen, x=x, y=y)
+ self.magnitud = magnitud
+ self.texto = texto
+ self.color = pilas.colores.blanco
+ self.centro = ("centro", "centro")
+ self.fijo = True
+
+ def obtener_texto(self):
+ return self.imagen.texto
+
+ def definir_texto(self, texto):
+ self.imagen.texto = texto
+ self._definir_area_de_texto(texto, self.magnitud)
+
+ texto = property(obtener_texto, definir_texto, doc="El texto que se tiene que mostrar.")
+
+ def obtener_magnitud(self):
+ return self.imagen.magnitud
+
+ def definir_magnitud(self, magnitud):
+ self._magnitud = magnitud
+ self.imagen.magnitud = magnitud
+
+ magnitud = property(obtener_magnitud, definir_magnitud, doc="El tamaño del texto.")
+
+ def obtener_color(self):
+ return self.imagen.color
+
+ def definir_color(self, color):
+ self.imagen.color = color
+
+ color = property(obtener_color, definir_color, doc="Color del texto.")
+
+ def _definir_area_de_texto(self, texto, magnitud):
+ self._ancho, self._alto = pilas.mundo.motor.obtener_area_de_texto(texto, magnitud)
diff --git a/pilas/actores/tortuga.py b/pilas/actores/tortuga.py
new file mode 100644
index 0000000..e6cf55c
--- /dev/null
+++ b/pilas/actores/tortuga.py
@@ -0,0 +1,99 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+
+class Tortuga(Actor):
+ "Representa una tortuga que se mueve por la pantalla como la tortuga de Logo."
+
+ def __init__(self, x=0, y=0, dibuja=True):
+ self.pizarra = pilas.actores.Pizarra()
+
+ imagen = pilas.imagenes.cargar('tortuga.png')
+ Actor.__init__(self, imagen, x=x, y=y)
+
+ self.rotacion = 0
+ self.velocidad = 6
+
+ self.anterior_x = x
+ self.anterior_y = y
+
+ if dibuja:
+ self.bajalapiz()
+ else:
+ self.subelapiz()
+
+ self.color = pilas.colores.negro
+
+ def avanzar(self, pasos):
+ self.hacer_luego(pilas.comportamientos.Avanzar(pasos, self.velocidad))
+
+ def giraderecha(self, delta):
+ self.hacer_luego(pilas.comportamientos.Girar(abs(delta), self.velocidad))
+
+ def giraizquierda(self, delta):
+ self.hacer_luego(pilas.comportamientos.Girar(-abs(delta), self.velocidad))
+
+ def actualizar(self):
+ if self.anterior_x != self.x or self.anterior_y != self.y:
+ self.dibujar_linea_desde_el_punto_anterior()
+ self.anterior_x = self.x
+ self.anterior_y = self.y
+
+ def dibujar_linea_desde_el_punto_anterior(self):
+ self.pizarra.linea(self.anterior_x, self.anterior_y, self.x, self.y, self.color, grosor=4)
+
+ def bajalapiz(self):
+ self.lapiz_bajo = True
+
+ def subelapiz(self):
+ self.lapiz_bajo = False
+
+ def pon_color(self, color):
+ self.color = color
+
+ def crear_poligono(self, lados = 4, escala = 100, sentido = -1):
+ "dibuja un poligono de n lados"
+ for i in range(lados):
+ rotacion = 360 / lados
+ self.avanzar(escala)
+ if sentido == 1:
+ self.giraderecha(rotacion)
+ else:
+ self.giraizquierda(rotacion)
+
+ def crear_circulo(self, radio = 30, sentido = -1):
+ "dibuja un circulo"
+ for i in range(36):
+ self.avanzar(radio)
+ if sentido == 1:
+ self.giraderecha(10)
+ else:
+ self.giraizquierda(10)
+
+ # Alias de metodos
+ av = avanzar
+ gd = giraderecha
+ gi = giraizquierda
+ bl = bajalapiz
+ sl = subelapiz
+ pc = pon_color
+
+
+ def get_color(self):
+ return self._color
+
+ def set_color(self, color):
+ self._color = color
+
+ color = property(get_color, set_color)
+
+ def pintar(self, color=None):
+ self.pizarra.pintar(color)
diff --git a/pilas/actores/utils.py b/pilas/actores/utils.py
new file mode 100644
index 0000000..da064dc
--- /dev/null
+++ b/pilas/actores/utils.py
@@ -0,0 +1,70 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import random
+import pilas
+
+def ordenar_actores_por_valor_z():
+ "Ordena todos los actores para que se impriman con 'z' como criterio de orden."
+ pilas.actores.todos.sort()
+
+def insertar_como_nuevo_actor(actor):
+ "Coloca a un actor en la lista de actores a imprimir en pantalla."
+ pilas.actores.todos.append(actor)
+
+def eliminar_un_actor(actor):
+ try:
+ pilas.actores.todos.remove(actor)
+ except ValueError:
+ #TODO: quitar este silenciador de excepcion.
+ pass
+
+def eliminar_a_todos():
+ a_eliminar = list(pilas.actores.todos)
+ a_eliminar = a_eliminar[1:] # evita borrar el fondo.
+
+ for x in a_eliminar:
+ x.eliminar()
+
+def destruir_a_todos():
+ "Elimina a los actores inmediatamente (evita que exploten o hagan algo)."
+ a_eliminar = list(pilas.actores.todos)
+
+ for x in a_eliminar:
+ x.destruir()
+
+def obtener_actor_en(x, y):
+ "Intenta obtener el actor mas cerca de la pantalla (z mas pequeño) en la posición (x, y)"
+
+ # Busca el objeto que colisiones ordenando en sentido inverso.
+ for sprite in pilas.actores.todos[::-1]:
+ if sprite.colisiona_con_un_punto(x, y):
+ return sprite
+
+ return None
+
+
+def fabricar(clase, cantidad=1, posiciones_al_azar=True, *k, **kv):
+ "Genera muchas intancias de objetos asignando posiciones aleatorias."
+
+ objetos_creados = []
+
+ for x in range(cantidad):
+ if posiciones_al_azar:
+ x = random.randint(-300, 300)
+ y = random.randint(-200, 200)
+ else:
+ x = 0
+ y = 0
+
+ kv['x'] = x
+ kv['y'] = y
+ nuevo = clase(*k, **kv)
+ objetos_creados.append(nuevo)
+
+ return pilas.grupo.Grupo(objetos_creados)
diff --git a/pilas/aplicacion.py b/pilas/aplicacion.py
new file mode 100644
index 0000000..7e0a9a0
--- /dev/null
+++ b/pilas/aplicacion.py
@@ -0,0 +1,63 @@
+import sys
+
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+import pilas
+from pilas.console import console_widget
+
+
+class Window(QtGui.QWidget):
+
+ def __init__(self, parent=None, pilas_width=320, pilas_height=240):
+ QtGui.QWidget.__init__(self, parent)
+
+ vbox = QtGui.QVBoxLayout(self)
+ pilas.iniciar(usar_motor='qt')
+ ventana_pilas = pilas.mundo.motor
+
+ ventana_pilas.setMinimumWidth(pilas_width)
+ ventana_pilas.setMinimumHeight(pilas_height)
+
+ horizontalLayout = QtGui.QHBoxLayout()
+ spacer1 = QtGui.QSpacerItem(58, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
+ horizontalLayout.addItem(spacer1)
+
+ horizontalLayout.addWidget(ventana_pilas)
+
+ spacer2 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
+ horizontalLayout.addItem(spacer2)
+
+ vbox.addLayout(horizontalLayout)
+
+ #vbox.addWidget(self.horizontalLayout)
+ #hbox.addWidget(ventana_pilas)
+
+ #Crear actor
+ self.mono = pilas.actores.Mono()
+ pilas.eventos.click_de_mouse.conectar(self.sonreir)
+
+ # Agrega la Consola
+ locals = {'pilas': pilas, 'mono': self.mono}
+ self.consoleWidget = console_widget.ConsoleWidget(locals)
+
+ vbox.addWidget(self.consoleWidget)
+
+ #self.ui.ventana = pilas.obtener_widget()
+ # Agrega un nuevo widget al layout existente.
+ #label = QtGui.QLabel(self.ui.centralwidget)
+ #self.ui.layout.addWidget(label)
+ #label.setText("Hola")
+
+ def sonreir(self, evento):
+ self.mono.sonreir()
+
+
+def main():
+ app = QtGui.QApplication(sys.argv)
+ ventana = Window()
+ ventana.show()
+ app.exec_()
+
+if __name__ == '__main__':
+ main()
diff --git a/pilas/atajos.py b/pilas/atajos.py
new file mode 100644
index 0000000..4e00b6d
--- /dev/null
+++ b/pilas/atajos.py
@@ -0,0 +1,17 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+fabricar = pilas.actores.utils.fabricar
+
+def crear_grupo(*k):
+ return pilas.grupos.Grupo(k)
+
+def definir_gravedad(x=0, y=-900):
+ pilas.mundo.fisica.definir_gravedad(x, y)
diff --git a/pilas/camara.py b/pilas/camara.py
new file mode 100644
index 0000000..c013c45
--- /dev/null
+++ b/pilas/camara.py
@@ -0,0 +1,39 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class Camara(object):
+ """Representa el punto de vista de la ventana.
+
+ Los atributos ``x`` e ``y`` indican cual debe ser el
+ punto central de la pantalla. Por defecto estos
+ valores con (0, 0)."""
+
+ def __init__(self, motor):
+ self.motor = motor
+
+ @pilas.utils.interpolable
+ def _set_x(self, x):
+ pilas.mundo.motor.definir_centro_de_la_camara(x, self.y)
+
+ def _get_x(self):
+ x, y = pilas.mundo.motor.obtener_centro_de_la_camara()
+ return x
+
+ @pilas.utils.interpolable
+ def _set_y(self, y):
+ pilas.mundo.motor.definir_centro_de_la_camara(self.x, y)
+
+ def _get_y(self):
+ x, y = pilas.mundo.motor.obtener_centro_de_la_camara()
+ return y
+
+ x = property(_get_x, _set_x)
+ y = property(_get_y, _set_y)
+
diff --git a/pilas/colisiones.py b/pilas/colisiones.py
new file mode 100644
index 0000000..558c396
--- /dev/null
+++ b/pilas/colisiones.py
@@ -0,0 +1,60 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import utils
+import pilas
+
+class Colisiones:
+ "Administra todas las colisiones entre actores."
+
+ def __init__(self):
+ self.colisiones = []
+
+ def verificar_colisiones(self):
+ for x in self.colisiones:
+ self._verificar_colisiones_en_tupla(x)
+
+ def _verificar_colisiones_en_tupla(self, tupla):
+ "Toma dos grupos de actores y analiza colisiones entre ellos."
+ (grupo_a, grupo_b, funcion_a_llamar) = tupla
+
+ for a in grupo_a:
+ for b in grupo_b:
+ if id(a) != id(b) and utils.colisionan(a, b):
+ funcion_a_llamar(a, b)
+
+ # verifica si alguno de los dos objetos muere en la colision.
+ if a not in pilas.actores.todos:
+ if a in grupo_a:
+ list.remove(grupo_a, a)
+
+ if b not in pilas.actores.todos:
+ if b in grupo_b:
+ list.remove(grupo_b, b)
+
+ def agregar(self, grupo_a, grupo_b, funcion_a_llamar):
+ "Agrega dos listas de actores para analizar colisiones."
+
+ if not isinstance(grupo_a, list):
+ grupo_a = [grupo_a]
+
+ if not isinstance(grupo_b, list):
+ grupo_b = [grupo_b]
+
+ self.colisiones.append((grupo_a, grupo_b, funcion_a_llamar))
+
+ def obtener_colisiones(self, actor, grupo_de_actores):
+ "Retorna una lista de los actores que colisionan con uno en particular."
+
+ lista_de_colisiones = []
+
+ for a in grupo_de_actores:
+ if id(actor) != id(a) and utils.colisionan(actor, a):
+ lista_de_colisiones.append(a)
+
+ return lista_de_colisiones
diff --git a/pilas/colores.py b/pilas/colores.py
new file mode 100644
index 0000000..c8ec4ce
--- /dev/null
+++ b/pilas/colores.py
@@ -0,0 +1,48 @@
+import pilas
+
+class Color(object):
+ "Representa un color en base a 4 componentes."
+
+ def __init__(self, r, g, b, a=255):
+ self.r = r
+ self.g = g
+ self.b = b
+ self.a = a
+
+ def obtener(self):
+ return pilas.motor.Color(self.r, self.g, self.b, self.a)
+
+ def __str__(self):
+ return "<Color (%d, %d, %d, %d)>" %(self.r, self.g, self.b, self.a)
+
+ def obtener_componentes(self):
+ return (self.r, self.g, self.b, self.a)
+
+# Colores principales.
+negro = Color(0, 0, 0)
+blanco = Color(255, 255, 255)
+rojo = Color(255, 0, 0)
+verde = Color(0, 255, 0)
+azul = Color(0, 0, 255)
+gris = Color(128, 128, 128)
+
+# Colores secundarios
+amarillo = Color(255, 255, 0)
+magenta = Color(255, 0, 255)
+cyan = Color(0, 255, 255)
+grisclaro = Color(192, 192, 192)
+grisoscuro = Color(100, 100, 100)
+verdeoscuro = Color(0, 128, 0)
+azuloscuro = Color(0, 0, 128)
+naranja = Color(255, 200, 0)
+rosa = Color(255, 175, 175)
+violeta = Color(128, 0, 255)
+marron = Color(153, 102, 0)
+
+# Colores transparentes
+negro = Color(0, 0, 0, 160)
+blanco = Color(255, 255, 255, 160)
+rojo_transparente = Color(255, 0, 0, 160)
+verde_transparente = Color(0, 255, 0, 160)
+azul_transparente = Color(0, 0, 255, 160)
+gris_transparente = Color(128, 128, 128, 160)
diff --git a/pilas/comportamientos.py b/pilas/comportamientos.py
new file mode 100644
index 0000000..7fb486e
--- /dev/null
+++ b/pilas/comportamientos.py
@@ -0,0 +1,107 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import math
+import pilas
+
+class Comportamiento(object):
+ "Representa un comportamiento (estrategia) que se puede anexar a un actor."
+
+ def iniciar(self, receptor):
+ "Se invoca cuando se anexa el comportamiento a un actor."
+ self.receptor = receptor
+
+ def actualizar(self):
+ """Actualiza el comportamiento en un instante dado.
+
+ Si este metodo retorna True entonces el actor dejará
+ de ejecutar este comportamiento."""
+ pass
+
+ def terminar(self):
+ pass
+
+
+class Girar(Comportamiento):
+ "Hace girar constantemente al actor respecto de su eje de forma relativa."
+
+ def __init__(self, delta, velocidad):
+ self.delta = delta
+
+ if delta > 0:
+ self.velocidad = velocidad
+ else:
+ self.velocidad = -velocidad
+
+
+ def iniciar(self, receptor):
+ "Define el angulo inicial."
+ self.receptor = receptor
+ self.angulo_final = (receptor.rotacion + self.delta) % 360
+
+ def actualizar(self):
+ self.receptor.rotacion += self.velocidad
+
+ delta = abs(self.receptor.rotacion - self.angulo_final)
+
+ if delta <= abs(self.velocidad):
+ self.receptor.rotacion = self.angulo_final
+ return True
+
+class Saltar(Comportamiento):
+
+ def __init__(self, velocidad_inicial=10):
+ self.velocidad_inicial = velocidad_inicial
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ self.suelo = int(self.receptor.y)
+ self.velocidad = self.velocidad_inicial
+
+ def actualizar(self):
+ self.receptor.y += self.velocidad
+ self.velocidad -= 0.3
+
+ if self.receptor.y <= self.suelo:
+ self.velocidad_inicial /= 2.0
+ self.velocidad = self.velocidad_inicial
+
+ if self.velocidad_inicial <= 1:
+ # Si toca el suelo
+ self.receptor.y = self.suelo
+ return True
+
+
+class Avanzar(Comportamiento):
+ "Desplaza al actor en la dirección y sentido indicado por una rotación."
+
+ def __init__(self, pasos, velocidad=5):
+ self.pasos = abs(pasos)
+ self.velocidad = velocidad
+
+ def iniciar(self, receptor):
+ self.receptor = receptor
+ rotacion_en_radianes = math.radians(-receptor.rotacion)
+ self.dx = math.cos(rotacion_en_radianes)
+ self.dy = math.sin(rotacion_en_radianes)
+
+ def actualizar(self):
+ salir = False
+
+ if self.pasos - self.velocidad < 0:
+ avance = self.pasos
+ salir = True
+ else:
+ avance = self.velocidad
+
+ self.pasos -= avance
+ self.receptor.x += self.dx * avance
+ self.receptor.y += self.dy * avance
+
+ if salir:
+ return True
diff --git a/pilas/console/__init__.py b/pilas/console/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pilas/console/__init__.py
diff --git a/pilas/console/console.py b/pilas/console/console.py
new file mode 100644
index 0000000..975a9a6
--- /dev/null
+++ b/pilas/console/console.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import sys
+from code import InteractiveConsole
+
+
+class Cache(object):
+ """Replace stdout and stderr behavior in order to collect outputs."""
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ """Clean the cache."""
+ self.out = []
+
+ def write(self, line):
+ """Collect the output into cache to be accesed later."""
+ self.out.append(line)
+
+ def flush(self):
+ """Join together all the outputs and return it to be displayed."""
+ if len(self.out) > 1:
+ output = ''.join(self.out)[:-1]
+ self.reset()
+ return output
+
+
+class Console(InteractiveConsole):
+ """Work as a Python Console."""
+
+ def __init__(self, locals):
+ InteractiveConsole.__init__(self, locals)
+ self.stdout = sys.stdout
+ self.stderr = sys.stderr
+ self._cache = Cache()
+ self.output = ''
+
+ def get_output(self):
+ """Replace out and error channels with cache."""
+ sys.stdout = self._cache
+ sys.stderr = self._cache
+
+ def return_output(self):
+ """Reassign the proper values to output and error channel."""
+ sys.stdout = self.stdout
+ sys.stderr = self.stderr
+
+ def push(self, line):
+ """Insert a command into the console."""
+ if line in ('exit()', 'help()'):
+ return
+ self.get_output()
+ val = InteractiveConsole.push(self, line)
+ self.return_output()
+ self.output = self._cache.flush()
+ return val
diff --git a/pilas/console/console_widget.py b/pilas/console/console_widget.py
new file mode 100644
index 0000000..c96d702
--- /dev/null
+++ b/pilas/console/console_widget.py
@@ -0,0 +1,329 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+
+import re
+
+from PyQt4.QtGui import QPlainTextEdit
+from PyQt4.QtGui import QTextCursor
+from PyQt4.QtGui import QTextFormat
+from PyQt4.QtGui import QTextEdit
+from PyQt4.QtGui import QColor
+from PyQt4.QtCore import Qt
+from PyQt4.QtCore import SIGNAL
+
+from pilas.console import console
+from pilas.console import highlighter
+
+
+INDENT = 4
+
+BRACES = {"'": "'",
+ '"': '"',
+ '{': '}',
+ '[': ']',
+ '(': ')'}
+
+EDITOR_STYLE = """QPlainTextEdit {
+ font-family: monospace;
+ font-size: 10;
+ color: black;
+ background-color: white;
+ selection-color: white;
+ selection-background-color: #437DCD;
+ }"""
+
+
+class ConsoleWidget(QPlainTextEdit):
+
+ def __init__(self, locals):
+ QPlainTextEdit.__init__(self, u'>>> ')
+ self.setUndoRedoEnabled(False)
+ self.setStyleSheet(EDITOR_STYLE)
+ self.setToolTip(self.tr("Show/Hide (F4)"))
+
+ self._patIsWord = re.compile('\w+')
+ self.prompt = u'>>> '
+ self._console = console.Console(locals)
+ self._history = []
+ self._braces = None
+
+ self._highlighter = highlighter.Highlighter(self.document(), 'python',
+ highlighter.COLOR_SCHEME)
+
+ self.connect(self, SIGNAL("cursorPositionChanged()"),
+ self.highlight_current_line)
+ self.highlight_current_line()
+ self.setCursorPosition(0)
+
+ def setCursorPosition(self, position):
+ self.moveCursor(QTextCursor.StartOfLine)
+ for i in xrange(len(self.prompt) + position):
+ self.moveCursor(QTextCursor.Right)
+
+ def keyPressEvent(self, event):
+ if event.key() in (Qt.Key_Enter, Qt.Key_Return):
+ self._write_command()
+ return
+ if self._get_cursor_position() < 0:
+ self.setCursorPosition(0)
+ if event.key() == Qt.Key_Tab:
+ self.textCursor().insertText(' ' * INDENT)
+ return
+ if event.key() == Qt.Key_Home:
+ self.setCursorPosition(0)
+ return
+ if event.key() == Qt.Key_PageUp:
+ return
+ elif event.key() in (Qt.Key_Left, Qt.Key_Backspace):
+ if self._get_cursor_position() == 0:
+ return
+ elif event.key() == Qt.Key_Up:
+ self._set_command(self._get_prev_history_entry())
+ return
+ elif event.key() == Qt.Key_Down:
+ self._set_command(self._get_next_history_entry())
+ return
+
+ if event.key() == Qt.Key_Tab:
+ if self.textCursor().hasSelection():
+ self.indent_more()
+ return
+ else:
+ self.textCursor().insertText(' ' * INDENT)
+ return
+ elif event.key() == Qt.Key_Backspace:
+ if self.textCursor().hasSelection():
+ QPlainTextEdit.keyPressEvent(self, event)
+ return
+ for i in xrange(INDENT):
+ self.moveCursor(QTextCursor.Left, QTextCursor.KeepAnchor)
+ text = self.textCursor().selection()
+ if unicode(text.toPlainText()) == ' ' * INDENT:
+ self.textCursor().removeSelectedText()
+ return
+ else:
+ for i in xrange(text.toPlainText().size()):
+ self.moveCursor(QTextCursor.Right)
+ elif event.key() == Qt.Key_Home:
+ if event.modifiers() == Qt.ShiftModifier:
+ move = QTextCursor.KeepAnchor
+ else:
+ move = QTextCursor.MoveAnchor
+ if self.textCursor().atBlockStart():
+ self.moveCursor(QTextCursor.WordRight, move)
+ return
+ elif event.key() in (Qt.Key_Enter, Qt.Key_Return) and \
+ event.modifiers() == Qt.ShiftModifier:
+ return
+ elif unicode(event.text()) in \
+ (set(BRACES.values()) - set(["'", '"'])):
+ self.moveCursor(QTextCursor.Left, QTextCursor.KeepAnchor)
+ brace = unicode(self.textCursor().selection().toPlainText())
+ self.moveCursor(QTextCursor.Right)
+ self.moveCursor(QTextCursor.Right, QTextCursor.KeepAnchor)
+ braceClose = unicode(self.textCursor().selection().toPlainText())
+ self.moveCursor(QTextCursor.Left)
+ if BRACES.get(brace, False) == unicode(event.text()) and \
+ braceClose == unicode(event.text()):
+ self.moveCursor(QTextCursor.Right)
+ return
+ selection = self.textCursor().selectedText()
+
+ QPlainTextEdit.keyPressEvent(self, event)
+
+ if unicode(event.text()) in BRACES:
+ cursor = self.textCursor()
+ cursor.movePosition(QTextCursor.StartOfLine,
+ QTextCursor.KeepAnchor)
+ self.textCursor().insertText(
+ BRACES[unicode(event.text())])
+ self.moveCursor(QTextCursor.Left)
+ self.textCursor().insertText(selection)
+
+ def highlight_current_line(self):
+ self.emit(SIGNAL("cursorPositionChange(int, int)"),
+ self.textCursor().blockNumber() + 1,
+ self.textCursor().columnNumber())
+ self.extraSelections = []
+
+ selection = QTextEdit.ExtraSelection()
+ lineColor = QColor(highlighter.COLOR_SCHEME.get('current-line',
+ highlighter.COLOR_SCHEME['current-line']))
+ lineColor.setAlpha(20)
+ selection.format.setBackground(lineColor)
+ selection.format.setProperty(QTextFormat.FullWidthSelection,
+ True)
+ selection.cursor = self.textCursor()
+ selection.cursor.clearSelection()
+ self.extraSelections.append(selection)
+ self.setExtraSelections(self.extraSelections)
+
+ #Brace matching
+ if self._braces is not None:
+ self._braces = None
+ cursor = self.textCursor()
+ if cursor.position() == 0:
+ self.setExtraSelections(self.extraSelections)
+ return
+ cursor.movePosition(QTextCursor.PreviousCharacter,
+ QTextCursor.KeepAnchor)
+ text = unicode(cursor.selectedText())
+ pos1 = cursor.position()
+ if text in (')', ']', '}'):
+ pos2 = self._match_braces(pos1, text, forward=False)
+ elif text in ('(', '[', '{'):
+ pos2 = self._match_braces(pos1, text, forward=True)
+ else:
+ self.setExtraSelections(self.extraSelections)
+ return
+ if pos2 is not None:
+ self._braces = (pos1, pos2)
+ selection = QTextEdit.ExtraSelection()
+ selection.format.setForeground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-foreground',
+ highlighter.COLOR_SCHEME.get('brace-foreground'))))
+ selection.format.setBackground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-background',
+ highlighter.COLOR_SCHEME.get('brace-background'))))
+ selection.cursor = cursor
+ self.extraSelections.append(selection)
+ selection = QTextEdit.ExtraSelection()
+ selection.format.setForeground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-foreground',
+ highlighter.COLOR_SCHEME.get('brace-foreground'))))
+ selection.format.setBackground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-background',
+ highlighter.COLOR_SCHEME.get('brace-background'))))
+ selection.cursor = self.textCursor()
+ selection.cursor.setPosition(pos2)
+ selection.cursor.movePosition(QTextCursor.NextCharacter,
+ QTextCursor.KeepAnchor)
+ self.extraSelections.append(selection)
+ else:
+ self._braces = (pos1,)
+ selection = QTextEdit.ExtraSelection()
+ selection.format.setBackground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-background',
+ highlighter.COLOR_SCHEME.get('brace-background'))))
+ selection.format.setForeground(QColor(
+ highlighter.COLOR_SCHEME.get('brace-foreground',
+ highlighter.COLOR_SCHEME.get('brace-foreground'))))
+ selection.cursor = cursor
+ self.extraSelections.append(selection)
+ self.setExtraSelections(self.extraSelections)
+
+ def _text_under_cursor(self):
+ tc = self.textCursor()
+ tc.select(QTextCursor.WordUnderCursor)
+ return tc.selectedText()
+
+ def get_selection(self, posStart, posEnd):
+ cursor = self.textCursor()
+ cursor.setPosition(posStart)
+ cursor2 = self.textCursor()
+ if posEnd == QTextCursor.End:
+ cursor2.movePosition(posEnd)
+ cursor.setPosition(cursor2.position(), QTextCursor.KeepAnchor)
+ else:
+ cursor.setPosition(posEnd, QTextCursor.KeepAnchor)
+ text = cursor.selectedText()
+ return unicode(text)
+
+ def _match_braces(self, position, brace, forward):
+ """based on: http://gitorious.org/khteditor"""
+ if forward:
+ braceMatch = {'(': ')', '[': ']', '{': '}'}
+ text = self.get_selection(position, QTextCursor.End)
+ braceOpen, braceClose = 1, 1
+ else:
+ braceMatch = {')': '(', ']': '[', '}': '{'}
+ text = self.get_selection(QTextCursor.Start, position)
+ braceOpen, braceClose = len(text) - 1, len(text) - 1
+ while True:
+ if forward:
+ posClose = text.find(braceMatch[brace], braceClose)
+ else:
+ posClose = text.rfind(braceMatch[brace], 0, braceClose + 1)
+ if posClose > -1:
+ if forward:
+ braceClose = posClose + 1
+ posOpen = text.find(brace, braceOpen, posClose)
+ else:
+ braceClose = posClose - 1
+ posOpen = text.rfind(brace, posClose, braceOpen + 1)
+ if posOpen > -1:
+ if forward:
+ braceOpen = posOpen + 1
+ else:
+ braceOpen = posOpen - 1
+ else:
+ if forward:
+ return position + posClose
+ else:
+ return position - (len(text) - posClose)
+ else:
+ return
+
+ def _add_prompt(self, incomplete):
+ if incomplete:
+ prompt = '.' * 3 + ' '
+ else:
+ prompt = self.prompt
+ self.appendPlainText(prompt)
+ self.moveCursor(QTextCursor.End)
+
+ def _get_cursor_position(self):
+ return self.textCursor().columnNumber() - len(self.prompt)
+
+ def _write_command(self):
+ command = self.document().findBlockByLineNumber(
+ self.document().lineCount() - 1).text()
+ #remove the prompt from the QString
+ command = command.remove(0, len(self.prompt)).toUtf8().data()
+ self._add_history(command.decode('utf8'))
+ incomplete = self._write(command.decode('utf8'))
+ if not incomplete:
+ output = self._read()
+ if output is not None:
+ if output.__class__.__name__ == 'unicode':
+ output = output.encode('utf8')
+ self.appendPlainText(output.decode('utf8'))
+ self._add_prompt(incomplete)
+
+ def _set_command(self, command):
+ self.moveCursor(QTextCursor.End)
+ self.moveCursor(QTextCursor.StartOfLine, QTextCursor.KeepAnchor)
+ for i in xrange(len(self.prompt)):
+ self.moveCursor(QTextCursor.Right, QTextCursor.KeepAnchor)
+ self.textCursor().removeSelectedText()
+ self.textCursor().insertText(command)
+ self.moveCursor(QTextCursor.End)
+
+ def mousePressEvent(self, event):
+ #to avoid selection
+ event.ignore()
+
+ def _write(self, line):
+ return self._console.push(line)
+
+ def _read(self):
+ return self._console.output
+
+ def _add_history(self, command):
+ if command and (not self._history or self._history[-1] != command):
+ self._history.append(command)
+ self.history_index = len(self._history)
+
+ def _get_prev_history_entry(self):
+ if self._history:
+ self.history_index = max(0, self.history_index - 1)
+ return self._history[self.history_index]
+ return ''
+
+ def _get_next_history_entry(self):
+ if self._history:
+ hist_len = len(self._history)
+ self.history_index = min(hist_len, self.history_index + 1)
+ if self.history_index < hist_len:
+ return self._history[self.history_index]
+ return ''
diff --git a/pilas/console/highlighter.py b/pilas/console/highlighter.py
new file mode 100644
index 0000000..a17ab20
--- /dev/null
+++ b/pilas/console/highlighter.py
@@ -0,0 +1,299 @@
+#-*-coding:utf-8-*-
+from __future__ import absolute_import
+# based on Python Syntax highlighting from:
+# http://diotavelli.net/PyQtWiki/Python%20syntax%20highlighting
+
+import os
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+from PyQt4.QtGui import QColor
+from PyQt4.QtGui import QTextCharFormat
+from PyQt4.QtGui import QFont
+from PyQt4.QtGui import QSyntaxHighlighter
+from PyQt4.QtCore import QRegExp
+
+
+COLOR_SCHEME = {
+ "keyword": "darkMagenta",
+ "operator": "darkRed",
+ "brace": "#858585",
+ "definition": "black",
+ "string": "green",
+ "string2": "darkGreen",
+ "comment": "gray",
+ "properObject": "darkBlue",
+ "numbers": "brown",
+ "spaces": "#BFBFBF",
+ "extras": "orange",
+ "editor-background": "white",
+ "editor-selection-color": "white",
+ "editor-selection-background": "#437DCD",
+ "editor-text": "black",
+ "current-line": "darkCyan",
+ "selected-word": "yellow",
+ "brace-background": "#5BC85B",
+ "brace-foreground": "red"}
+FONT_FAMILY = 'Monospace'
+FONT_SIZE = 11
+SYNTAX = {}
+
+
+def load_syntax():
+ syntax_file = os.path.join(os.path.dirname(__file__), 'lang',
+ 'python.json')
+ structure = None
+ read = open(syntax_file, 'r')
+ structure = json.load(read)
+ read.close()
+ SYNTAX['python'] = structure
+
+
+def format(color, style=''):
+ """Return a QTextCharFormat with the given attributes."""
+ _color = QColor()
+ _color.setNamedColor(color)
+
+ _format = QTextCharFormat()
+ _format.setFontFamily(FONT_FAMILY)
+ _format.setForeground(_color)
+ if 'bold' in style:
+ _format.setFontWeight(QFont.Bold)
+ if 'italic' in style:
+ _format.setFontItalic(True)
+
+ return _format
+
+
+# Syntax styles that can be shared by all languages
+STYLES = {
+ 'keyword': format(COLOR_SCHEME['keyword'], 'bold'),
+ 'operator': format(COLOR_SCHEME['operator']),
+ 'brace': format(COLOR_SCHEME['brace']),
+ 'definition': format(COLOR_SCHEME['definition'], 'bold'),
+ 'string': format(COLOR_SCHEME['string']),
+ 'string2': format(COLOR_SCHEME['string2']),
+ 'comment': format(COLOR_SCHEME['comment'], 'italic'),
+ 'properObject': format(COLOR_SCHEME['properObject'], 'italic'),
+ 'numbers': format(COLOR_SCHEME['numbers']),
+ 'spaces': format(COLOR_SCHEME['spaces']),
+ 'extras': format(COLOR_SCHEME['extras'])}
+
+
+def restyle(scheme):
+ STYLES['keyword'] = format(scheme.get('keyword',
+ COLOR_SCHEME['keyword']), 'bold')
+ STYLES['operator'] = format(scheme.get('operator',
+ COLOR_SCHEME['operator']))
+ STYLES['brace'] = format(scheme.get('brace',
+ COLOR_SCHEME['brace']))
+ STYLES['definition'] = format(scheme.get('definition',
+ COLOR_SCHEME['definition']), 'bold')
+ STYLES['string'] = format(scheme.get('string',
+ COLOR_SCHEME['string']))
+ STYLES['string2'] = format(scheme.get('string2',
+ COLOR_SCHEME['string2']))
+ STYLES['comment'] = format(scheme.get('comment',
+ COLOR_SCHEME['comment']), 'italic')
+ STYLES['properObject'] = format(scheme.get('properObject',
+ COLOR_SCHEME['properObject']), 'italic')
+ STYLES['numbers'] = format(scheme.get('numbers',
+ COLOR_SCHEME['numbers']))
+ STYLES['spaces'] = format(scheme.get('spaces',
+ COLOR_SCHEME['spaces']))
+ STYLES['extras'] = format(scheme.get('extras',
+ COLOR_SCHEME['extras']))
+
+
+class Highlighter (QSyntaxHighlighter):
+
+ # braces
+ braces = ['\\(', '\\)', '\\{', '\\}', '\\[', '\\]']
+
+ def __init__(self, document, lang, scheme=None):
+ QSyntaxHighlighter.__init__(self, document)
+ self.apply_highlight(lang, scheme)
+
+ def apply_highlight(self, lang, scheme=None):
+ load_syntax()
+ langSyntax = SYNTAX.get(lang, {})
+ if scheme:
+ restyle(scheme)
+
+ keywords = langSyntax.get('keywords', [])
+ operators = langSyntax.get('operators', [])
+ extras = langSyntax.get('extras', [])
+
+ rules = []
+
+ # Keyword, operator, brace and extras rules
+ rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
+ for w in keywords]
+ rules += [(r'%s' % o, 0, STYLES['operator'])
+ for o in operators]
+ rules += [(r'%s' % b, 0, STYLES['brace'])
+ for b in Highlighter.braces]
+ rules += [(r'\b%s\b' % e, 0, STYLES['extras'])
+ for e in extras]
+
+ # All other rules
+ proper = langSyntax.get('properObject', None)
+ if proper is not None:
+ proper = '\\b' + str(proper[0]) + '\\b'
+ rules += [(proper, 0, STYLES['properObject'])]
+
+ rules.append((r'__\w+__', 0, STYLES['properObject']))
+
+ definition = langSyntax.get('definition', [])
+ for de in definition:
+ expr = '\\b' + de + '\\b\\s*(\\w+)'
+ rules.append((expr, 1, STYLES['definition']))
+
+ # Numeric literals
+ rules += [
+ (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
+ (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
+ STYLES['numbers']),
+ ]
+
+ regex = langSyntax.get('regex', [])
+ for reg in regex:
+ expr = reg[0]
+ color = COLOR_SCHEME['extras']
+ style = ''
+ if len(reg) > 1:
+ color = COLOR_SCHEME[reg[1]]
+ if len(reg) > 2:
+ style = reg[2]
+ rules.append((expr, 0, format(color, style)))
+
+ comments = langSyntax.get('comment', [])
+ for co in comments:
+ expr = co + '[^\\n]*'
+ rules.append((expr, 0, STYLES['comment']))
+
+ stringChar = langSyntax.get('string', [])
+ for sc in stringChar:
+ expr = r'"[^"\\]*(\\.[^"\\]*)*"' if sc == '"' \
+ else r"'[^'\\]*(\\.[^'\\]*)*'"
+ rules.append((expr, 0, STYLES['string']))
+
+ # Multi-line strings (expression, flag, style)
+ # FIXME: The triple-quotes in these two lines will mess up the
+ # syntax highlighting from this point onward
+ self.tri_single = (QRegExp("'''"), 1, STYLES['string2']) # '''
+ self.tri_double = (QRegExp('"""'), 2, STYLES['string2']) # """
+
+ multi = langSyntax.get('multiline_comment', [])
+ if multi:
+ self.multi_start = (QRegExp(multi['open']), STYLES['comment'])
+ self.multi_end = (QRegExp(multi['close']), STYLES['comment'])
+ else:
+ self.multi_start = None
+
+ # Build a QRegExp for each pattern
+ self.rules = [(QRegExp(pat), index, fmt)
+ for (pat, index, fmt) in rules]
+ #Apply Highlight to the document... (when colors change)
+ self.rehighlight()
+
+ def highlightBlock(self, text):
+ """Apply syntax highlighting to the given block of text."""
+ for expression, nth, format in self.rules:
+ index = expression.indexIn(text, 0)
+
+ while index >= 0:
+ # We actually want the index of the nth match
+ index = expression.pos(nth)
+ length = expression.cap(nth).length()
+ self.setFormat(index, length, format)
+ index = expression.indexIn(text, index + length)
+
+ self.setCurrentBlockState(0)
+ if not self.multi_start:
+ # Do multi-line strings
+ in_multiline = self.match_multiline(text, *self.tri_single)
+ if not in_multiline:
+ in_multiline = self.match_multiline(text, *self.tri_double)
+ else:
+ # Do multi-line comment
+ self.comment_multiline(text, self.multi_end[0], *self.multi_start)
+
+ #Spaces
+ expression = QRegExp('\s+')
+ index = expression.indexIn(text, 0)
+ while index >= 0:
+ index = expression.pos(0)
+ length = expression.cap(0).length()
+ self.setFormat(index, length, STYLES['spaces'])
+ index = expression.indexIn(text, index + length)
+
+ def match_multiline(self, text, delimiter, in_state, style):
+ """Do highlighting of multi-line strings. ``delimiter`` should be a
+ ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
+ ``in_state`` should be a unique integer to represent the corresponding
+ state changes when inside those strings. Returns True if we're still
+ inside a multi-line string when this function is finished.
+ """
+ # If inside triple-single quotes, start at 0
+ if self.previousBlockState() == in_state:
+ start = 0
+ add = 0
+ # Otherwise, look for the delimiter on this line
+ else:
+ start = delimiter.indexIn(text)
+ # Move past this match
+ add = delimiter.matchedLength()
+
+ # As long as there's a delimiter match on this line...
+ while start >= 0:
+ # Look for the ending delimiter
+ end = delimiter.indexIn(text, start + add)
+ # Ending delimiter on this line?
+ if end >= add:
+ length = end - start + add + delimiter.matchedLength()
+ self.setCurrentBlockState(0)
+ # No; multi-line string
+ else:
+ self.setCurrentBlockState(in_state)
+ length = text.length() - start + add
+ # Apply formatting
+ self.setFormat(start, length, style)
+ # Look for the next match
+ start = delimiter.indexIn(text, start + length)
+
+ # Return True if still inside a multi-line string, False otherwise
+ if self.currentBlockState() == in_state:
+ return True
+ else:
+ return False
+
+ def comment_multiline(self, text, delimiter_end, delimiter_start, style):
+ startIndex = 0
+ if self.previousBlockState() != 1:
+ startIndex = delimiter_start.indexIn(text)
+ while startIndex >= 0:
+ endIndex = delimiter_end.indexIn(text, startIndex)
+ commentLength = 0
+ if endIndex == -1:
+ self.setCurrentBlockState(1)
+ commentLength = text.length() - startIndex
+ else:
+ commentLength = endIndex - startIndex + \
+ delimiter_end.matchedLength()
+
+ self.setFormat(startIndex, commentLength, style)
+ startIndex = delimiter_start.indexIn(text,
+ startIndex + commentLength)
+
+
+class EmptyHighlighter (QSyntaxHighlighter):
+
+ def __init__(self, document):
+ QSyntaxHighlighter.__init__(self, document)
+
+ def highlightBlock(self, text):
+ pass
diff --git a/pilas/console/lang/python.json b/pilas/console/lang/python.json
new file mode 100644
index 0000000..0c4f1b3
--- /dev/null
+++ b/pilas/console/lang/python.json
@@ -0,0 +1,81 @@
+{
+ "comment": [
+ "#"
+ ],
+ "extension": [
+ "py"
+ ],
+ "definition": [
+ "def",
+ "class"
+ ],
+ "string": [
+ "'",
+ "\""
+ ],
+ "properObject": [
+ "self"
+ ],
+ "operators": [
+ "=",
+ "==",
+ "!=",
+ "<",
+ "<=",
+ ">",
+ ">=",
+ "\\+",
+ "-",
+ "\\*",
+ "/",
+ "//",
+ "\\%",
+ "\\*\\*",
+ "\\+=",
+ "-=",
+ "\\*=",
+ "/=",
+ "\\%=",
+ "\\^",
+ "\\|",
+ "\\&",
+ "\\~",
+ ">>",
+ "<<"
+ ],
+ "keywords": [
+ "and",
+ "assert",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "exec",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "not",
+ "or",
+ "pass",
+ "print",
+ "raise",
+ "return",
+ "super",
+ "try",
+ "while",
+ "yield",
+ "None",
+ "True",
+ "False"
+ ]
+}
diff --git a/pilas/control.py b/pilas/control.py
new file mode 100644
index 0000000..d91fbfd
--- /dev/null
+++ b/pilas/control.py
@@ -0,0 +1,112 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.simbolos import *
+import eventos
+
+__doc__ = """
+Módulo pilas.control
+====================
+
+"""
+
+class Control(object):
+ """Representa un control de teclado sencillo.
+
+ Este objeto permite acceder al estado del teclado usando
+ atributos.
+
+ Por ejemplo, con este objeto, para saber si el usuario
+ está pulsando el direccional hacia la izquierda de
+ puedes ejecutar::
+
+ if pilas.mundo.control.izquierda:
+ print 'Ha pulsado hacia la izquierda'
+
+ Es decir, si bien Control es una clase, no hace falta
+ instanciarla. Ya existe un objeto que se puede consultar
+ bajo el nombre ``pilas.mundo.control``.
+
+ Entonces, una vez que tienes la referencia para consultar, los
+ atributos que tiene este objeto control son::
+
+ izquierda
+ derecha
+ arriba
+ abajo
+ boton
+
+ Cada uno de estos atributos te pueden devolver True, o False, indicando
+ si el control está pulsado o no.
+
+ Ten en cuenta que este objeto también se puede imprimir usando
+ la sentencia ``print``. Esto es útil para ver el estado completo
+ del control de una sola vez:
+
+ >>> print pilas.mundo.control
+ <Control izquierda: False derecha: False arriba: False abajo: False boton: False>
+
+
+ Consultando controles desde un actor:
+
+ Una forma habitual de usar los controles, es consultarlos
+ directamente desde el codigo de un actor.
+
+ Para consultar los controles para cambiar la posicion horizontal de
+ un actor podrías implementar el método ``actualizar``::
+
+ class Patito(pilas.actores.Actor):
+
+ def __init__(self):
+ pilas.actores.Actor.__init__(self)
+ self.imagen = "patito.png"
+
+ def actualizar(self):
+ if pilas.mundo.control.izquierda:
+ self.x -= 5
+ self.espejado = True
+ elif pilas.mundo.control.derecha:
+ self.x += 5
+ self.espejado = False
+
+ .. image:: ../../pilas/data/patito.png
+ """
+
+ def __init__(self):
+ self.izquierda = False
+ self.derecha = False
+ self.arriba = False
+ self.abajo = False
+ self.boton = False
+
+ eventos.pulsa_tecla.conectar(self.cuando_pulsa_una_tecla)
+ eventos.suelta_tecla.conectar(self.cuando_suelta_una_tecla)
+
+ def cuando_pulsa_una_tecla(self, evento):
+ self.procesar_cambio_de_estado_en_la_tecla(evento.codigo, True)
+
+ def cuando_suelta_una_tecla(self, evento):
+ self.procesar_cambio_de_estado_en_la_tecla(evento.codigo, False)
+
+ def procesar_cambio_de_estado_en_la_tecla(self, codigo, estado):
+ mapa = {
+ IZQUIERDA: 'izquierda',
+ DERECHA: 'derecha',
+ ARRIBA: 'arriba',
+ ABAJO: 'abajo',
+ SELECCION: 'boton',
+ }
+
+ if mapa.has_key(codigo):
+ setattr(self, mapa[codigo], estado)
+
+ def __str__(self):
+ return "<Control izquierda: %s derecha: %s arriba: %s abajo: %s boton: %s>" %(
+ str(self.izquierda), str(self.derecha), str(self.arriba),
+ str(self.abajo), str(self.boton))
diff --git a/pilas/data/aceituna.png b/pilas/data/aceituna.png
new file mode 100644
index 0000000..dae99e4
--- /dev/null
+++ b/pilas/data/aceituna.png
Binary files differ
diff --git a/pilas/data/aceituna_burla.png b/pilas/data/aceituna_burla.png
new file mode 100644
index 0000000..3d3a98e
--- /dev/null
+++ b/pilas/data/aceituna_burla.png
Binary files differ
diff --git a/pilas/data/aceituna_grita.png b/pilas/data/aceituna_grita.png
new file mode 100644
index 0000000..34b8905
--- /dev/null
+++ b/pilas/data/aceituna_grita.png
Binary files differ
diff --git a/pilas/data/aceituna_risa.png b/pilas/data/aceituna_risa.png
new file mode 100644
index 0000000..8ae5a88
--- /dev/null
+++ b/pilas/data/aceituna_risa.png
Binary files differ
diff --git a/pilas/data/banana.png b/pilas/data/banana.png
new file mode 100644
index 0000000..b352b03
--- /dev/null
+++ b/pilas/data/banana.png
Binary files differ
diff --git a/pilas/data/batalhao.png b/pilas/data/batalhao.png
new file mode 100644
index 0000000..9734c81
--- /dev/null
+++ b/pilas/data/batalhao.png
Binary files differ
diff --git a/pilas/data/bomba.png b/pilas/data/bomba.png
new file mode 100644
index 0000000..9cf957a
--- /dev/null
+++ b/pilas/data/bomba.png
Binary files differ
diff --git a/pilas/data/boton/boton_normal.png b/pilas/data/boton/boton_normal.png
new file mode 100644
index 0000000..706d180
--- /dev/null
+++ b/pilas/data/boton/boton_normal.png
Binary files differ
diff --git a/pilas/data/boton/boton_over.png b/pilas/data/boton/boton_over.png
new file mode 100644
index 0000000..b5848cd
--- /dev/null
+++ b/pilas/data/boton/boton_over.png
Binary files differ
diff --git a/pilas/data/boton/boton_press.png b/pilas/data/boton/boton_press.png
new file mode 100644
index 0000000..436871d
--- /dev/null
+++ b/pilas/data/boton/boton_press.png
Binary files differ
diff --git a/pilas/data/boton/tema.png b/pilas/data/boton/tema.png
new file mode 100644
index 0000000..8ce9420
--- /dev/null
+++ b/pilas/data/boton/tema.png
Binary files differ
diff --git a/pilas/data/boton/tema.xcf b/pilas/data/boton/tema.xcf
new file mode 100644
index 0000000..3dcc2ba
--- /dev/null
+++ b/pilas/data/boton/tema.xcf
Binary files differ
diff --git a/pilas/data/caja.png b/pilas/data/caja.png
new file mode 100644
index 0000000..3f8a151
--- /dev/null
+++ b/pilas/data/caja.png
Binary files differ
diff --git a/pilas/data/cooperativista/alerta.png b/pilas/data/cooperativista/alerta.png
new file mode 100644
index 0000000..17f103f
--- /dev/null
+++ b/pilas/data/cooperativista/alerta.png
Binary files differ
diff --git a/pilas/data/cooperativista/camina.png b/pilas/data/cooperativista/camina.png
new file mode 100644
index 0000000..dbdb5a5
--- /dev/null
+++ b/pilas/data/cooperativista/camina.png
Binary files differ
diff --git a/pilas/data/cooperativista/camina_sujeta.png b/pilas/data/cooperativista/camina_sujeta.png
new file mode 100644
index 0000000..f4235bb
--- /dev/null
+++ b/pilas/data/cooperativista/camina_sujeta.png
Binary files differ
diff --git a/pilas/data/cooperativista/ok.png b/pilas/data/cooperativista/ok.png
new file mode 100644
index 0000000..7d86a64
--- /dev/null
+++ b/pilas/data/cooperativista/ok.png
Binary files differ
diff --git a/pilas/data/cooperativista/parado.png b/pilas/data/cooperativista/parado.png
new file mode 100644
index 0000000..91f0266
--- /dev/null
+++ b/pilas/data/cooperativista/parado.png
Binary files differ
diff --git a/pilas/data/cooperativista/parado_sujeta.png b/pilas/data/cooperativista/parado_sujeta.png
new file mode 100644
index 0000000..fb6fd68
--- /dev/null
+++ b/pilas/data/cooperativista/parado_sujeta.png
Binary files differ
diff --git a/pilas/data/cooperativista/trabajando.png b/pilas/data/cooperativista/trabajando.png
new file mode 100644
index 0000000..47e59b4
--- /dev/null
+++ b/pilas/data/cooperativista/trabajando.png
Binary files differ
diff --git a/pilas/data/cursordisparo.png b/pilas/data/cursordisparo.png
new file mode 100644
index 0000000..21daaa3
--- /dev/null
+++ b/pilas/data/cursordisparo.png
Binary files differ
diff --git a/pilas/data/cursores/arrastrando.png b/pilas/data/cursores/arrastrando.png
new file mode 100644
index 0000000..3b56e22
--- /dev/null
+++ b/pilas/data/cursores/arrastrando.png
Binary files differ
diff --git a/pilas/data/cursores/dibujar.png b/pilas/data/cursores/dibujar.png
new file mode 100644
index 0000000..bd90f74
--- /dev/null
+++ b/pilas/data/cursores/dibujar.png
Binary files differ
diff --git a/pilas/data/cursores/normal.png b/pilas/data/cursores/normal.png
new file mode 100644
index 0000000..7feffb0
--- /dev/null
+++ b/pilas/data/cursores/normal.png
Binary files differ
diff --git a/pilas/data/cursores/prohibido.png b/pilas/data/cursores/prohibido.png
new file mode 100644
index 0000000..77010c2
--- /dev/null
+++ b/pilas/data/cursores/prohibido.png
Binary files differ
diff --git a/pilas/data/disparo.png b/pilas/data/disparo.png
new file mode 100644
index 0000000..266540d
--- /dev/null
+++ b/pilas/data/disparo.png
Binary files differ
diff --git a/pilas/data/ejes.png b/pilas/data/ejes.png
new file mode 100644
index 0000000..74f14b6
--- /dev/null
+++ b/pilas/data/ejes.png
Binary files differ
diff --git a/pilas/data/ejes.svg b/pilas/data/ejes.svg
new file mode 100644
index 0000000..39b2dda
--- /dev/null
+++ b/pilas/data/ejes.svg
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="New document 1">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;">
+ <path
+ id="path3596"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3593"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.8) translate(12.5,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <inkscape:perspective
+ id="perspective4792"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart-9"
+ style="overflow:visible">
+ <path
+ id="path3593-4"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend-5"
+ style="overflow:visible">
+ <path
+ id="path3596-9"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="261.32635"
+ inkscape:cy="692.10052"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1362"
+ inkscape:window-height="730"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 345.71429,486.6479 0,-277.14286"
+ id="path2816" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 207.91089,348.28586 277.14286,0"
+ id="path2816-3" />
+ <text
+ xml:space="preserve"
+ style="font-size:14.605721;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ x="460.62955"
+ y="325.05234"
+ id="text4818"><tspan
+ sodipodi:role="line"
+ id="tspan4820"></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ x="462.85715"
+ y="367.36218"
+ id="text4822"><tspan
+ sodipodi:role="line"
+ id="tspan4824"
+ x="462.85715"
+ y="367.36218">eje X</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:11px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ x="355"
+ y="224.50504"
+ id="text4826"><tspan
+ sodipodi:role="line"
+ id="tspan4828"
+ x="355"
+ y="224.50504">eje Y</tspan></text>
+ </g>
+</svg>
diff --git a/pilas/data/ejes.xcf b/pilas/data/ejes.xcf
new file mode 100644
index 0000000..8b1d175
--- /dev/null
+++ b/pilas/data/ejes.xcf
Binary files differ
diff --git a/pilas/data/estrella.png b/pilas/data/estrella.png
new file mode 100644
index 0000000..223462a
--- /dev/null
+++ b/pilas/data/estrella.png
Binary files differ
diff --git a/pilas/data/explosion.png b/pilas/data/explosion.png
new file mode 100644
index 0000000..d2e7fa3
--- /dev/null
+++ b/pilas/data/explosion.png
Binary files differ
diff --git a/pilas/data/explosion.wav b/pilas/data/explosion.wav
new file mode 100644
index 0000000..c8acedf
--- /dev/null
+++ b/pilas/data/explosion.wav
Binary files differ
diff --git a/pilas/data/fondos/blanco.png b/pilas/data/fondos/blanco.png
new file mode 100644
index 0000000..281b88a
--- /dev/null
+++ b/pilas/data/fondos/blanco.png
Binary files differ
diff --git a/pilas/data/fondos/espacio.jpg b/pilas/data/fondos/espacio.jpg
new file mode 100644
index 0000000..60981a3
--- /dev/null
+++ b/pilas/data/fondos/espacio.jpg
Binary files differ
diff --git a/pilas/data/fondos/noche.jpg b/pilas/data/fondos/noche.jpg
new file mode 100644
index 0000000..7e93b7d
--- /dev/null
+++ b/pilas/data/fondos/noche.jpg
Binary files differ
diff --git a/pilas/data/fondos/pasto.png b/pilas/data/fondos/pasto.png
new file mode 100644
index 0000000..107054a
--- /dev/null
+++ b/pilas/data/fondos/pasto.png
Binary files differ
diff --git a/pilas/data/fondos/selva.jpg b/pilas/data/fondos/selva.jpg
new file mode 100644
index 0000000..b6eff87
--- /dev/null
+++ b/pilas/data/fondos/selva.jpg
Binary files differ
diff --git a/pilas/data/fondos/tarde.jpg b/pilas/data/fondos/tarde.jpg
new file mode 100644
index 0000000..cc1bc8b
--- /dev/null
+++ b/pilas/data/fondos/tarde.jpg
Binary files differ
diff --git a/pilas/data/fondos/volley.jpg b/pilas/data/fondos/volley.jpg
new file mode 100644
index 0000000..b75b3c6
--- /dev/null
+++ b/pilas/data/fondos/volley.jpg
Binary files differ
diff --git a/pilas/data/globo.png b/pilas/data/globo.png
new file mode 100644
index 0000000..5127438
--- /dev/null
+++ b/pilas/data/globo.png
Binary files differ
diff --git a/pilas/data/globo.xcf b/pilas/data/globo.xcf
new file mode 100644
index 0000000..bb379b4
--- /dev/null
+++ b/pilas/data/globo.xcf
Binary files differ
diff --git a/pilas/data/grillas/plataformas_10_10.png b/pilas/data/grillas/plataformas_10_10.png
new file mode 100644
index 0000000..a796b3a
--- /dev/null
+++ b/pilas/data/grillas/plataformas_10_10.png
Binary files differ
diff --git a/pilas/data/icono_pausa.png b/pilas/data/icono_pausa.png
new file mode 100644
index 0000000..cc3a371
--- /dev/null
+++ b/pilas/data/icono_pausa.png
Binary files differ
diff --git a/pilas/data/iconos/borrar.png b/pilas/data/iconos/borrar.png
new file mode 100644
index 0000000..2f59c5e
--- /dev/null
+++ b/pilas/data/iconos/borrar.png
Binary files differ
diff --git a/pilas/data/iconos/lupa.png b/pilas/data/iconos/lupa.png
new file mode 100644
index 0000000..0a3bf9c
--- /dev/null
+++ b/pilas/data/iconos/lupa.png
Binary files differ
diff --git a/pilas/data/iconos/ok.png b/pilas/data/iconos/ok.png
new file mode 100644
index 0000000..fec1c0a
--- /dev/null
+++ b/pilas/data/iconos/ok.png
Binary files differ
diff --git a/pilas/data/interfaz/barra.png b/pilas/data/interfaz/barra.png
new file mode 100644
index 0000000..cd0bb8f
--- /dev/null
+++ b/pilas/data/interfaz/barra.png
Binary files differ
diff --git a/pilas/data/interfaz/caja.png b/pilas/data/interfaz/caja.png
new file mode 100644
index 0000000..4992a13
--- /dev/null
+++ b/pilas/data/interfaz/caja.png
Binary files differ
diff --git a/pilas/data/interfaz/deslizador.png b/pilas/data/interfaz/deslizador.png
new file mode 100644
index 0000000..e59e3d2
--- /dev/null
+++ b/pilas/data/interfaz/deslizador.png
Binary files differ
diff --git a/pilas/data/interfaz/selector.png b/pilas/data/interfaz/selector.png
new file mode 100644
index 0000000..daeb883
--- /dev/null
+++ b/pilas/data/interfaz/selector.png
Binary files differ
diff --git a/pilas/data/interfaz/selector_seleccionado.png b/pilas/data/interfaz/selector_seleccionado.png
new file mode 100644
index 0000000..5db41ea
--- /dev/null
+++ b/pilas/data/interfaz/selector_seleccionado.png
Binary files differ
diff --git a/pilas/data/invisible.png b/pilas/data/invisible.png
new file mode 100644
index 0000000..79cc8ed
--- /dev/null
+++ b/pilas/data/invisible.png
Binary files differ
diff --git a/pilas/data/juegobase/data/logo.png b/pilas/data/juegobase/data/logo.png
new file mode 100644
index 0000000..45fc1f0
--- /dev/null
+++ b/pilas/data/juegobase/data/logo.png
Binary files differ
diff --git a/pilas/data/juegobase/ejecutar.py b/pilas/data/juegobase/ejecutar.py
new file mode 100644
index 0000000..b889021
--- /dev/null
+++ b/pilas/data/juegobase/ejecutar.py
@@ -0,0 +1,9 @@
+import pilas
+
+pilas.iniciar()
+pilas.fondos.Pasto()
+pilas.avisar('Use Alt+q para salir.')
+
+actor = pilas.actores.Actor("data/logo.png")
+
+pilas.ejecutar()
diff --git a/pilas/data/mapa.png b/pilas/data/mapa.png
new file mode 100644
index 0000000..3fde3f0
--- /dev/null
+++ b/pilas/data/mapa.png
Binary files differ
diff --git a/pilas/data/marcianitos/martian.png b/pilas/data/marcianitos/martian.png
new file mode 100644
index 0000000..b89c14a
--- /dev/null
+++ b/pilas/data/marcianitos/martian.png
Binary files differ
diff --git a/pilas/data/marcianitos/martian.xcf b/pilas/data/marcianitos/martian.xcf
new file mode 100644
index 0000000..3b3faf9
--- /dev/null
+++ b/pilas/data/marcianitos/martian.xcf
Binary files differ
diff --git a/pilas/data/marcianitos/martian_64_128.xcf b/pilas/data/marcianitos/martian_64_128.xcf
new file mode 100644
index 0000000..b9c8f7f
--- /dev/null
+++ b/pilas/data/marcianitos/martian_64_128.xcf
Binary files differ
diff --git a/pilas/data/marcianitos/martian_64_129.xcf b/pilas/data/marcianitos/martian_64_129.xcf
new file mode 100644
index 0000000..b9c8f7f
--- /dev/null
+++ b/pilas/data/marcianitos/martian_64_129.xcf
Binary files differ
diff --git a/pilas/data/moneda.png b/pilas/data/moneda.png
new file mode 100644
index 0000000..a2b8bbc
--- /dev/null
+++ b/pilas/data/moneda.png
Binary files differ
diff --git a/pilas/data/monkey_normal.png b/pilas/data/monkey_normal.png
new file mode 100644
index 0000000..e11f0e9
--- /dev/null
+++ b/pilas/data/monkey_normal.png
Binary files differ
diff --git a/pilas/data/monkey_shout.png b/pilas/data/monkey_shout.png
new file mode 100644
index 0000000..7bbc223
--- /dev/null
+++ b/pilas/data/monkey_shout.png
Binary files differ
diff --git a/pilas/data/monkey_smile.png b/pilas/data/monkey_smile.png
new file mode 100644
index 0000000..63121e0
--- /dev/null
+++ b/pilas/data/monkey_smile.png
Binary files differ
diff --git a/pilas/data/mono.png b/pilas/data/mono.png
new file mode 100644
index 0000000..e11f0e9
--- /dev/null
+++ b/pilas/data/mono.png
Binary files differ
diff --git a/pilas/data/nave.png b/pilas/data/nave.png
new file mode 100644
index 0000000..73fd3ac
--- /dev/null
+++ b/pilas/data/nave.png
Binary files differ
diff --git a/pilas/data/parque.tmx b/pilas/data/parque.tmx
new file mode 100644
index 0000000..c95ca19
--- /dev/null
+++ b/pilas/data/parque.tmx
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="orthogonal" width="20" height="15" tilewidth="32" tileheight="32">
+ <tileset firstgid="1" name="batalhao" tilewidth="32" tileheight="32">
+ <image source="batalhao.png" width="512" height="96"/>
+ </tileset>
+ <layer name="suelo" width="20" height="15">
+ <data encoding="csv">
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,34,1,1,1,1,1,1,1,1,2,1,1,2,
+1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,34,1,1,1,1,1,1,1,2,1,1,34,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,
+1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,34,1,1,1,1,
+1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+</data>
+ </layer>
+ <layer name="obstaculos" width="20" height="15">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,19,3,3,4,20,0,0,0,0,
+0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+3,3,20,0,0,0,0,0,17,0,0,0,0,0,0,33,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+33,0,0,0,0,33,0,0,0,0,0,0,33,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,33,0,0,0,0,0,0,0,0,33,0,0,0,0,0,33,0,0,
+0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,33,0,0,0,0,0,0,0,19,3,4,3,20,0,0,0,33,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+</map>
diff --git a/pilas/data/patito.png b/pilas/data/patito.png
new file mode 100644
index 0000000..6be4566
--- /dev/null
+++ b/pilas/data/patito.png
Binary files differ
diff --git a/pilas/data/patito_desde_arriba.png b/pilas/data/patito_desde_arriba.png
new file mode 100644
index 0000000..3dee5d6
--- /dev/null
+++ b/pilas/data/patito_desde_arriba.png
Binary files differ
diff --git a/pilas/data/pelota.png b/pilas/data/pelota.png
new file mode 100644
index 0000000..6a8afc2
--- /dev/null
+++ b/pilas/data/pelota.png
Binary files differ
diff --git a/pilas/data/piedra_chica.png b/pilas/data/piedra_chica.png
new file mode 100644
index 0000000..82ed70c
--- /dev/null
+++ b/pilas/data/piedra_chica.png
Binary files differ
diff --git a/pilas/data/piedra_grande.png b/pilas/data/piedra_grande.png
new file mode 100644
index 0000000..3395d26
--- /dev/null
+++ b/pilas/data/piedra_grande.png
Binary files differ
diff --git a/pilas/data/piedra_media.png b/pilas/data/piedra_media.png
new file mode 100644
index 0000000..0db6de5
--- /dev/null
+++ b/pilas/data/piedra_media.png
Binary files differ
diff --git a/pilas/data/pilas-logo.png b/pilas/data/pilas-logo.png
new file mode 100644
index 0000000..f9c9377
--- /dev/null
+++ b/pilas/data/pilas-logo.png
Binary files differ
diff --git a/pilas/data/pingu.png b/pilas/data/pingu.png
new file mode 100644
index 0000000..c013604
--- /dev/null
+++ b/pilas/data/pingu.png
Binary files differ
diff --git a/pilas/data/plataformas.png b/pilas/data/plataformas.png
new file mode 100644
index 0000000..a796b3a
--- /dev/null
+++ b/pilas/data/plataformas.png
Binary files differ
diff --git a/pilas/data/prueba.png b/pilas/data/prueba.png
new file mode 100644
index 0000000..bd24c5c
--- /dev/null
+++ b/pilas/data/prueba.png
Binary files differ
diff --git a/pilas/data/punto.png b/pilas/data/punto.png
new file mode 100644
index 0000000..577d76b
--- /dev/null
+++ b/pilas/data/punto.png
Binary files differ
diff --git a/pilas/data/shout.wav b/pilas/data/shout.wav
new file mode 100644
index 0000000..4a465c3
--- /dev/null
+++ b/pilas/data/shout.wav
Binary files differ
diff --git a/pilas/data/sin_imagen.png b/pilas/data/sin_imagen.png
new file mode 100644
index 0000000..cd98643
--- /dev/null
+++ b/pilas/data/sin_imagen.png
Binary files differ
diff --git a/pilas/data/smile.wav b/pilas/data/smile.wav
new file mode 100644
index 0000000..7c14f8c
--- /dev/null
+++ b/pilas/data/smile.wav
Binary files differ
diff --git a/pilas/data/tick.wav b/pilas/data/tick.wav
new file mode 100644
index 0000000..3d4945a
--- /dev/null
+++ b/pilas/data/tick.wav
Binary files differ
diff --git a/pilas/data/tortuga.png b/pilas/data/tortuga.png
new file mode 100644
index 0000000..95b325b
--- /dev/null
+++ b/pilas/data/tortuga.png
Binary files differ
diff --git a/pilas/data/window.ui b/pilas/data/window.ui
new file mode 100644
index 0000000..64df717
--- /dev/null
+++ b/pilas/data/window.ui
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Window</class>
+ <widget class="QWidget" name="Window">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>503</width>
+ <height>477</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGraphicsView" name="graphicsView">
+ <property name="backgroundBrush">
+ <brush brushstyle="NoBrush">
+ <color alpha="255">
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/pilas/depurador.py b/pilas/depurador.py
new file mode 100644
index 0000000..9a662f9
--- /dev/null
+++ b/pilas/depurador.py
@@ -0,0 +1,202 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas import pilasversion
+import sys
+
+class Depurador(object):
+ """Esta clase permite hacer depuraciones visuales.
+
+ La depuracion visual en pilas consiste en poder mostrar informacion
+ que generalmente es invisible a los jugadores. Por ejemplo, donde
+ estan situados los puntos de control, los radios de colision etc.
+
+ Esta clase administra varios modos depuracion, que son los
+ que dibujan figuras geometricas.
+ """
+
+ def __init__(self, lienzo, fps):
+ self.modos = []
+ self.lienzo = lienzo
+ ModoDepurador.grosor_de_lineas = 1
+ self.fps = fps
+ self.posicion_del_mouse = (0, 0)
+ pilas.eventos.mueve_mouse.conectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.pulsa_tecla.conectar(self.cuando_pulsa_tecla)
+
+ def cuando_mueve_el_mouse(self, evento):
+ self.posicion_del_mouse = (evento.x, evento.y)
+ return True
+
+ def comienza_dibujado(self, motor):
+ for m in self.modos:
+ m.comienza_dibujado(motor, self.lienzo)
+
+ def dibuja_al_actor(self, motor, actor):
+ for m in self.modos:
+ m.dibuja_al_actor(motor, self.lienzo, actor)
+
+ def termina_dibujado(self, motor):
+ if self.modos:
+ self._mostrar_cantidad_de_actores(motor)
+ self._mostrar_cuadros_por_segundo(motor)
+ self._mostrar_posicion_del_mouse(motor)
+ self._mostrar_nombres_de_modos(motor)
+
+ for m in self.modos:
+ m.termina_dibujado(motor, self.lienzo)
+
+ def cuando_pulsa_tecla(self, evento):
+ if evento.codigo == 'F7':
+ self._alternar_modo(ModoInformacionDeSistema)
+ elif evento.codigo == 'F8':
+ self._alternar_modo(ModoPuntosDeControl)
+ elif evento.codigo == 'F9':
+ self._alternar_modo(ModoRadiosDeColision)
+ elif evento.codigo == 'F10':
+ self._alternar_modo(ModoArea)
+ elif evento.codigo == 'F11':
+ self._alternar_modo(ModoFisica)
+ elif evento.codigo == 'F12':
+ self._alternar_modo(ModoPosicion)
+ elif evento.texto == '+':
+ self._cambiar_grosor_de_bordes(+1)
+ elif evento.texto == '-':
+ self._cambiar_grosor_de_bordes(-1)
+
+ def _cambiar_grosor_de_bordes(self, cambio):
+ ModoDepurador.grosor_de_lineas = max(1, ModoDepurador.grosor_de_lineas + cambio)
+
+ def _alternar_modo(self, clase_del_modo):
+ clases_activas = [x.__class__ for x in self.modos]
+
+ if clase_del_modo in clases_activas:
+ self._desactivar_modo(clase_del_modo)
+ else:
+ self._activar_modo(clase_del_modo)
+
+ def _activar_modo(self, clase_del_modo):
+ pilas.eventos.inicia_modo_depuracion.send('depurador')
+ instancia_del_modo = clase_del_modo(self)
+ self.modos.append(instancia_del_modo)
+ # Ordena todos los registros por numero de tecla.
+ self.modos.sort(key=lambda x: x.orden_de_tecla())
+
+ def _desactivar_modo(self, clase_del_modo):
+ instancia_a_eliminar = [x for x in self.modos
+ if x.__class__ == clase_del_modo]
+ self.modos.remove(instancia_a_eliminar[0])
+
+ if not self.modos:
+ pilas.eventos.sale_modo_depuracion.send('depurador')
+
+ def _mostrar_nombres_de_modos(self, motor):
+ dy = 0
+ izquierda, derecha, arriba, abajo = pilas.utils.obtener_bordes()
+
+ for modo in self.modos:
+ texto = modo.tecla + " " + modo.__class__.__name__ + " habilitado."
+ self.lienzo.texto_absoluto(motor, texto, izquierda + 10, arriba -20 +dy,
+ color=pilas.colores.violeta)
+ dy -= 20
+
+ def _mostrar_posicion_del_mouse(self, motor):
+ izquierda, derecha, arriba, abajo = pilas.utils.obtener_bordes()
+ x, y = self.posicion_del_mouse
+ texto = u"Posición del mouse: x=%d y=%d " %(x, y)
+ self.lienzo.texto_absoluto(motor, texto, derecha - 230, abajo + 10, color=pilas.colores.violeta)
+
+ def _mostrar_cuadros_por_segundo(self, motor):
+ izquierda, derecha, arriba, abajo = pilas.utils.obtener_bordes()
+ rendimiento = self.fps.obtener_cuadros_por_segundo()
+ texto = "Cuadros por segundo: %s" %(rendimiento)
+ self.lienzo.texto_absoluto(motor, texto, izquierda + 10, abajo + 10,
+ color=pilas.colores.violeta)
+
+ def _mostrar_cantidad_de_actores(self, motor):
+ izquierda, derecha, arriba, abajo = pilas.utils.obtener_bordes()
+ total_de_actores = len(pilas.actores.todos)
+ texto = "Cantidad de actores: %s" %(total_de_actores)
+ self.lienzo.texto_absoluto(motor, texto, izquierda + 10, abajo + 30,
+ color=pilas.colores.violeta)
+
+class ModoDepurador(object):
+ tecla = "F00"
+
+ def __init__(self, depurador):
+ self.depurador = depurador
+
+ def comienza_dibujado(self, motor, lienzo):
+ pass
+
+ def dibuja_al_actor(self, motor, lienzo, actor):
+ pass
+
+ def termina_dibujado(self, motor, lienzo):
+ pass
+
+ def orden_de_tecla(self):
+ return int(self.tecla[1:])
+
+class ModoPuntosDeControl(ModoDepurador):
+ tecla = "F8"
+
+ def dibuja_al_actor(self, motor, lienzo, actor):
+ lienzo.cruz(motor, actor.x, actor.y, color=pilas.colores.rojo, grosor=ModoDepurador.grosor_de_lineas)
+
+class ModoRadiosDeColision(ModoDepurador):
+ tecla = "F9"
+
+ def dibuja_al_actor(self, motor, lienzo, actor):
+ lienzo.circulo(motor, actor.x, actor.y, actor.radio_de_colision, color=pilas.colores.verde, grosor=ModoDepurador.grosor_de_lineas)
+
+class ModoArea(ModoDepurador):
+ tecla = "F10"
+
+ def dibuja_al_actor(self, motor, lienzo, actor):
+ dx, dy = actor.centro
+ lienzo.rectangulo(motor, actor.x - dx, actor.y + dy, actor.ancho, actor.alto, color=pilas.colores.azul, grosor=ModoDepurador.grosor_de_lineas)
+
+class ModoPosicion(ModoDepurador):
+ tecla = "F12"
+
+ def __init__(self, depurador):
+ ModoDepurador.__init__(self, depurador)
+
+ def dibuja_al_actor(self, motor, lienzo, actor):
+ if not isinstance(actor, pilas.fondos.Fondo):
+ texto = "(%d, %d)" %(actor.x, actor.y)
+ lienzo.texto(motor, texto, actor.derecha, actor.abajo, color=pilas.colores.violeta)
+
+class ModoFisica(ModoDepurador):
+ tecla = "F11"
+
+ def termina_dibujado(self, motor, lienzo):
+ grosor = ModoDepurador.grosor_de_lineas
+ pilas.mundo.fisica.dibujar_figuras_sobre_lienzo(motor, lienzo, grosor)
+
+class ModoInformacionDeSistema(ModoDepurador):
+ tecla = "F7"
+
+ def __init__(self, depurador):
+ ModoDepurador.__init__(self, depurador)
+
+ self.informacion = [
+ "Usando el motor: " + pilas.mundo.motor.__class__.__name__,
+ "Sistema: " + sys.platform,
+ "Version de pilas: " + pilasversion.VERSION,
+ "Version de python: " + sys.subversion[0] + " " + sys.subversion[1],
+ ]
+
+ def termina_dibujado(self, motor, lienzo):
+ izquierda, derecha, arriba, abajo = pilas.utils.obtener_bordes()
+
+ for (i, texto) in enumerate(self.informacion):
+ posicion_y = abajo + 50 + i * 20
+ lienzo.texto(motor, texto, izquierda + 10, posicion_y, color=pilas.colores.negro)
diff --git a/pilas/dispatch/__init__.py b/pilas/dispatch/__init__.py
new file mode 100644
index 0000000..683aba9
--- /dev/null
+++ b/pilas/dispatch/__init__.py
@@ -0,0 +1,9 @@
+"""Multi-consumer multi-producer dispatching mechanism
+
+Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1
+See license.txt for original license.
+
+Heavily modified for Django's purposes.
+"""
+
+from pilas.dispatch.dispatcher import Signal
diff --git a/pilas/dispatch/dispatcher.py b/pilas/dispatch/dispatcher.py
new file mode 100644
index 0000000..5576625
--- /dev/null
+++ b/pilas/dispatch/dispatcher.py
@@ -0,0 +1,243 @@
+import weakref
+
+from pilas.dispatch import saferef
+
+WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
+
+
+class DictObj(object):
+ def __init__(self, d):
+ self.d = d
+
+ def __getattr__(self, m):
+ return self.d.get(m, None)
+
+ def __str__(self):
+ return "<Evento %s>" %(str(self.d))
+
+def _make_id(target):
+ if hasattr(target, 'im_func'):
+ return (id(target.im_self), id(target.im_func))
+ return id(target)
+
+class Signal(object):
+ """
+ Base class for all signals
+
+ Internal attributes:
+
+ receivers
+ { receriverkey (id) : weakref(receiver) }
+ """
+
+ def __init__(self, providing_args=None):
+ """
+ Create a new signal.
+
+ providing_args
+ A list of the arguments this signal can pass along in a send() call.
+ """
+ self.receivers = []
+ if providing_args is None:
+ providing_args = []
+ self.providing_args = set(providing_args)
+
+ def conectar(self, receptor, emisor=None, weak=True, uid=None):
+ return self.connect(receptor, emisor, weak, uid)
+
+ def connect(self, receiver, sender=None, weak=True, uid=None):
+ """
+ Connect receiver to sender for signal.
+
+ Arguments:
+
+ receiver
+ A function or an instance method which is to receive signals.
+ Receivers must be hashable objects.
+
+ if weak is True, then receiver must be weak-referencable (more
+ precisely saferef.safeRef() must be able to create a reference
+ to the receiver).
+
+ Receivers must be able to accept keyword arguments.
+
+ If receivers have a uid attribute, the receiver will
+ not be added if another receiver already exists with that
+ uid.
+
+ sender
+ The sender to which the receiver should respond Must either be
+ of type Signal, or None to receive events from any sender.
+
+ weak
+ Whether to use weak references to the receiver By default, the
+ module will attempt to use weak references to the receiver
+ objects. If this parameter is false, then strong references will
+ be used.
+
+ uid
+ An identifier used to uniquely identify a particular instance of
+ a receiver. This will usually be a string, though it may be
+ anything hashable.
+ """
+
+ if uid:
+ lookup_key = (uid, _make_id(sender))
+ else:
+ lookup_key = (_make_id(receiver), _make_id(sender))
+
+ if weak:
+ receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver)
+
+ for r_key, _ in self.receivers:
+ if r_key == lookup_key:
+ break
+ else:
+ self.receivers.append((lookup_key, receiver))
+
+ def desconectar(self, receptor=None, emisor=None, weak=True, uid=None):
+ self.disconnect(receptor, emisor, weak, uid)
+
+ def disconnect(self, receiver=None, sender=None, weak=True, uid=None):
+ """
+ Disconnect receiver from sender for signal.
+
+ If weak references are used, disconnect need not be called. The receiver
+ will be remove from dispatch automatically.
+
+ Arguments:
+
+ receiver
+ The registered receiver to disconnect. May be none if
+ uid is specified.
+
+ sender
+ The registered sender to disconnect
+
+ weak
+ The weakref state to disconnect
+
+ uid
+ the unique identifier of the receiver to disconnect
+ """
+ if uid:
+ lookup_key = (uid, _make_id(sender))
+ else:
+ lookup_key = (_make_id(receiver), _make_id(sender))
+
+ for index in xrange(len(self.receivers)):
+ (r_key, _) = self.receivers[index]
+ if r_key == lookup_key:
+ del self.receivers[index]
+ break
+
+ def send(self, sender, **named):
+ """
+ Send signal from sender to all connected receivers.
+
+ If any receiver raises an error, the error propagates back through send,
+ terminating the dispatch loop, so it is quite possible to not have all
+ receivers called if a raises an error.
+
+ Arguments:
+
+ sender
+ The sender of the signal Either a specific object or None.
+
+ named
+ Named arguments which will be passed to receivers.
+
+ Returns a list of tuple pairs [(receiver, response), ... ].
+ """
+ responses = []
+ if not self.receivers:
+ return responses
+
+ for receiver in self._live_receivers(_make_id(sender)):
+ response = receiver(DictObj(named))
+ responses.append((receiver, response))
+ return responses
+
+ def send_robust(self, sender, **named):
+ """
+ Send signal from sender to all connected receivers catching errors.
+
+ Arguments:
+
+ sender
+ The sender of the signal Can be any python object (normally one
+ registered with a connect if you actually want something to
+ occur).
+
+ named
+ Named arguments which will be passed to receivers. These
+ arguments must be a subset of the argument names defined in
+ providing_args.
+
+ Return a list of tuple pairs [(receiver, response), ... ]. May raise
+ DispatcherKeyError.
+
+ if any receiver raises an error (specifically any subclass of
+ Exception), the error instance is returned as the result for that
+ receiver.
+ """
+ responses = []
+ if not self.receivers:
+ return responses
+
+ # Call each receiver with whatever arguments it can accept.
+ # Return a list of tuple pairs [(receiver, response), ... ].
+ for receiver in self._live_receivers(_make_id(sender)):
+ try:
+ response = receiver(signal=self, sender=sender, **named)
+ except Exception, err:
+ responses.append((receiver, err))
+ else:
+ responses.append((receiver, response))
+ return responses
+
+ def _live_receivers(self, senderkey):
+ """
+ Filter sequence of receivers to get resolved, live receivers.
+
+ This checks for weak references and resolves them, then returning only
+ live receivers.
+ """
+ none_senderkey = _make_id(None)
+ receivers = []
+
+ for (receiverkey, r_senderkey), receiver in self.receivers:
+ if r_senderkey == none_senderkey or r_senderkey == senderkey:
+ if isinstance(receiver, WEAKREF_TYPES):
+ # Dereference the weak reference.
+ receiver = receiver()
+ if receiver is not None:
+ receivers.append(receiver)
+ else:
+ receivers.append(receiver)
+ return receivers
+
+ def _remove_receiver(self, receiver):
+ """
+ Remove dead receivers from connections.
+ """
+
+ to_remove = []
+ for key, connected_receiver in self.receivers:
+ if connected_receiver == receiver:
+ to_remove.append(key)
+ for key in to_remove:
+ for idx, (r_key, _) in enumerate(self.receivers):
+ if r_key == key:
+ del self.receivers[idx]
+
+ def esta_conectado(self):
+ "Indica si tiene alguna funcion conectada."
+ return self.receivers
+
+ def imprimir_funciones_conectadas(self):
+ "Imprime todas las funciones que tiene conectado el evento."
+ for clave, referencia in self.receivers:
+ nombre = referencia.__str__()
+ nombre = nombre[nombre.index('(')+1:nombre.index(")")]
+ print "\t", nombre \ No newline at end of file
diff --git a/pilas/dispatch/license.txt b/pilas/dispatch/license.txt
new file mode 100644
index 0000000..505090d
--- /dev/null
+++ b/pilas/dispatch/license.txt
@@ -0,0 +1,36 @@
+django.dispatch was originally forked from PyDispatcher.
+
+PyDispatcher License:
+
+ Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ The name of Patrick K. O'Brien, or the name of any Contributor,
+ may not be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/pilas/dispatch/saferef.py b/pilas/dispatch/saferef.py
new file mode 100644
index 0000000..8bcfd8a
--- /dev/null
+++ b/pilas/dispatch/saferef.py
@@ -0,0 +1,250 @@
+"""
+"Safe weakrefs", originally from pyDispatcher.
+
+Provides a way to safely weakref any function, including bound methods (which
+aren't handled by the core weakref module).
+"""
+
+import weakref, traceback
+
+def safeRef(target, onDelete = None):
+ """Return a *safe* weak reference to a callable target
+
+ target -- the object to be weakly referenced, if it's a
+ bound method reference, will create a BoundMethodWeakref,
+ otherwise creates a simple weakref.
+ onDelete -- if provided, will have a hard reference stored
+ to the callable to be called after the safe reference
+ goes out of scope with the reference object, (either a
+ weakref or a BoundMethodWeakref) as argument.
+ """
+ if hasattr(target, 'im_self'):
+ if target.im_self is not None:
+ # Turn a bound method into a BoundMethodWeakref instance.
+ # Keep track of these instances for lookup by disconnect().
+ assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
+ reference = get_bound_method_weakref(
+ target=target,
+ onDelete=onDelete
+ )
+ return reference
+ if callable(onDelete):
+ return weakref.ref(target, onDelete)
+ else:
+ return weakref.ref( target )
+
+class BoundMethodWeakref(object):
+ """'Safe' and reusable weak references to instance methods
+
+ BoundMethodWeakref objects provide a mechanism for
+ referencing a bound method without requiring that the
+ method object itself (which is normally a transient
+ object) is kept alive. Instead, the BoundMethodWeakref
+ object keeps weak references to both the object and the
+ function which together define the instance method.
+
+ Attributes:
+ key -- the identity key for the reference, calculated
+ by the class's calculateKey method applied to the
+ target instance method
+ deletionMethods -- sequence of callable objects taking
+ single argument, a reference to this object which
+ will be called when *either* the target object or
+ target function is garbage collected (i.e. when
+ this object becomes invalid). These are specified
+ as the onDelete parameters of safeRef calls.
+ weakSelf -- weak reference to the target object
+ weakFunc -- weak reference to the target function
+
+ Class Attributes:
+ _allInstances -- class attribute pointing to all live
+ BoundMethodWeakref objects indexed by the class's
+ calculateKey(target) method applied to the target
+ objects. This weak value dictionary is used to
+ short-circuit creation so that multiple references
+ to the same (object, function) pair produce the
+ same BoundMethodWeakref instance.
+
+ """
+
+ _allInstances = weakref.WeakValueDictionary()
+
+ def __new__( cls, target, onDelete=None, *arguments,**named ):
+ """Create new instance or return current instance
+
+ Basically this method of construction allows us to
+ short-circuit creation of references to already-
+ referenced instance methods. The key corresponding
+ to the target is calculated, and if there is already
+ an existing reference, that is returned, with its
+ deletionMethods attribute updated. Otherwise the
+ new instance is created and registered in the table
+ of already-referenced methods.
+ """
+ key = cls.calculateKey(target)
+ current =cls._allInstances.get(key)
+ if current is not None:
+ current.deletionMethods.append( onDelete)
+ return current
+ else:
+ base = super( BoundMethodWeakref, cls).__new__( cls )
+ cls._allInstances[key] = base
+ base.__init__( target, onDelete, *arguments,**named)
+ return base
+
+ def __init__(self, target, onDelete=None):
+ """Return a weak-reference-like instance for a bound method
+
+ target -- the instance-method target for the weak
+ reference, must have im_self and im_func attributes
+ and be reconstructable via:
+ target.im_func.__get__( target.im_self )
+ which is true of built-in instance methods.
+ onDelete -- optional callback which will be called
+ when this weak reference ceases to be valid
+ (i.e. either the object or the function is garbage
+ collected). Should take a single argument,
+ which will be passed a pointer to this object.
+ """
+ def remove(weak, self=self):
+ """Set self.isDead to true when method or instance is destroyed"""
+ methods = self.deletionMethods[:]
+ del self.deletionMethods[:]
+ try:
+ del self.__class__._allInstances[ self.key ]
+ except KeyError:
+ pass
+ for function in methods:
+ try:
+ if callable( function ):
+ function( self )
+ except Exception, e:
+ try:
+ traceback.print_exc()
+ except AttributeError, err:
+ print '''Exception during saferef %s cleanup function %s: %s'''%(
+ self, function, e
+ )
+ self.deletionMethods = [onDelete]
+ self.key = self.calculateKey( target )
+ self.weakSelf = weakref.ref(target.im_self, remove)
+ self.weakFunc = weakref.ref(target.im_func, remove)
+ self.selfName = str(target.im_self)
+ self.funcName = str(target.im_func.__name__)
+
+ def calculateKey( cls, target ):
+ """Calculate the reference key for this reference
+
+ Currently this is a two-tuple of the id()'s of the
+ target object and the target function respectively.
+ """
+ return (id(target.im_self),id(target.im_func))
+ calculateKey = classmethod( calculateKey )
+
+ def __str__(self):
+ """Give a friendly representation of the object"""
+ return """%s( %s.%s )"""%(
+ self.__class__.__name__,
+ self.selfName,
+ self.funcName,
+ )
+
+ __repr__ = __str__
+
+ def __nonzero__( self ):
+ """Whether we are still a valid reference"""
+ return self() is not None
+
+ def __cmp__( self, other ):
+ """Compare with another reference"""
+ if not isinstance (other,self.__class__):
+ return cmp( self.__class__, type(other) )
+ return cmp( self.key, other.key)
+
+ def __call__(self):
+ """Return a strong reference to the bound method
+
+ If the target cannot be retrieved, then will
+ return None, otherwise returns a bound instance
+ method for our object and function.
+
+ Note:
+ You may call this method any number of times,
+ as it does not invalidate the reference.
+ """
+ target = self.weakSelf()
+ if target is not None:
+ function = self.weakFunc()
+ if function is not None:
+ return function.__get__(target)
+ return None
+
+class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
+ """A specialized BoundMethodWeakref, for platforms where instance methods
+ are not descriptors.
+
+ It assumes that the function name and the target attribute name are the
+ same, instead of assuming that the function is a descriptor. This approach
+ is equally fast, but not 100% reliable because functions can be stored on an
+ attribute named differenty than the function's name such as in:
+
+ class A: pass
+ def foo(self): return "foo"
+ A.bar = foo
+
+ But this shouldn't be a common use case. So, on platforms where methods
+ aren't descriptors (such as Jython) this implementation has the advantage
+ of working in the most cases.
+ """
+ def __init__(self, target, onDelete=None):
+ """Return a weak-reference-like instance for a bound method
+
+ target -- the instance-method target for the weak
+ reference, must have im_self and im_func attributes
+ and be reconstructable via:
+ target.im_func.__get__( target.im_self )
+ which is true of built-in instance methods.
+ onDelete -- optional callback which will be called
+ when this weak reference ceases to be valid
+ (i.e. either the object or the function is garbage
+ collected). Should take a single argument,
+ which will be passed a pointer to this object.
+ """
+ assert getattr(target.im_self, target.__name__) == target, \
+ ("method %s isn't available as the attribute %s of %s" %
+ (target, target.__name__, target.im_self))
+ super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
+
+ def __call__(self):
+ """Return a strong reference to the bound method
+
+ If the target cannot be retrieved, then will
+ return None, otherwise returns a bound instance
+ method for our object and function.
+
+ Note:
+ You may call this method any number of times,
+ as it does not invalidate the reference.
+ """
+ target = self.weakSelf()
+ if target is not None:
+ function = self.weakFunc()
+ if function is not None:
+ # Using curry() would be another option, but it erases the
+ # "signature" of the function. That is, after a function is
+ # curried, the inspect module can't be used to determine how
+ # many arguments the function expects, nor what keyword
+ # arguments it supports, and pydispatcher needs this
+ # information.
+ return getattr(target, function.__name__)
+ return None
+
+def get_bound_method_weakref(target, onDelete):
+ """Instantiates the appropiate BoundMethodWeakRef, depending on the details of
+ the underlying class method implementation"""
+ if hasattr(target, '__get__'):
+ # target method is a descriptor, so the default implementation works:
+ return BoundMethodWeakref(target=target, onDelete=onDelete)
+ else:
+ # no luck, use the alternative implementation:
+ return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete)
diff --git a/pilas/ejemplos/__init__.py b/pilas/ejemplos/__init__.py
new file mode 100644
index 0000000..89f0399
--- /dev/null
+++ b/pilas/ejemplos/__init__.py
@@ -0,0 +1,7 @@
+from piezas import Piezas
+from colisiones import Colisiones
+from fisica import ColisionesFisicas
+
+
+def ejecutar():
+ print "asdasd"
diff --git a/pilas/ejemplos/colisiones.py b/pilas/ejemplos/colisiones.py
new file mode 100644
index 0000000..c8295ee
--- /dev/null
+++ b/pilas/ejemplos/colisiones.py
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+def comer(mono, banana):
+ mono.sonreir()
+ banana.eliminar()
+
+class Colisiones(pilas.escenas.Normal):
+ """Es una escena que tiene un mono y algunas frutas para comer."""
+
+
+ def __init__(self):
+ pilas.escenas.Normal.__init__(self, pilas.colores.grisoscuro)
+ self.crear_personajes()
+
+ pilas.mundo.colisiones.agregar([self.mono], self.bananas, comer)
+ pilas.avisar("Utilice el mouse para mover al mono y comer")
+
+ def crear_personajes(self):
+ self.mono = pilas.actores.Mono()
+ self.mono.aprender(pilas.habilidades.Arrastrable)
+
+ self.bananas = pilas.atajos.fabricar(pilas.actores.Banana, 20)
+
+
+
+
diff --git a/pilas/ejemplos/data/fondo_piezas.png b/pilas/ejemplos/data/fondo_piezas.png
new file mode 100644
index 0000000..bec6841
--- /dev/null
+++ b/pilas/ejemplos/data/fondo_piezas.png
Binary files differ
diff --git a/pilas/ejemplos/data/piezas.png b/pilas/ejemplos/data/piezas.png
new file mode 100644
index 0000000..5c4ff8e
--- /dev/null
+++ b/pilas/ejemplos/data/piezas.png
Binary files differ
diff --git a/pilas/ejemplos/fisica.py b/pilas/ejemplos/fisica.py
new file mode 100644
index 0000000..20b9d35
--- /dev/null
+++ b/pilas/ejemplos/fisica.py
@@ -0,0 +1,26 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class ColisionesFisicas(pilas.escenas.Normal):
+
+ def __init__(self):
+ pilas.escenas.Normal.__init__(self, pilas.colores.grisoscuro)
+ pilas.avisar("Un ejemplo de colisiones")
+
+ pilas.fondos.Pasto()
+
+ m = pilas.actores.Mono()
+ m.aprender(pilas.habilidades.Arrastrable)
+ m.aprender(pilas.habilidades.ColisionableComoPelota)
+
+ b = pilas.actores.Bomba()
+ b.aprender(pilas.habilidades.RebotarComoPelota)
+
+ pilas.atajos.fabricar(pilas.actores.Pelota, 20)
diff --git a/pilas/ejemplos/listaseleccion.py b/pilas/ejemplos/listaseleccion.py
new file mode 100644
index 0000000..1ac1ac0
--- /dev/null
+++ b/pilas/ejemplos/listaseleccion.py
@@ -0,0 +1,12 @@
+import pilas
+pilas.iniciar()
+
+
+def cuando_selecciona(opcion):
+ print "Ha seleccionado la opcion:", opcion
+
+
+consulta = pilas.interfaz.ListaSeleccion(['Uno', 'Dos', 'Tres'], cuando_selecciona)
+
+pilas.ejecutar()
+
diff --git a/pilas/ejemplos/piezas.py b/pilas/ejemplos/piezas.py
new file mode 100644
index 0000000..e7cae75
--- /dev/null
+++ b/pilas/ejemplos/piezas.py
@@ -0,0 +1,247 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+import random
+
+
+class Piezas(pilas.escenas.Normal):
+ """Representa la escena de rompecabezas.
+
+ La escena comienza con una imagen que se descompone en muchos
+ actores Pieza.
+ """
+
+ def __init__(self, ruta_a_la_imagen="ejemplos/data/piezas.png", filas=4, columnas=4, al_terminar=None):
+ pilas.actores.utils.eliminar_a_todos()
+ pilas.escenas.Normal.__init__(self, pilas.colores.grisoscuro)
+ grilla = pilas.imagenes.cargar_grilla(ruta_a_la_imagen, columnas, filas)
+ self.crear_piezas(grilla, filas, columnas)
+ self.pieza_en_movimiento = None
+
+ pilas.eventos.click_de_mouse.conectar(self.al_hacer_click)
+ pilas.eventos.termina_click.connect(self.al_soltar_el_click)
+ pilas.eventos.mueve_mouse.connect(self.al_mover_el_mouse)
+
+ self.sonido_tick = pilas.sonidos.cargar("tick.wav")
+ self.al_terminar = al_terminar
+ self.piezas_desconectadas = filas * columnas -1
+
+ def crear_piezas(self, grilla, filas, columnas):
+ "Genera todas las piezas en base al tamaño del constructor."
+ self.piezas = []
+ self.grupos = {}
+
+ for x in range(filas * columnas):
+ self.grupos[x] = set([x])
+ pieza = Pieza(self, grilla, x, filas, columnas)
+ self.piezas.append(pieza)
+ pieza.x = random.randint(-200, 200)
+ pieza.y = random.randint(-200, 200)
+
+
+ def al_hacer_click(self, evento):
+ "Atiente cualquier click que realice el usuario en la pantalla."
+ x, y = evento.x, evento.y
+ pieza_debajo_de_mouse = pilas.actores.utils.obtener_actor_en(x, y)
+
+ if pieza_debajo_de_mouse and isinstance(pieza_debajo_de_mouse, Pieza):
+ self.pieza_en_movimiento = pieza_debajo_de_mouse
+ self.pieza_en_movimiento.mostrar_arriba_todas_las_piezas()
+
+ def al_soltar_el_click(self, evento):
+ if self.pieza_en_movimiento:
+ self.pieza_en_movimiento.soltar_todas_las_piezas_del_grupo()
+ self.pieza_en_movimiento.mostrar_abajo_todas_las_piezas()
+ self.pieza_en_movimiento = None
+
+ def al_mover_el_mouse(self, evento):
+ if self.pieza_en_movimiento:
+ self.pieza_en_movimiento.x += evento.dx
+ self.pieza_en_movimiento.y += evento.dy
+
+ def conectar(self, pieza_a, pieza_b):
+ a = pieza_a.numero
+ b = pieza_b.numero
+
+
+ if a in self.grupos[b]:
+ #Evita contectar mas de una vez a dos piezas.
+ return
+
+ """Inicialmente comienzo con::
+
+
+ 0: [0, 1, 2]
+ 1: [0, 1, 2]
+ 2: [0, 1, 2]
+ 3: [3]
+
+ ¿y si conecto la pieza 3 con la 2?
+
+ - tendría que obtener todas las piezas que conoce 2.
+
+ - iterar en ese grupo y decirle a cada pieza que sume a 3 en su grupo::
+
+ 0: [0, 1, 2, 3]
+ 1: [0, 1, 2, 3]
+ 2: [0, 1, 2, 3]
+
+ - luego solo me falta tomar a uno de esos grupos actualizados
+ y decirle a 3 que ese será su grupo::
+
+ 3: [0, 1, 2, 3]
+ """
+
+ grupo_nuevo = set(self.grupos[a]).union(self.grupos[b])
+
+ for pieza in grupo_nuevo:
+ self.grupos[pieza] = grupo_nuevo
+
+ self.piezas_desconectadas -= 1
+
+ if self.piezas_desconectadas < 1:
+ if self.al_terminar:
+ self.al_terminar()
+
+ self.sonido_tick.reproducir()
+
+class Pieza(pilas.actores.Animado):
+ """Representa una pieza del rompecabezas.
+
+ Esta pieza se puede arrastrar con el mouse y cuando se suelta
+ intentará conectarse con las demás."""
+
+ def __init__(self, escena_padre, grilla, cuadro, filas, columnas):
+ "Genera la pieza que representa una parte de la imagen completa."
+ self.escena_padre = escena_padre
+ self.numero = cuadro
+ pilas.actores.Animado.__init__(self, grilla)
+
+ self.z_de_la_pieza_mas_alta = 0
+ self.asignar_numero_de_piezas_laterales(cuadro, columnas)
+
+ self.definir_cuadro(cuadro)
+
+ self.radio_de_colision = self.obtener_ancho() / 2 + 12
+ self.piezas_conectadas = []
+
+ def asignar_numero_de_piezas_laterales(self, cuadro, columnas):
+ "Guarda el numero de las piezas que se pueden conectar en sus bordes."
+ self.numero_arriba = cuadro - columnas
+ self.numero_abajo = cuadro + columnas
+
+ if cuadro % columnas == 0:
+ self.numero_izquierda = -1
+ else:
+ self.numero_izquierda = cuadro - 1
+
+ if cuadro % columnas == columnas -1:
+ self.numero_derecha = -1
+ else:
+ self.numero_derecha = cuadro + 1
+
+ def soltar_todas_las_piezas_del_grupo(self):
+ for numero in self.escena_padre.grupos[self.numero]:
+ pieza = self.escena_padre.piezas[numero]
+ pieza.soltar()
+
+ def soltar(self):
+ # Busca todas las colisiones entre esta pieza
+ # que se suelta y todas las demás.
+ colisiones = pilas.mundo.colisiones.obtener_colisiones(self, self.escena_padre.piezas)
+
+ for x in colisiones:
+ self.intentar_conectarse_a(x)
+
+ def se_pueden_conectar_los_bordes(self, borde1, borde2):
+ distancia = pilas.utils.distancia(borde1, borde2)
+ return distancia < 12
+
+ def intentar_conectarse_a(self, otra):
+ "Intenta vincular dos piezas, siempre y cuando coincidan en sus bordes."
+
+ # Intenta conectar los bordes laterales
+ if self.numero_derecha == otra.numero:
+ if self.se_pueden_conectar_los_bordes(self.derecha, otra.izquierda):
+ otra.izquierda = self.derecha
+ otra.arriba = self.arriba
+ self.conectar_con(otra)
+
+ elif self.numero_izquierda == otra.numero:
+ if self.se_pueden_conectar_los_bordes(self.izquierda, otra.derecha):
+ otra.derecha = self.izquierda
+ otra.arriba = self.arriba
+ self.conectar_con(otra)
+
+ # Intenta conectar los bordes superior e inferior
+ if self.numero_abajo == otra.numero:
+ if self.se_pueden_conectar_los_bordes(self.abajo, otra.arriba):
+ otra.arriba = self.abajo
+ otra.izquierda = self.izquierda
+ self.conectar_con(otra)
+
+ elif self.numero_arriba == otra.numero:
+ if self.se_pueden_conectar_los_bordes(self.arriba, otra.abajo):
+ otra.abajo = self.arriba
+ otra.izquierda = self.izquierda
+ self.conectar_con(otra)
+
+
+ def conectar_con(self, otra_pieza):
+ self.escena_padre.conectar(self, otra_pieza)
+
+
+ def __repr__(self):
+ return "<<Pieza %d>>" %(self.animacion.obtener_cuadro())
+
+
+ def set_x(self, x):
+ "A diferencia de los actores normales, las piezas tienen que mover a todo su grupo."
+ dx = x - self.x
+
+ for numero in self.escena_padre.grupos[self.numero]:
+ try:
+ pieza = self.escena_padre.piezas[numero]
+ pieza.definir_posicion(pieza.x + dx, pieza.y)
+ except IndexError:
+ pass
+
+ def set_y(self, y):
+ "A diferencia de los actores normales, las piezas tienen que mover a todo su grupo."
+ dy = y - self.y
+
+ for numero in self.escena_padre.grupos[self.numero]:
+ try:
+ pieza = self.escena_padre.piezas[numero]
+ pieza.definir_posicion(pieza.x, pieza.y + dy)
+ except IndexError:
+ pass
+
+ def get_x(self):
+ x, y = self.obtener_posicion()
+ return x
+
+ def get_y(self):
+ x, y = self.obtener_posicion()
+ return y
+
+ x = property(get_x, set_x, doc="Define la posición horizontal.")
+ y = property(get_y, set_y, doc="Define la posición vertical.")
+
+ def mostrar_arriba_todas_las_piezas(self):
+ for numero in self.escena_padre.grupos[self.numero]:
+ pieza = self.escena_padre.piezas[numero]
+ pieza.z = -1
+
+ def mostrar_abajo_todas_las_piezas(self):
+ for numero in self.escena_padre.grupos[self.numero]:
+ pieza = self.escena_padre.piezas[numero]
+ pieza.z = 0
+
+
diff --git a/pilas/ejemplos/test.py b/pilas/ejemplos/test.py
new file mode 100644
index 0000000..3e98456
--- /dev/null
+++ b/pilas/ejemplos/test.py
@@ -0,0 +1,40 @@
+# -*- encoding: utf-8 -*-
+import random
+import unittest
+import pilas
+
+class MockEscena:
+
+ def __init__(self):
+ self.grupos = {1: [],
+ 8: []}
+
+class TestPiezas(unittest.TestCase):
+
+ def test_pieza_de_ejemplo(self):
+
+ filas = 3
+ columnas = 3
+
+ mock_escena = MockEscena()
+
+ grilla = pilas.imagenes.carga_grilla("ejemplos/data/piezas.png", filas, columnas)
+ p = pilas.ejemplos.piezas.Pieza(mock_escena, grilla, 1, filas, columnas)
+
+ self.assertEqual(p.numero, 1)
+ self.assertEqual(p.numero_derecha, 2)
+ self.assertEqual(p.numero_izquierda, 0)
+ self.assertTrue(p.numero_arriba < 0)
+ self.assertEqual(p.numero_abajo, 4)
+
+ p = pilas.ejemplos.piezas.Pieza(mock_escena, grilla, 8, filas, columnas)
+
+ self.assertEqual(p.numero, 8)
+ self.assertEqual(p.numero_derecha, -1) # la pieza 8 no tiene borde derecho.
+ self.assertEqual(p.numero_izquierda, 7)
+ self.assertEqual(p.numero_arriba, 5)
+ self.assertEqual(p.numero_abajo, 11) # la pieza 8 no tiene parte de abajo
+
+if __name__ == '__main__':
+ pilas.iniciar()
+ unittest.main()
diff --git a/pilas/escenas.py b/pilas/escenas.py
new file mode 100644
index 0000000..e7e081f
--- /dev/null
+++ b/pilas/escenas.py
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class Escena(object):
+ "Escena abstracta."
+
+ def __init__(self):
+ pilas.actores.utils.destruir_a_todos()
+ pilas.mundo.definir_escena(self)
+
+ def iniciar(self):
+ pass
+
+ def terminar(self):
+ pass
+
+
+class Normal(Escena):
+ "Representa la escena inicial mas simple."
+
+ def __init__(self, color_de_fondo=None):
+ Escena.__init__(self)
+ self.fondo = pilas.fondos.Color(color_de_fondo)
diff --git a/pilas/estudiante.py b/pilas/estudiante.py
new file mode 100644
index 0000000..0322a8a
--- /dev/null
+++ b/pilas/estudiante.py
@@ -0,0 +1,73 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas.utils
+
+class Estudiante:
+ """Representa la habilidad de poder realizar habiliadades y comportamientos."""
+
+ def __init__(self):
+ self.habilidades = []
+ self.comportamiento_actual = None
+ self.comportamientos = []
+ self.repetir_comportamientos_por_siempre = False
+
+ def aprender(self, classname, *k, **w):
+ "Comienza a realizar una habilidad indicada por parametros."
+ objeto_habilidad = classname(self, *k, **w)
+ self.habilidades.append(objeto_habilidad)
+
+ def hacer_luego(self, comportamiento, repetir_por_siempre=False):
+ """Define un nuevo comportamiento para realizar al final.
+
+ Los actores pueden tener una cadena de comportamientos, este
+ metodo agrega el comportamiento al final de la cadena.
+ """
+
+ self.comportamientos.append(comportamiento)
+ self.repetir_comportamientos_por_siempre = repetir_por_siempre
+
+ def hacer(self, comportamiento):
+ "Define el comportamiento para el actor de manera inmediata."
+ self.comportamientos.append(comportamiento)
+ self._adoptar_el_siguiente_comportamiento()
+
+ def eliminar_habilidades(self):
+ "Elimina todas las habilidades asociadas al actor."
+ for h in self.habilidades:
+ h.eliminar()
+
+ def eliminar_comportamientos(self):
+ "Elimina todos los comportamientos que tiene que hacer el actor."
+ for c in self.comportamientos:
+ c.eliminar()
+
+ def actualizar_habilidades(self):
+ for h in self.habilidades:
+ h.actualizar()
+
+ def actualizar_comportamientos(self):
+ termina = None
+
+ if self.comportamiento_actual:
+ termina = self.comportamiento_actual.actualizar()
+
+ if termina:
+ if self.repetir_comportamientos_por_siempre:
+ self.comportamientos.append(self.comportamiento_actual)
+ self._adoptar_el_siguiente_comportamiento()
+ else:
+ self._adoptar_el_siguiente_comportamiento()
+
+ def _adoptar_el_siguiente_comportamiento(self):
+ if self.comportamientos:
+ self.comportamiento_actual = self.comportamientos.pop(0)
+ self.comportamiento_actual.iniciar(self)
+ else:
+ self.comportamiento_actual = None
+
diff --git a/pilas/eventos.py b/pilas/eventos.py
new file mode 100644
index 0000000..d7a78bd
--- /dev/null
+++ b/pilas/eventos.py
@@ -0,0 +1,42 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import dispatch
+
+mueve_mouse = dispatch.Signal(providing_args=['x', 'y', 'dx', 'dy'])
+click_de_mouse = dispatch.Signal(providing_args=['button', 'x', 'y'])
+termina_click = dispatch.Signal(providing_args=['button', 'x', 'y'])
+mueve_rueda = dispatch.Signal(providing_args=['delta'])
+pulsa_tecla = dispatch.Signal(providing_args=['codigo', 'texto'])
+suelta_tecla = dispatch.Signal(providing_args=['codigo', 'texto'])
+pulsa_tecla_escape = dispatch.Signal(providing_args=[])
+actualizar = dispatch.Signal(providing_args=[])
+post_dibujar = dispatch.Signal(providing_args=[])
+
+# Se emite cuando el mundo ingresa o sale del modo depuracion (pulsando F12)
+inicia_modo_depuracion = dispatch.Signal(providing_args=[])
+sale_modo_depuracion = dispatch.Signal(providing_args=[])
+actualiza_modo_depuracion = dispatch.Signal(providing_args=[])
+
+
+def imprimir_todos():
+ "Muestra en consola los eventos activos y a quienes invocan"
+ imprime_alguno = False
+
+ for x in globals().items():
+ nombre = x[0]
+ evento = x[1]
+
+ if isinstance(evento, dispatch.Signal):
+ if evento.esta_conectado():
+ imprime_alguno = True
+ print "%s:" %(nombre)
+ evento.imprimir_funciones_conectadas()
+
+ if not imprime_alguno:
+ print "Ningun evento esta conectado."
diff --git a/pilas/fisica.py b/pilas/fisica.py
new file mode 100644
index 0000000..b13ad6e
--- /dev/null
+++ b/pilas/fisica.py
@@ -0,0 +1,452 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas import colores
+
+try:
+ import Box2D as box2d
+except ImportError:
+ print "No esta disponible box2d, se deshabilitara la fisica."
+
+import math
+
+class Fisica(object):
+ """Representa un simulador de mundo fisico, usando la biblioteca box2d."""
+
+ def __init__(self, area, gravedad=(0, -90)):
+ self.area = area
+ try:
+ self.escenario = box2d.b2AABB()
+ self.escenario.lowerBound = (-1000.0, -1000.0)
+ self.escenario.upperBound = (1000.0, 1000.0)
+ self.gravedad = box2d.b2Vec2(gravedad[0], gravedad[1])
+ try:
+ self.mundo = box2d.b2World(self.escenario, self.gravedad, True)
+ except ValueError:
+ print "Solo esta disponible el motor de fisica para box2d 2.0.2b1"
+ raise AttributeError("...")
+ except AttributeError:
+ print "Deshabilitando modulo de fisica (no se encuentra instalado pybox2d en este equipo)"
+ self.mundo = None
+ return
+
+ self.constante_mouse = None
+ self.i = 0
+ self.crear_bordes_del_escenario()
+ self.figuras_a_eliminar = []
+
+ def crear_bordes_del_escenario(self):
+ self.crear_techo(self.area)
+ self.crear_suelo(self.area)
+ self.crear_paredes(self.area)
+
+ def reiniciar(self):
+ for x in self.mundo.bodyList:
+ self.mundo.DestroyBody(x)
+
+ self.crear_bordes_del_escenario()
+
+ def capturar_figura_con_el_mouse(self, figura):
+ if self.constante_mouse:
+ self.cuando_suelta_el_mouse()
+
+ self.constante_mouse = ConstanteDeMovimiento(figura)
+
+ def cuando_mueve_el_mouse(self, x, y):
+ if self.constante_mouse:
+ self.constante_mouse.mover(x, y)
+
+ def cuando_suelta_el_mouse(self):
+ if self.constante_mouse:
+ self.constante_mouse.eliminar()
+ self.constante_mouse = None
+
+ def actualizar(self):
+ if self.mundo:
+ self.mundo.Step(1.0 / 20.0, 10, 8)
+ self.i += 1
+ self._procesar_figuras_a_eliminar()
+
+ def _procesar_figuras_a_eliminar(self):
+ "Elimina las figuras que han sido marcadas para quitar."
+ if self.figuras_a_eliminar:
+ for x in self.figuras_a_eliminar:
+ # Solo elimina las figuras que actualmente existen.
+ if x in self.mundo.bodyList:
+ self.mundo.DestroyBody(x)
+ self.figuras_a_eliminar = []
+
+ def dibujar_figuras_sobre_lienzo(self, motor, lienzo, grosor=1):
+ "Dibuja todas las figuras en una pizarra. Indicado para depuracion."
+ cuerpos = self.mundo.bodyList
+ cantidad_de_figuras = 0
+
+ for cuerpo in cuerpos:
+ xform = cuerpo.GetXForm()
+
+ for figura in cuerpo.shapeList:
+ cantidad_de_figuras += 1
+ tipo_de_figura = figura.GetType()
+
+ if tipo_de_figura == box2d.e_polygonShape:
+ vertices = []
+
+ for v in figura.vertices:
+ pt = box2d.b2Mul(xform, v)
+ vertices.append((pt.x, pt.y))
+
+ lienzo.poligono(motor, vertices, color=colores.rojo, grosor=grosor, cerrado=True)
+
+ elif tipo_de_figura == box2d.e_circleShape:
+ lienzo.circulo(motor, cuerpo.position.x, cuerpo.position.y, figura.radius, colores.rojo, grosor=grosor)
+ else:
+ print "no puedo identificar el tipo de figura."
+
+ def crear_cuerpo(self, definicion_de_cuerpo):
+ return self.mundo.CreateBody(definicion_de_cuerpo)
+
+ def crear_suelo(self, (ancho, alto), restitucion=0):
+ self.suelo = Rectangulo(0, -alto/2, ancho, 2, dinamica=False, fisica=self, restitucion=restitucion)
+
+ def crear_techo(self, (ancho, alto), restitucion=0):
+ self.techo = Rectangulo(0, alto/2, ancho, 2, dinamica=False, fisica=self, restitucion=restitucion)
+
+ def crear_paredes(self, (ancho, alto), restitucion=0):
+ self.pared_izquierda = Rectangulo(-ancho/2, 0, 2, alto, dinamica=False, fisica=self, restitucion=restitucion)
+ self.pared_derecha = Rectangulo(ancho/2, 0, 2, alto, dinamica=False, fisica=self, restitucion=restitucion)
+
+ def eliminar_suelo(self):
+ if self.suelo:
+ self.suelo.eliminar()
+ self.suelo = None
+
+ def eliminar_techo(self):
+ if self.techo:
+ self.techo.eliminar()
+ self.techo = None
+
+ def eliminar_paredes(self):
+ if self.pared_izquierda:
+ self.pared_derecha.eliminar()
+ self.pared_izquierda.eliminar()
+ self.pared_derecha = None
+ self.pared_izquierda = None
+
+ def eliminar_figura(self, figura):
+ self.figuras_a_eliminar.append(figura)
+
+ def obtener_distancia_al_suelo(self, x, y, dy):
+ """Obtiene la distancia hacia abajo desde el punto (x,y).
+
+ El valor de 'dy' tiene que ser positivo.
+
+ Si la funcion no encuentra obstaculos retornara
+ dy, pero en paso contrario retornara un valor menor
+ a dy.
+ """
+
+ if dy < 0:
+ raise Exception("El valor de 'dy' debe ser positivo, ahora vale '%f'." %(dy))
+
+ delta = 0
+
+ while delta < dy:
+
+ if self.obtener_cuerpos_en(x, y-delta):
+ return delta
+
+ delta += 1
+
+ return delta
+
+ def obtener_cuerpos_en(self, x, y):
+ "Retorna una lista de cuerpos que se encuentran en la posicion (x, y) o retorna una lista vacia []."
+
+ AABB = box2d.b2AABB()
+ f = 1
+ AABB.lowerBound = (x-f, y-f)
+ AABB.upperBound = (x+f, y+f)
+
+ cuantos, cuerpos = self.mundo.Query(AABB, 2)
+
+ if cuantos == 0:
+ return []
+
+ lista_de_cuerpos = []
+
+ for s in cuerpos:
+ cuerpo = s.GetBody()
+
+ if s.TestPoint(cuerpo.GetXForm(), (x, y)):
+ lista_de_cuerpos.append(cuerpo)
+
+ return lista_de_cuerpos
+
+ def definir_gravedad(self, x, y):
+ pilas.fisica.definir_gravedad(x, y)
+
+class Figura(object):
+ """Representa un figura que simula un cuerpo fisico.
+
+ Esta figura es abstracta, no está pensada para crear
+ objetos a partir de ella. Se usa como base para el resto
+ de las figuras cómo el Circulo o el Rectangulo simplemente."""
+
+ def obtener_x(self):
+ return self._cuerpo.position.x
+
+ def definir_x(self, x):
+ self._cuerpo.SetXForm((x, self.y), self._cuerpo.GetAngle())
+
+ def obtener_y(self):
+ return self._cuerpo.position.y
+
+ def definir_y(self, y):
+ self._cuerpo.SetXForm((self.x, y), self._cuerpo.GetAngle())
+
+ def obtener_rotacion(self):
+ return - math.degrees(self._cuerpo.GetAngle())
+
+ def definir_rotacion(self, angulo):
+ self._cuerpo.SetXForm((self.x, self.y), math.radians(-angulo))
+
+ def impulsar(self, dx, dy):
+ self._cuerpo.ApplyImpulse((dx, dy), self._cuerpo.GetWorldCenter())
+
+ def obtener_velocidad_lineal(self):
+ velocidad = self._cuerpo.GetLinearVelocity()
+ return (velocidad.x, velocidad.y)
+
+ def detener(self):
+ """Hace que la figura regrese al reposo."""
+ self.definir_velocidad_lineal(0, 0)
+
+ def definir_velocidad_lineal(self, dx=None, dy=None):
+ anterior_dx, anterior_dy = self.obtener_velocidad_lineal()
+
+ if dx is None:
+ dx = anterior_dx
+ if dy is None:
+ dy = anterior_dy
+
+ self._cuerpo.SetLinearVelocity((dx, dy))
+
+ def empujar(self, dx=None, dy=None):
+ self.definir_velocidad_lineal(dx, dy)
+
+ def eliminar(self):
+ """Quita una figura de la simulación."""
+ pilas.mundo.fisica.eliminar_figura(self._cuerpo)
+
+ x = property(obtener_x, definir_x, doc="define la posición horizontal.")
+ y = property(obtener_y, definir_y, doc="define la posición vertical.")
+ rotacion = property(obtener_rotacion, definir_rotacion, doc="define la rotacion.")
+
+class Circulo(Figura):
+ """Representa un cuerpo de circulo.
+
+ Generalmente estas figuras se pueden construir independientes de un
+ actor, y luego asociar.
+
+ Por ejemplo, podríamos crear un círculo:
+
+ >>> circulo_dinamico = pilas.fisica.Circulo(10, 200, 50)
+
+ y luego tomar un actor cualquiera, y decirle que se comporte
+ cómo el circulo:
+
+ >>> mono = pilas.actores.Mono()
+ >>> mono.imitar(circulo_dinamico)
+ """
+
+ def __init__(self, x, y, radio, dinamica=True, densidad=1.0,
+ restitucion=0.56, friccion=10.5, amortiguacion=0.1,
+ fisica=None):
+
+ if not fisica:
+ fisica = pilas.mundo.fisica
+
+ bodyDef = box2d.b2BodyDef()
+ bodyDef.position=(x, y)
+ bodyDef.linearDamping = amortiguacion
+ #userData = { 'color' : self.parent.get_color() }
+ #bodyDef.userData = userData
+ #self.parent.element_count += 1
+
+ body = fisica.crear_cuerpo(bodyDef)
+
+ # Create the Body
+ if not dinamica:
+ densidad = 0
+
+ # Add a shape to the Body
+ circleDef = box2d.b2CircleDef()
+ circleDef.density = densidad
+ circleDef.radius = radio
+ circleDef.restitution = restitucion
+ circleDef.friction = friccion
+
+ body.CreateShape(circleDef)
+ body.SetMassFromShapes()
+
+ self._cuerpo = body
+
+class Rectangulo(Figura):
+ """Representa un rectángulo que puede colisionar con otras figuras.
+
+ Se puede crear un rectángulo independiente y luego asociarlo
+ a un actor de la siguiente forma:
+
+ >>> rect = pilas.fisica.Rectangulo(50, 90, True)
+ >>> actor = pilas.actores.Pingu()
+ >>> actor.imitar(rect)
+ """
+
+ def __init__(self, x, y, ancho, alto, dinamica=True, densidad=1.0,
+ restitucion=0.56, friccion=10.5, amortiguacion=0.1,
+ fisica=None):
+
+ if not fisica:
+ fisica = pilas.mundo.fisica
+
+ bodyDef = box2d.b2BodyDef()
+ bodyDef.position=(x, y)
+ bodyDef.linearDamping = amortiguacion
+
+ #userData = { 'color' : self.parent.get_color() }
+ #bodyDef.userData = userData
+ #self.parent.element_count += 1
+
+ body = fisica.crear_cuerpo(bodyDef)
+
+ # Create the Body
+ if not dinamica:
+ densidad = 0
+
+ # Add a shape to the Body
+ boxDef = box2d.b2PolygonDef()
+
+ boxDef.SetAsBox(ancho/2, alto/2, (0,0), 0)
+ boxDef.density = densidad
+ boxDef.restitution = restitucion
+ boxDef.friction = friccion
+ body.CreateShape(boxDef)
+
+ body.SetMassFromShapes()
+
+ self._cuerpo = body
+
+
+class Poligono(Figura):
+ """Representa un cuerpo poligonal.
+
+ El poligono necesita al menos tres puntos para dibujarse, y cada
+ uno de los puntos se tienen que ir dando en orden de las agujas
+ del relog.
+
+ Por ejemplo:
+
+ >>> pilas.fisica.Poligono([(100, 2), (-50, 0), (-100, 100.0)])
+
+ """
+
+ def __init__(self, puntos, dinamica=True, densidad=1.0,
+ restitucion=0.56, friccion=10.5, amortiguacion=0.1,
+ fisica=None):
+
+ if not fisica:
+ fisica = pilas.mundo.fisica
+
+ bodyDef = box2d.b2BodyDef()
+ bodyDef.position=puntos[0]
+ bodyDef.linearDamping = amortiguacion
+ body = fisica.crear_cuerpo(bodyDef)
+
+ # Create the Body
+ if not dinamica:
+ densidad = 0
+
+ if len(puntos) < 3:
+ raise Exception("Tienes que definir al menos 3 puntos para tener un poligono")
+
+ # Add a shape to the Body
+ poligono_def = box2d.b2PolygonDef()
+ puntos.reverse()
+ poligono_def.setVertices(puntos)
+
+ poligono_def.density = densidad
+ poligono_def.restitution = restitucion
+ poligono_def.friction = friccion
+ #poligono_def.setVertices(puntos)
+ #poligono_def.vertexCount = len(puntos)
+
+ #for indice, punto in enumerate(puntos):
+ # poligono_def.setVertex(indice, punto[0], punto[1])
+ # #poligono_def.vertices[indice] = punto
+
+ body.CreateShape(poligono_def)
+ body.SetMassFromShapes()
+ self._cuerpo = body
+
+class ConstanteDeMovimiento():
+
+ def __init__(self, figura):
+ md = box2d.b2MouseJointDef()
+ mundo = pilas.mundo.fisica.mundo
+ md.body1 = mundo.GetGroundBody()
+ md.body2 = figura._cuerpo
+ md.target = (figura.x, figura.y)
+ md.maxForce = 5000.0 * figura._cuerpo.GetMass()
+
+ try:
+ self.constante = mundo.CreateJoint(md).getAsType()
+ except:
+ self.constante = mundo.CreateJoint(md)
+
+ figura._cuerpo.WakeUp()
+
+ def mover(self, x, y):
+ self.constante.SetTarget((x, y))
+
+ def eliminar(self):
+ pilas.mundo.fisica.mundo.DestroyJoint(self.constante)
+
+class ConstanteDeDistancia():
+ """Representa una distancia fija entre dos figuras.
+
+ Esta constante es útil para representar ejes o barras
+ que sostienen dos cuerpos. Por ejemplo, un eje entre dos
+ ruedas en un automóvil:
+
+ >>> circulo_1 = pilas.fisica.Circulo(-100, 0, 50)
+ >>> circulo_2 = pilas.fisica.Circulo(100, 50, 50)
+ >>> barra = pilas.fisica.ConstanteDeDistancia(circulo_1, circulo_2)
+
+ La distancia que tiene que respetarse en la misma que tienen
+ las figuras en el momento en que se establece la constante.
+ """
+
+ def __init__(self, figura_1, figura_2, fisica=None):
+ if not fisica:
+ fisica = pilas.mundo.fisica
+
+ if not isinstance(figura_1, Figura) or not isinstance(figura_2, Figura):
+ raise Exception("Las dos figuras tienen que ser objetos de la clase Figura.")
+
+ constante = box2d.b2DistanceJointDef()
+ constante.Initialize(figura_1._cuerpo, figura_2._cuerpo, (0,0), (0,0))
+ constante.collideConnected = True
+ self.constante = fisica.mundo.CreateJoint(constante)
+
+
+ def eliminar(self):
+ pilas.mundo.fisica.mundo.DestroyJoint(self.constante_mouse)
+
+def definir_gravedad(x=0, y=-90):
+ pilas.mundo.fisica.mundo.gravity = (x, y)
diff --git a/pilas/fondos.py b/pilas/fondos.py
new file mode 100644
index 0000000..4380ceb
--- /dev/null
+++ b/pilas/fondos.py
@@ -0,0 +1,64 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class Fondo(pilas.actores.Actor):
+
+ def __init__(self, imagen):
+ pilas.actores.Actor.__init__(self, imagen)
+ self.z = 1000
+
+class Volley(Fondo):
+ "Muestra una escena que tiene un fondo de pantalla de paisaje."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/volley.jpg")
+
+class Pasto(Fondo):
+ "Muestra una escena que tiene un fondo de pantalla de paisaje."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/pasto.png")
+
+class Selva(Fondo):
+ "Muestra una escena que tiene un fondo de pantalla de paisaje."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/selva.jpg")
+
+
+class Tarde(Fondo):
+ "Representa una escena de fondo casi naranja."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/tarde.jpg")
+
+
+class Espacio(Fondo):
+ "Es un espacio con estrellas."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/espacio.jpg")
+
+class Noche(Fondo):
+ "Muestra una escena que tiene un fondo de pantalla de paisaje."
+
+ def __init__(self):
+ Fondo.__init__(self, "fondos/noche.jpg")
+
+class Color(Fondo):
+
+ def __init__(self, color):
+ Fondo.__init__(self, "invisible.png")
+ self.color = color
+ self.lienzo = pilas.imagenes.cargar_lienzo()
+
+ def dibujar(self, motor):
+ if self.color:
+ self.lienzo.pintar(motor, self.color)
diff --git a/pilas/fps.py b/pilas/fps.py
new file mode 100644
index 0000000..943ae6a
--- /dev/null
+++ b/pilas/fps.py
@@ -0,0 +1,57 @@
+#import pygame
+import time
+import pygame
+
+class ___FPS:
+
+ def __init__(self, fps, usar_modo_economico):
+ pass
+
+ def actualizar(self):
+ time.sleep(1 / 40.0)
+
+ def obtener_cuadros_por_segundo(self):
+ return 0
+
+class FPS(object):
+ #print "Usando pygame en el modulo fps"
+
+ def __init__(self, fps, usar_modo_economico):
+ self.antes = self.ahora = pygame.time.get_ticks()
+ self.frecuencia = 1000.0 / fps
+ self.t_fps = self.ahora
+ self.rendimiento = 0
+ self.cuadros_por_segundo = "??"
+ self.usar_modo_economico = usar_modo_economico
+
+ def actualizar(self):
+ retorno = 0
+ self.ahora = pygame.time.get_ticks()
+
+ dt = self.ahora - self.antes
+
+ while dt >= self.frecuencia:
+ self.antes += self.frecuencia
+ dt = self.ahora - self.antes
+ retorno += 1
+
+ if self.ahora - self.t_fps > 1000.0:
+ #print self.cuadros_por_segundo
+ self.cuadros_por_segundo = str(self.rendimiento)
+ self.t_fps += 1000.0
+ self.rendimiento = 0
+ else:
+ if self.usar_modo_economico:
+ pygame.time.wait(int(self.frecuencia - dt))
+ else:
+ pygame.time.delay(int(self.frecuencia - dt))
+
+ returno = 1
+
+
+ self.rendimiento += 1
+ return retorno
+
+
+ def obtener_cuadros_por_segundo(self):
+ return self.cuadros_por_segundo
diff --git a/pilas/grupo.py b/pilas/grupo.py
new file mode 100644
index 0000000..466ad46
--- /dev/null
+++ b/pilas/grupo.py
@@ -0,0 +1,48 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+import random
+
+class Grupo(list):
+ """Un grupo es un contenedor que funciona como una lista normal, pero mejorada.
+
+ Los grupos pueden contener actores, y permite que a todos los actores
+ se los pueda tratar como uno.
+
+ Por ejemplo si tienes un contenedor con 20 actores, podrías ampliar
+ el tamaño de todos ellos juntos usando la sentencia::
+
+ grupo = pilas.atajos.fabricar(pilas.actores.Mono, 20)
+ grupo.escala = 2
+
+ """
+
+ def __getattr__(self, attr):
+ """Esta funcion se asegura de que cada vez que se invoque a un metodo
+ del grupo, en realidad, el grupo va a invocar a ese metodo pero
+ en todos sus elementos. Algo asi como un map."""
+
+ def map_a_todos(*k, **kw):
+ for a in self:
+ funcion = getattr(a, attr)
+ funcion(*k, **kw)
+
+ return map_a_todos
+
+ def __setattr__(self, atributo, valor):
+ for a in self:
+ setattr(a, atributo, valor)
+
+ def desordenar(self):
+ for a in self:
+ a.x = random.randint(-300, 300)
+ a.y = random.randint(-200, 200)
+
+ def limpiar(self):
+ eliminar = list(self)
+ for e in eliminar:
+ e.eliminar()
diff --git a/pilas/habilidades.py b/pilas/habilidades.py
new file mode 100644
index 0000000..74d3ba9
--- /dev/null
+++ b/pilas/habilidades.py
@@ -0,0 +1,251 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import random
+import pilas
+
+
+class Habilidad(object):
+
+ def __init__(self, receptor):
+ self.receptor = receptor
+
+ def actualizar(self):
+ pass
+
+ def eliminar(self):
+ pass
+
+class RebotarComoPelota(Habilidad):
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ error = random.randint(-10, 10) / 10.0
+
+ circulo = pilas.fisica.Circulo(receptor.x + error,
+ receptor.y + error,
+ receptor.radio_de_colision)
+ receptor.aprender(pilas.habilidades.Imitar, circulo)
+ self.circulo = circulo
+ receptor.impulsar = self.impulsar
+ receptor.empujar = self.empujar
+
+ def eliminar(self):
+ self.circulo.eliminar()
+
+ def impulsar(self, dx, dy):
+ self.circulo.impulsar(dx, dy)
+
+ def empujar(self, dx, dy):
+ self.circulo.empujar(dx, dy)
+
+class RebotarComoCaja(Habilidad):
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ error = random.randint(-10, 10) / 10.0
+ rectangulo = pilas.fisica.Rectangulo(receptor.x + error,
+ receptor.y + error,
+ receptor.radio_de_colision*2 - 4,
+ receptor.radio_de_colision*2 - 4,
+ )
+ receptor.aprender(pilas.habilidades.Imitar, rectangulo)
+ self.rectangulo = rectangulo
+
+ def eliminar(self):
+ self.rectangulo.eliminar()
+
+
+class ColisionableComoPelota(RebotarComoPelota):
+
+ def __init__(self, receptor):
+ RebotarComoPelota.__init__(self, receptor)
+
+ def actualizar(self):
+ self.figura.body.position.x = self.receptor.x
+ self.figura.body.position.y = self.receptor.y
+
+ def eliminar(self):
+ pilas.fisica.fisica.eliminar(self.figura)
+
+class SeguirAlMouse(Habilidad):
+ "Hace que un actor siga la posición del mouse en todo momento."
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ pilas.eventos.mueve_mouse.connect(self.mover)
+
+ def mover(self, evento):
+ self.receptor.x = evento.x
+ self.receptor.y = evento.y
+
+class AumentarConRueda(Habilidad):
+ "Permite cambiar el tamaño de un actor usando la ruedita scroll del mouse."
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ pilas.eventos.mueve_rueda.connect(self.cambiar_de_escala)
+
+ def cambiar_de_escala(self, evento):
+ self.receptor.escala += (evento.delta / 4.0)
+
+
+class SeguirClicks(Habilidad):
+ "Hace que el actor se coloque la posición del cursor cuando se hace click."
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ pilas.eventos.click_de_mouse.connect(self.moverse_a_este_punto)
+
+ def moverse_a_este_punto(self, evento):
+ self.receptor.x = [evento.x], 0.5
+ self.receptor.y = [evento.y], 0.5
+
+
+class Arrastrable(Habilidad):
+ """Hace que un objeto se pueda arrastrar con el puntero del mouse.
+
+ Cuando comienza a mover al actor se llama al metodo ''comienza_a_arrastrar''
+ y cuando termina llama a ''termina_de_arrastrar''. Estos nombres
+ de metodos se llaman para que puedas personalizar estos eventos, dado
+ que puedes usar polimorfismo para redefinir el comportamiento
+ de estos dos metodos. Observa un ejemplo de esto en
+ el ejemplo ``pilas.ejemplos.Piezas``.
+ """
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ pilas.eventos.click_de_mouse.connect(self.cuando_intenta_arrastrar)
+
+ def cuando_intenta_arrastrar(self, evento):
+ "Intenta mover el objeto con el mouse cuando se pulsa sobre el."
+ if self.receptor.colisiona_con_un_punto(evento.x, evento.y):
+ pilas.eventos.termina_click.connect(self.cuando_termina_de_arrastrar)
+ pilas.eventos.mueve_mouse.connect(self.cuando_arrastra, uid='cuando_arrastra')
+ self.comienza_a_arrastrar()
+
+ def cuando_arrastra(self, evento):
+ "Arrastra el actor a la posicion indicada por el puntero del mouse."
+ if self._el_receptor_tiene_fisica():
+ pilas.mundo.fisica.cuando_mueve_el_mouse(evento.x, evento.y)
+ else:
+ self.receptor.x += evento.dx
+ self.receptor.y += evento.dy
+
+ def cuando_termina_de_arrastrar(self, evento):
+ "Suelta al actor porque se ha soltado el botón del mouse."
+ pilas.eventos.mueve_mouse.disconnect(uid='cuando_arrastra')
+ self.termina_de_arrastrar()
+
+ def comienza_a_arrastrar(self):
+ if self._el_receptor_tiene_fisica():
+ pilas.mundo.fisica.capturar_figura_con_el_mouse(self.receptor.figura)
+
+ def termina_de_arrastrar(self):
+ if self._el_receptor_tiene_fisica():
+ pilas.mundo.fisica.cuando_suelta_el_mouse()
+
+ def _el_receptor_tiene_fisica(self):
+ return hasattr(self.receptor, 'figura')
+
+
+class MoverseConElTeclado(Habilidad):
+ "Hace que un actor cambie de posición con pulsar el teclado."
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ pilas.eventos.actualizar.connect(self.on_key_press)
+
+ def on_key_press(self, evento):
+ velocidad = 5
+ c = pilas.mundo.control
+
+ if c.izquierda:
+ self.receptor.x -= velocidad
+ elif c.derecha:
+ self.receptor.x += velocidad
+
+ if c.arriba:
+ self.receptor.y += velocidad
+ elif c.abajo:
+ self.receptor.y -= velocidad
+
+class PuedeExplotar(Habilidad):
+ "Hace que un actor se pueda hacer explotar invocando al metodo eliminar."
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ receptor.eliminar = self.eliminar_y_explotar
+
+ def eliminar_y_explotar(self):
+ explosion = pilas.actores.Explosion()
+ explosion.x = self.receptor.x
+ explosion.y = self.receptor.y
+ explosion.escala = self.receptor.escala * 2
+ pilas.actores.Actor.eliminar(self.receptor)
+
+
+class SeMantieneEnPantalla(Habilidad):
+ """Se asegura de que el actor regrese a la pantalla si sale.
+
+ Si el actor sale por la derecha de la pantalla, entonces regresa
+ por la izquiera. Si sale por arriba regresa por abajo y asi..."""
+
+ def actualizar(self):
+ # Se asegura de regresar por izquierda y derecha.
+ if self.receptor.derecha < -320:
+ self.receptor.izquierda = 320
+ elif self.receptor.izquierda > 320:
+ self.receptor.derecha = -320
+
+ # Se asegura de regresar por arriba y abajo.
+ if self.receptor.abajo > 240:
+ self.receptor.arriba = -240
+ elif self.receptor.arriba < -240:
+ self.receptor.abajo = 240
+
+
+class PisaPlataformas(Habilidad):
+
+ def __init__(self, receptor):
+ Habilidad.__init__(self, receptor)
+ error = random.randint(-10, 10) / 10.0
+ self.figura = pilas.fisica.fisica.crear_figura_cuadrado(receptor.x + error,
+ receptor.y + error,
+ receptor.radio_de_colision,
+ masa=10,
+ elasticidad=0,
+ friccion=0)
+ self.ultimo_x = receptor.x
+ self.ultimo_y = receptor.y
+
+ def actualizar(self):
+ # Mueve el objeto siempre y cuando no parezca que algo
+ # no fisico (es decir de pymunk) lo ha afectado.
+ self.receptor.x = self.figura.body.position.x
+ self.receptor.y = self.figura.body.position.y
+
+ def eliminar(self):
+ pilas.fisica.fisica.eliminar(self.figura)
+
+class Imitar(Habilidad):
+
+ def __init__(self, receptor, objeto_a_imitar):
+ Habilidad.__init__(self, receptor)
+ self.objeto_a_imitar = objeto_a_imitar
+ receptor.figura = objeto_a_imitar
+
+ def actualizar(self):
+ self.receptor.x = self.objeto_a_imitar.x
+ self.receptor.y = self.objeto_a_imitar.y
+ self.receptor.rotacion = self.objeto_a_imitar.rotacion
+
+ def eliminar(self):
+ if isinstance(self.objeto_a_imitar, pilas.fisica.Figura):
+ self.objeto_a_imitar.eliminar()
+
diff --git a/pilas/imagenes.py b/pilas/imagenes.py
new file mode 100644
index 0000000..163445f
--- /dev/null
+++ b/pilas/imagenes.py
@@ -0,0 +1,77 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+import os
+
+def cargar(ruta):
+ """Intenta cargar la imagen indicada por el argumento ``ruta``.
+
+ Por ejemplo::
+
+ import pilas
+
+ imagen = pilas.imagenes.cargar("mi_archivo.png")
+
+ En caso de éxito retorna el objeto Image, que se puede asignar
+ a un Actor.
+
+ El directorio de búsqueda de la imagen sigue el siguiente orden:
+
+ * primero busca en el directorio actual.
+ * luego en 'data'.
+ * por último en el directorio estándar de la biblioteca.
+
+ En caso de error genera una excepción de tipo IOError.
+ """
+
+ if not pilas.mundo:
+ mensaje = "Tiene que invocar a la funcion ``pilas.iniciar()`` para comenzar."
+ print mensaje
+ raise Exception(mensaje)
+
+ ruta = pilas.utils.obtener_ruta_al_recurso(ruta)
+ return pilas.mundo.motor.cargar_imagen(ruta)
+
+def cargar_grilla(ruta, columnas=1, filas=1):
+ """Representa una grilla de imagenes con varios cuadros de animación.
+
+ Una grilla es un objeto que se tiene que inicializar con la ruta
+ a una imagen, la cantidad de columnas y filas.
+
+ Por ejemplo, si tenemos una grilla con 2 columnas y 3 filas
+ podemos asociarla a un actor de la siguiente manera::
+
+ grilla = pilas.imagenes.cargar_grilla("animacion.png", 2, 3)
+ grilla.asignar(actor)
+
+ Entonces, a partir de ahora nuestro actor muestra solamente un
+ cuadro de toda la grilla.
+
+ Si quieres avanzar la animacion tienes que modificar el objeto
+ grilla y asignarlo nuevamente al actor::
+
+ grilla.avanzar()
+ grilla.asignar(actor)
+ """
+ if not pilas.mundo:
+ mensaje = "Tiene que invocar a la funcion ``pilas.iniciar()`` para comenzar."
+ print mensaje
+ raise Exception(mensaje)
+
+ ruta = pilas.utils.obtener_ruta_al_recurso(ruta)
+ return pilas.mundo.motor.obtener_grilla(ruta, columnas, filas)
+
+def cargar_lienzo():
+ """Representa un rectangulo (inicialmente transparente) para dibujar."""
+ return pilas.mundo.motor.obtener_lienzo()
+
+def cargar_superficie(ancho, alto):
+ return pilas.mundo.motor.obtener_superficie(ancho, alto)
+
+cargar_imagen = cargar
diff --git a/pilas/interfaz/__init__.py b/pilas/interfaz/__init__.py
new file mode 100644
index 0000000..8c9c1e0
--- /dev/null
+++ b/pilas/interfaz/__init__.py
@@ -0,0 +1,14 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+
+from deslizador import Deslizador
+from selector import Selector
+from ingreso_de_texto import IngresoDeTexto
+from lista_seleccion import ListaSeleccion
+from boton import Boton
diff --git a/pilas/interfaz/boton.py b/pilas/interfaz/boton.py
new file mode 100644
index 0000000..28043d4
--- /dev/null
+++ b/pilas/interfaz/boton.py
@@ -0,0 +1,65 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+
+class Boton(pilas.actores.Actor):
+
+ def __init__(self, texto="Sin texto", x=0, y=0, icono=None):
+ pilas.actores.Actor.__init__(self, x=x, y=y)
+ self.texto = texto
+ self._crear_imagenes_de_botones()
+ self.centro = ("centro", "centro")
+ self.funcion = None
+
+ if icono:
+ self.icono = pilas.imagenes.cargar(icono)
+ else:
+ self.icono = None
+
+ pilas.eventos.mueve_mouse.conectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.click_de_mouse.conectar(self.cuando_hace_click)
+
+ def conectar(self, funcion):
+ self.funcion = funcion
+
+ def _crear_imagenes_de_botones(self):
+ "Genera las 3 imagenes de los botones."
+ ancho, alto = pilas.utils.obtener_area_de_texto(self.texto)
+ tema = pilas.imagenes.cargar("boton/tema.png")
+
+ self.imagen_normal = self._crear_imagen(tema, self.texto, ancho, 0)
+ self.imagen_sobre = self._crear_imagen(tema, self.texto, ancho, 103)
+ self.imagen_click = self._crear_imagen(tema, self.texto, ancho, 205)
+
+ self.imagen = self.imagen_normal
+
+ def cuando_mueve_el_mouse(self, evento):
+ if self.colisiona_con_un_punto(evento.x, evento.y):
+ self.imagen = self.imagen_sobre
+ else:
+ self.imagen = self.imagen_normal
+
+ def cuando_hace_click(self, evento):
+ if self.imagen == self.imagen_sobre:
+ self.imagen = self.imagen_click
+
+ if self.funcion:
+ self.funcion()
+
+ def _crear_imagen(self, tema, texto, ancho, dx):
+ "Genera una imagen de superficie de boton."
+ imagen = pilas.imagenes.cargar_superficie(20 + ancho, 30)
+ imagen.pintar_parte_de_imagen(tema, dx, 0, 5, 25, 0, 0)
+
+ for x in range(1, ancho + 20, 5):
+ imagen.pintar_parte_de_imagen(tema, dx + 5, 0, 5, 25, x, 0)
+
+ imagen.pintar_parte_de_imagen(tema, dx + 75, 0, 5, 25, ancho + 15, 0)
+ imagen.texto(texto, 10, 17)
+ return imagen
diff --git a/pilas/interfaz/deslizador.py b/pilas/interfaz/deslizador.py
new file mode 100644
index 0000000..37322ef
--- /dev/null
+++ b/pilas/interfaz/deslizador.py
@@ -0,0 +1,87 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+#
+# Deslizador creado por Pablo Garrido
+
+import pilas
+from pilas.actores import Actor
+
+
+class Deslizador(Actor):
+
+ def __init__(self, x=0, y=0, ruta_barra = 'interfaz/barra.png',
+ ruta_deslizador = 'interfaz/deslizador.png'):
+
+ self.deslizador = None
+ Actor.__init__(self, ruta_barra, x=x, y=y)
+ self.deslizador = Actor(ruta_deslizador, self.x, self.y)
+ self.centro = ('izquierda', 'centro')
+
+ self.click = False
+
+ pilas.eventos.click_de_mouse.conectar(self.click_del_mouse)
+ pilas.eventos.mueve_mouse.conectar(self.movimiento_del_mouse)
+ pilas.eventos.termina_click.conectar(self.termino_del_click)
+
+ self.progreso = 0
+ self.posicion_relativa_x = 0
+
+ self.funciones = []
+
+ # establecemos posicion inicial
+ self.x = x
+ self.y = y
+
+ def set_transparencia(self, nuevo_valor):
+ self.transparencia = nuevo_valor
+ self.deslizador.transparencia = nuevo_valor
+
+ def definir_posicion(self, x, y):
+ self.limite_izq = self.x
+ self.limite_der = self.x + self.obtener_ancho()
+
+ self._actor.definir_posicion(x, y)
+ if self.deslizador:
+ self.deslizador.definir_posicion(x + self.posicion_relativa_x, y)
+
+ def conectar(self, f):
+ self.funciones.append(f)
+
+ def desconectar(self, f):
+ self.funciones.remove(f)
+
+ def ejecutar_funciones(self, valor):
+ for i in self.funciones:
+ i(valor)
+
+ def click_del_mouse(self, click):
+
+ if self.deslizador.colisiona_con_un_punto(click.x, click.y):
+ self.click = True
+
+ def movimiento_del_mouse(self, movimiento):
+ if self.click == True:
+ ancho = self.obtener_ancho()
+ factor = (self.deslizador.x + (ancho - abs(self.x))) / ancho - 1
+ self.progreso = factor
+
+ self.ejecutar_funciones(factor)
+
+ self.deslizador.x = movimiento.x
+
+ if self.deslizador.x <= self.limite_izq:
+ self.deslizador.x = self.limite_izq
+
+ elif self.deslizador.x >= self.limite_der:
+ self.deslizador.x = self.limite_der
+
+ self.posicion_relativa_x = self.deslizador.x - self.x
+
+
+ def termino_del_click(self, noclick):
+ self.click = False
diff --git a/pilas/interfaz/ingreso_de_texto.py b/pilas/interfaz/ingreso_de_texto.py
new file mode 100644
index 0000000..418c932
--- /dev/null
+++ b/pilas/interfaz/ingreso_de_texto.py
@@ -0,0 +1,86 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+import re
+
+class IngresoDeTexto(pilas.actores.Actor):
+
+ def __init__(self, texto_inicial="", x=0, y=0, ancho=300, limite_de_caracteres=20, icono=None):
+ pilas.actores.Actor.__init__(self, x=x, y=y)
+ self.texto = texto_inicial
+ self.cursor = ""
+ self._cargar_lienzo(ancho)
+
+ if icono:
+ self.icono = pilas.imagenes.cargar(icono)
+ else:
+ self.icono = None
+
+ self.imagen_caja = pilas.imagenes.cargar("interfaz/caja.png")
+ self.centro = ("centro", "centro")
+ self._actualizar_imagen()
+ self.limite_de_caracteres = limite_de_caracteres
+ self.cualquier_caracter()
+
+ pilas.eventos.pulsa_tecla.conectar(self.cuando_pulsa_una_tecla)
+ pilas.mundo.agregar_tarea_siempre(0.40, self._actualizar_cursor)
+
+ def _actualizar_cursor(self):
+ if self.cursor == "":
+ self.cursor = "_"
+ else:
+ self.cursor = ""
+
+ self._actualizar_imagen()
+ return True
+
+ def cualquier_caracter(self):
+ self.caracteres_permitidos = re.compile(".*")
+
+ def solo_numeros(self):
+ self.caracteres_permitidos = re.compile("\d+")
+
+ def solo_letras(self):
+ self.caracteres_permitidos = re.compile("[a-z]+")
+
+ def cuando_pulsa_una_tecla(self, evento):
+ if evento.codigo == '\x08' or evento.texto == '\x08':
+ # Indica que se quiere borrar un caracter
+ self.texto = self.texto[:-1]
+ else:
+ if len(self.texto) < self.limite_de_caracteres:
+ nuevo_texto = self.texto + evento.texto
+
+ if (self.caracteres_permitidos.match(evento.texto)):
+ self.texto = self.texto + evento.texto
+ else:
+ print "Rechazando el ingreso del caracter:", evento.texto
+ else:
+ print "Rechazando caracter por llegar al limite."
+
+ self._actualizar_imagen()
+
+ def _cargar_lienzo(self, ancho):
+ self.imagen = pilas.imagenes.cargar_superficie(ancho, 30)
+
+ def _actualizar_imagen(self):
+ ancho = self.imagen_caja.ancho()
+ alto = self.imagen_caja.alto()
+ self.imagen.pintar_parte_de_imagen(self.imagen_caja, 0, 0, 40, ancho, 0, 0)
+
+ if self.icono:
+ dx = 20
+ self.imagen.pintar_parte_de_imagen(self.icono, 0, 0, 40, ancho, 7, 7)
+ else:
+ dx = 0
+
+ for x in range(40, self.imagen.ancho() - 40):
+ self.imagen.pintar_parte_de_imagen(self.imagen_caja, ancho - 40, 0, 40, alto, x, 0)
+
+ self.imagen.texto(self.texto + self.cursor, 15 + dx, 20)
diff --git a/pilas/interfaz/lista_seleccion.py b/pilas/interfaz/lista_seleccion.py
new file mode 100644
index 0000000..4feb9fd
--- /dev/null
+++ b/pilas/interfaz/lista_seleccion.py
@@ -0,0 +1,53 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pilas
+from pilas.actores import Actor
+
+class ListaSeleccion(Actor):
+
+ def __init__(self, opciones, funcion_a_ejecutar=None, x=0, y=0):
+ Actor.__init__(self, x=x, y=y)
+ self.opciones = opciones
+ self.funcion_a_ejecutar = funcion_a_ejecutar
+
+ ancho, alto = pilas.mundo.motor.obtener_area_de_texto("\n".join(opciones))
+ self.imagen = pilas.imagenes.cargar_superficie(int(ancho + 35), int(alto + 5))
+
+ self._pintar_opciones()
+
+ pilas.eventos.mueve_mouse.conectar(self.cuando_mueve_el_mouse)
+ pilas.eventos.click_de_mouse.conectar(self.cuando_hace_click_con_el_mouse)
+ self.centro = ("centro", "centro")
+
+ def _pintar_opciones(self, pinta_indice_opcion=None):
+ self.imagen.pintar(pilas.colores.blanco)
+
+ if pinta_indice_opcion != None:
+ self.imagen.rectangulo(0, pinta_indice_opcion * 19, self.imagen.ancho(), 17, relleno=True, color=pilas.colores.naranja)
+
+ for indice, opcion in enumerate(self.opciones):
+ self.imagen.texto(opcion, 15, y=12 + indice * 20, color=pilas.colores.negro)
+
+ def cuando_mueve_el_mouse(self, evento):
+ if self.colisiona_con_un_punto(evento.x, evento.y):
+ opcion_seleccionada = self._detectar_opcion_bajo_el_mouse(evento)
+ self._pintar_opciones(opcion_seleccionada)
+
+ def cuando_hace_click_con_el_mouse(self, evento):
+ if self.colisiona_con_un_punto(evento.x, evento.y):
+ opcion = self._detectar_opcion_bajo_el_mouse(evento)
+ if self.funcion_a_ejecutar:
+ self.funcion_a_ejecutar(self.opciones[opcion])
+ else:
+ print "Cuidado, no has definido funcion a ejecutar en la lista de seleccion."
+
+ def _detectar_opcion_bajo_el_mouse(self, evento):
+ opcion = int((self.arriba - evento.y ) / 20)
+ if opcion in range(0, len(self.opciones)):
+ return opcion
diff --git a/pilas/interfaz/selector.py b/pilas/interfaz/selector.py
new file mode 100644
index 0000000..3d15663
--- /dev/null
+++ b/pilas/interfaz/selector.py
@@ -0,0 +1,64 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+#
+# Deslizador creado por Pablo Garrido
+
+import pilas
+
+class Selector(pilas.actores.Actor):
+
+ def __init__(self, texto, x=0, y=0, ancho=200):
+ pilas.actores.Actor.__init__(self, x=x, y=y)
+
+ self.texto = texto
+ self._cargar_lienzo(ancho)
+ self._cargar_imagenes()
+ self.funcion_de_respuesta = None
+
+ self.deseleccionar()
+ pilas.eventos.click_de_mouse.conectar(self.detection_click_mouse)
+
+ def _cargar_imagenes(self):
+ self.imagen_selector = pilas.imagenes.cargar("interfaz/selector.png")
+ self.imagen_selector_seleccionado = pilas.imagenes.cargar("interfaz/selector_seleccionado.png")
+
+ def _cargar_lienzo(self, ancho):
+ self.imagen = pilas.imagenes.cargar_superficie(ancho, 29)
+
+ def pintar_texto(self):
+ self.imagen.texto(self.texto, 35, 20)
+
+ def deseleccionar(self):
+ self.seleccionado = False
+ self.imagen.limpiar()
+ self.imagen.pintar_imagen(self.imagen_selector)
+ self.pintar_texto()
+ self.centro = ("centro", "centro")
+
+ def seleccionar(self):
+ self.seleccionado = True
+ self.imagen.limpiar()
+ self.imagen.pintar_imagen(self.imagen_selector_seleccionado)
+ self.pintar_texto()
+ self.centro = ("centro", "centro")
+
+ def detection_click_mouse(self, click):
+ if self.colisiona_con_un_punto(click.x, click.y):
+ self.alternar_seleccion()
+
+ def alternar_seleccion(self):
+ if self.seleccionado:
+ self.deseleccionar()
+ else:
+ self.seleccionar()
+
+ if self.funcion_de_respuesta:
+ self.funcion_de_respuesta(self.seleccionado)
+
+ def definir_accion(self, funcion):
+ self.funcion_de_respuesta = funcion
diff --git a/pilas/interpolaciones.py b/pilas/interpolaciones.py
new file mode 100644
index 0000000..4f52973
--- /dev/null
+++ b/pilas/interpolaciones.py
@@ -0,0 +1,86 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+class Interpolacion(object):
+ """Representa una interpolacion, que pasa por varios puntos clave.
+
+ Las interpolacione se utilizan para realizar movimientos de
+ actores en la pantalla. O simplemente para cambiar el
+ estado de un actor de un punto a otro, por ejemplo, de 0 a 360
+ grados de manera gradual.
+
+ Todo objeto de interpolaciones se puede asignar directamente a
+ una propiedad de un actor. Por ejemplo:
+
+ actor.rotation = pilas.interpolations.Lineal(400)
+
+ note que hay un atajo para usar estos objetos, es mejor
+ utilizar directamente una sentencias como la que sigue::
+
+ actor.rotation = pilas.interpolate(360)
+ """
+ pass
+
+
+class Lineal(Interpolacion):
+ "Representa una interpolación lineal."
+
+ def __init__(self, values, duration, delay):
+ """Inicializa la interpolación.
+
+ ``values`` tiene que ser una lista con todos los puntos
+ por los que se quiere adoptar valores y ``duration`` es la cantidad
+ de segundos que deben tomarse para realizar la interpolación.
+ """
+ self.values = values
+ self.duration = duration
+ self.delay = delay
+
+ def __neg__(self):
+ "Retorna la interpolación inversa a la original."
+ new_values = list(self.values)
+ new_values.reverse()
+ return Lineal(new_values, self.duration, self.delay)
+
+ def apply(self, target, function):
+ """Aplica la interpolación a un actor usando un método.
+
+ Esta funcionalidad se utiliza para que toda interpolación
+ se pueda acoplar a un actor.
+
+ La idea es contar con la interpolación, un actor y luego
+ ponerla en funcionamiento::
+
+ mi_interpolacion.apply(mono, set_rotation)
+
+ de esta forma los dos objetos están y seguirán estando
+ desacoplados."""
+
+ import pilas
+
+ # Tiempo que se debe invertir para hacer cada interpolacion
+ # individual.
+ step = self.duration / float(len(self.values))
+ step *= 1000.0
+
+ # En base a la funcion busca el getter que le dara
+ # el valor inicial.
+ getter = function.replace('set_', 'get_')
+ function_to_get_value = getattr(target, getter)
+ fist_value = function_to_get_value()
+
+ # Le indica al objeto que tiene que hacer para cumplir
+ # con cada paso de la interpolacion.
+ for index, value in enumerate(self.values):
+ pilas.mundo.tweener.addTweenNoArgs(target, function=function,
+ initial_value=fist_value,
+ value=value,
+ tweenDelay=self.delay * 1000.0 + (index * step),
+ tweenTime=step)
+ # El siguiente valor inicial sera el que ha alcanzado.
+ fist_value = value
diff --git a/pilas/lienzo.py b/pilas/lienzo.py
new file mode 100644
index 0000000..5ed9bdd
--- /dev/null
+++ b/pilas/lienzo.py
@@ -0,0 +1,6 @@
+from pilas.actores.pizarra import PizarraAbstracta
+
+# TODO: eliminar la pizarraAbstacta y reemplazarla
+# por este lienzo.
+
+Lienzo = PizarraAbstracta \ No newline at end of file
diff --git a/pilas/motores/__init__.py b/pilas/motores/__init__.py
new file mode 100644
index 0000000..57d6284
--- /dev/null
+++ b/pilas/motores/__init__.py
@@ -0,0 +1,9 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import motor
diff --git a/pilas/motores/motor.py b/pilas/motores/motor.py
new file mode 100644
index 0000000..76c1d26
--- /dev/null
+++ b/pilas/motores/motor.py
@@ -0,0 +1,75 @@
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+
+def abstract():
+ raise Exception("Tienes que re-definir este metodo.")
+
+class Motor(object):
+
+ def __init__(self):
+ pass
+
+ def obtener_actor(self, imagen, x, y):
+ abstract()
+
+ def obtener_texto(self, texto, x, y):
+ abstract()
+
+ def obtener_canvas(self, ancho, alto):
+ abstract()
+
+ def obtener_grilla(self, ruta, columnas, filas):
+ abstract()
+
+ def crear_ventana(self, ancho, alto, titulo):
+ abstract()
+
+ def ocultar_puntero_del_mouse(self):
+ abstract()
+
+ def mostrar_puntero_del_mouse(self):
+ abstract()
+
+ def cerrar_ventana(self):
+ abstract()
+
+ def dibujar_circulo(self, x, y, radio, color, color_borde):
+ abstract()
+
+ def pulsa_tecla(self, tecla):
+ abstract()
+
+ def centrar_ventana(self):
+ abstract()
+
+ def procesar_y_emitir_eventos(self):
+ abstract()
+
+ def procesar_evento_teclado(self, event):
+ abstract()
+
+ def definir_centro_de_la_camara(self, x, y):
+ abstract()
+
+ def obtener_centro_de_la_camara(self):
+ abstract()
+
+ def pintar(self, color):
+ abstract()
+
+ def cargar_sonido(self, ruta):
+ abstract()
+
+ def cargar_imagen(self, ruta):
+ abstract()
+
+ def obtener_imagen_cairo(self, imagen):
+ abstract()
+
+ def ejecutar_bucle_principal(self, mundo, ignorar_errores):
+ abstract()
diff --git a/pilas/motores/motor_qt.py b/pilas/motores/motor_qt.py
new file mode 100644
index 0000000..b416031
--- /dev/null
+++ b/pilas/motores/motor_qt.py
@@ -0,0 +1,705 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import os
+import sys
+import copy
+from PyQt4 import QtGui, QtCore
+from PyQt4.QtGui import QWidget
+
+try:
+ from PyQt4 import QtOpenGL
+ from PyQt4.QtOpenGL import QGLWidget
+except ImportError:
+ QGLWidget = object
+ print "No se encuentra soporte OpenGL en este equipo."
+
+
+import motor
+from pilas import imagenes
+from pilas import actores
+from pilas import eventos
+from pilas import utils
+from pilas import depurador
+
+from pilas import fps
+from pilas import simbolos
+from pilas import colores
+
+
+class BaseActor(object):
+
+ def __init__(self):
+ self._rotacion = 0
+ self._transparencia = 0
+ self.centro_x = 0
+ self.centro_y = 0
+ self._escala_x = 1
+ self._escala_y = 1
+ self._espejado = False
+ self.fijo = 0
+
+ def definir_centro(self, x, y):
+ self.centro_x = x
+ self.centro_y = y
+
+ def obtener_posicion(self):
+ return self.x, self.y
+
+ def definir_posicion(self, x, y):
+ self.x, self.y = x, y
+
+ def obtener_escala(self):
+ return self._escala_x
+
+ def definir_escala(self, s):
+ self._escala_x = s
+ self._escala_y = s
+
+ def definir_escala_x(self, s):
+ self._escala_x = s
+
+ def definir_escala_y(self, s):
+ self._escala_y = s
+
+ def definir_transparencia(self, nuevo_valor):
+ self._transparencia = nuevo_valor
+
+ def obtener_transparencia(self):
+ return self._transparencia
+
+ def obtener_rotacion(self):
+ return self._rotacion
+
+ def definir_rotacion(self, r):
+ self._rotacion = r
+
+ def set_espejado(self, espejado):
+ self._espejado = espejado
+
+class QtImagen(object):
+
+ def __init__(self, ruta):
+ self.ruta_original = ruta
+ self._imagen = QtGui.QPixmap(ruta)
+
+ def ancho(self):
+ return self._imagen.size().width()
+
+ def alto(self):
+ return self._imagen.size().height()
+
+ def centro(self):
+ "Retorna una tupla con la coordenada del punto medio del la imagen."
+ return (self.ancho()/2, self.alto()/2)
+
+ def avanzar(self):
+ pass
+
+ def dibujar(self, motor, x, y, dx=0, dy=0, escala_x=1, escala_y=1, rotacion=0, transparencia=0):
+ """Dibuja la imagen sobre la ventana que muestra el motor.
+
+ x, y: indican la posicion dentro del mundo.
+ dx, dy: es el punto centro de la imagen (importante para rotaciones).
+ escala_x, escala_yindican cambio de tamano (1 significa normal).
+ rotacion: angulo de inclinacion en sentido de las agujas del reloj.
+ """
+
+ motor.canvas.save()
+ centro_x, centro_y = motor.centro_fisico()
+ motor.canvas.translate(x + centro_x, centro_y - y)
+ motor.canvas.rotate(rotacion)
+ motor.canvas.scale(escala_x, escala_y)
+
+ if transparencia:
+ motor.canvas.setOpacity(1 - transparencia/100.0)
+
+ self._dibujar_pixmap(motor, -dx, -dy)
+ motor.canvas.restore()
+
+ def _dibujar_pixmap(self, motor, x, y):
+ motor.canvas.drawPixmap(x, y, self._imagen)
+
+ def __str__(self):
+ nombre_imagen = os.path.basename(self.ruta_original)
+ return "<Imagen del archivo '%s'>" %(nombre_imagen)
+
+
+class QtGrilla(QtImagen):
+
+ """Representa una grilla regular, que se utiliza en animaciones.
+
+ La grilla regular se tiene que crear indicando la cantidad
+ de filas y columnas. Una vez definida se puede usar como
+ una imagen normal, solo que tiene dos metodos adicionales
+ para ``definir_cuadro`` y ``avanzar`` el cuadro actual.
+ """
+
+ def __init__(self, ruta, columnas=1, filas=1):
+ QtImagen.__init__(self, ruta)
+ self.cantidad_de_cuadros = columnas * filas
+ self.columnas = columnas
+ self.filas = filas
+ self.cuadro_ancho = QtImagen.ancho(self) / columnas
+ self.cuadro_alto = QtImagen.alto(self) / filas
+ self.definir_cuadro(0)
+
+ def ancho(self):
+ return self.cuadro_ancho
+
+ def alto(self):
+ return self.cuadro_alto
+
+ def _dibujar_pixmap(self, motor, x, y):
+ motor.canvas.drawPixmap(x, y, self._imagen, self.dx, self.dy,
+ self.cuadro_ancho, self.cuadro_alto)
+
+ def definir_cuadro(self, cuadro):
+ self._cuadro = cuadro
+
+ frame_col = cuadro % self.columnas
+ frame_row = cuadro / self.columnas
+
+ self.dx = frame_col * self.cuadro_ancho
+ self.dy = frame_row * self.cuadro_alto
+
+ def avanzar(self):
+ ha_reiniciado = False
+ cuadro_actual = self._cuadro + 1
+
+ if cuadro_actual >= self.cantidad_de_cuadros:
+ cuadro_actual = 0
+ ha_reiniciado = True
+
+ self.definir_cuadro(cuadro_actual)
+ return ha_reiniciado
+
+ def obtener_cuadro(self):
+ return self._cuadro
+
+ def dibujarse_sobre_una_pizarra(self, pizarra, x, y):
+ pizarra.pintar_parte_de_imagen(self, self.dx, self.dy, self.cuadro_ancho, self.cuadro_alto, x, y)
+
+class QtTexto(QtImagen):
+
+ def __init__(self, texto, magnitud, motor):
+ self._ancho, self._alto = motor.obtener_area_de_texto(texto, magnitud)
+
+ def _dibujar_pixmap(self, motor, dx, dy):
+ nombre_de_fuente = motor.canvas.font().family()
+ fuente = QtGui.QFont(nombre_de_fuente, self.magnitud)
+ metrica = QtGui.QFontMetrics(fuente)
+
+ r, g, b, a = self.color.obtener_componentes()
+ motor.canvas.setPen(QtGui.QColor(r, g, b))
+ motor.canvas.setFont(fuente)
+ lines = self.texto.split('\n')
+
+ for line in lines:
+ motor.canvas.drawText(dx, dy + self._alto, line)
+ dy += metrica.height()
+
+ def ancho(self):
+ return self._ancho
+
+ def alto(self):
+ return self._alto
+
+
+class QtLienzo(QtImagen):
+
+ def __init__(self):
+ pass
+
+ def texto(self, motor, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
+ "Imprime un texto respespetando el desplazamiento de la camara."
+ self.texto_absoluto(motor, cadena, x, y, magnitud, fuente, color)
+
+ def texto_absoluto(self, motor, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
+ "Imprime un texto sin respetar al camara."
+ x, y = utils.hacer_coordenada_pantalla_absoluta(x, y)
+
+ r, g, b, a = color.obtener_componentes()
+ motor.canvas.setPen(QtGui.QColor(r, g, b))
+
+ if not fuente:
+ fuente = motor.canvas.font().family()
+
+ motor.canvas.setFont(QtGui.QFont(fuente, magnitud))
+ motor.canvas.drawText(x, y, cadena)
+
+ def pintar(self, motor, color):
+ r, g, b, a = color.obtener_componentes()
+ ancho, alto = motor.obtener_area()
+ motor.canvas.fillRect(0, 0, ancho, alto, QtGui.QColor(r, g, b))
+
+ def linea(self, motor, x0, y0, x1, y1, color=colores.negro, grosor=1):
+ x0, y0 = utils.hacer_coordenada_pantalla_absoluta(x0, y0)
+ x1, y1 = utils.hacer_coordenada_pantalla_absoluta(x1, y1)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ motor.canvas.setPen(pen)
+ motor.canvas.drawLine(x0, y0, x1, y1)
+
+ def poligono(self, motor, puntos, color=colores.negro, grosor=1, cerrado=False):
+ x, y = puntos[0]
+ if cerrado:
+ puntos.append((x, y))
+
+ for p in puntos[1:]:
+ nuevo_x, nuevo_y = p
+ self.linea(motor, x, y, nuevo_x, nuevo_y, color, grosor)
+ x, y = nuevo_x, nuevo_y
+
+
+ def cruz(self, motor, x, y, color=colores.negro, grosor=1):
+ t = 3
+ self.linea(motor, x - t, y - t, x + t, y + t, color, grosor)
+ self.linea(motor, x + t, y - t, x - t, y + t, color, grosor)
+
+ def circulo(self, motor, x, y, radio, color=colores.negro, grosor=1):
+ x, y = utils.hacer_coordenada_pantalla_absoluta(x, y)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ motor.canvas.setPen(pen)
+ motor.canvas.drawEllipse(x -radio, y-radio, radio*2, radio*2)
+
+ def rectangulo(self, motor, x, y, ancho, alto, color=colores.negro, grosor=1):
+ x, y = utils.hacer_coordenada_pantalla_absoluta(x, y)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ motor.canvas.setPen(pen)
+ motor.canvas.drawRect(x, y, ancho, alto)
+
+class QtSuperficie(QtImagen):
+
+ def __init__(self, ancho, alto):
+ self._imagen = QtGui.QPixmap(ancho, alto)
+ self._imagen.fill(QtGui.QColor(255, 255, 255, 0))
+ self.canvas = QtGui.QPainter()
+
+ def pintar(self, color):
+ r, g, b, a = color.obtener_componentes()
+ self._imagen.fill(QtGui.QColor(r, g, b, a))
+
+ def pintar_parte_de_imagen(self, imagen, origen_x, origen_y, ancho, alto, x, y):
+ self.canvas.begin(self._imagen)
+ self.canvas.drawPixmap(x, y, imagen._imagen, origen_x, origen_y, ancho, alto)
+ self.canvas.end()
+
+ def pintar_imagen(self, imagen, x=0, y=0):
+ self.pintar_parte_de_imagen(imagen, 0, 0, imagen.ancho(), imagen.alto(), x, y)
+
+ def texto(self, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
+ self.canvas.begin(self._imagen)
+ r, g, b, a = color.obtener_componentes()
+ self.canvas.setPen(QtGui.QColor(r, g, b))
+ dx = x
+ dy = y
+
+ if not fuente:
+ fuente = self.canvas.font().family()
+
+ font = QtGui.QFont(fuente, magnitud)
+ self.canvas.setFont(font)
+ metrica = QtGui.QFontMetrics(font)
+
+ for line in cadena.split('\n'):
+ self.canvas.drawText(dx, dy, line)
+ dy += metrica.height()
+
+ self.canvas.end()
+
+ def circulo(self, x, y, radio, color=colores.negro, relleno=False, grosor=1):
+ self.canvas.begin(self._imagen)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ self.canvas.setPen(pen)
+
+ if relleno:
+ self.canvas.setBrush(color)
+
+ self.canvas.drawEllipse(x -radio, y-radio, radio*2, radio*2)
+ self.canvas.end()
+
+ def rectangulo(self, x, y, ancho, alto, color=colores.negro, relleno=False, grosor=1):
+ self.canvas.begin(self._imagen)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ self.canvas.setPen(pen)
+
+ if relleno:
+ self.canvas.setBrush(color)
+
+ self.canvas.drawRect(x, y, ancho, alto)
+ self.canvas.end()
+
+ def linea(self, x, y, x2, y2, color=colores.negro, grosor=1):
+ self.canvas.begin(self._imagen)
+
+ r, g, b, a = color.obtener_componentes()
+ color = QtGui.QColor(r, g, b)
+ pen = QtGui.QPen(color, grosor)
+ self.canvas.setPen(pen)
+
+ self.canvas.drawLine(x, y, x2, y2)
+ self.canvas.end()
+
+ def poligono(self, puntos, color, grosor, cerrado=False):
+ x, y = puntos[0]
+
+ if cerrado:
+ puntos.append((x, y))
+
+ for p in puntos[1:]:
+ nuevo_x, nuevo_y = p
+ self.linea(x, y, nuevo_x, nuevo_y, color, grosor)
+ x, y = nuevo_x, nuevo_y
+
+ def dibujar_punto(self, x, y, color=colores.negro):
+ self.circulo(x, y, 3, color=color, relleno=True)
+
+ def limpiar(self):
+ self._imagen.fill(QtGui.QColor(0, 0, 0, 0))
+
+class QtActor(BaseActor):
+
+ def __init__(self, imagen="sin_imagen.png", x=0, y=0):
+
+ if isinstance(imagen, str):
+ self.imagen = imagenes.cargar(imagen)
+ else:
+ self.imagen = imagen
+
+ self.x = x
+ self.y = y
+ BaseActor.__init__(self)
+
+ def definir_imagen(self, imagen):
+ # permite que varios actores usen la misma grilla.
+ if isinstance(imagen, QtGrilla):
+ self.imagen = copy.copy(imagen)
+ else:
+ self.imagen = imagen
+
+ def obtener_imagen(self):
+ return self.imagen
+
+ def dibujar(self, motor):
+ escala_x, escala_y = self._escala_x, self._escala_y
+
+ if self._espejado:
+ escala_x *= -1
+
+ if not self.fijo:
+ x = self.x - motor.camara_x
+ y = self.y - motor.camara_y
+ else:
+ x = self.x
+ y = self.y
+
+ self.imagen.dibujar(motor, x, y,
+ self.centro_x, self.centro_y,
+ escala_x, escala_y, self._rotacion, self._transparencia)
+
+class QtSonido:
+
+ def __init__(self, ruta):
+ import pygame
+ pygame.mixer.init()
+ pygame.mixer.init()
+ self.sonido = pygame.mixer.Sound(ruta)
+
+ def reproducir(self):
+ # TODO: quitar esta nota...
+ # print "Usando pygame para reproducir sonido"
+ self.sonido.play()
+
+class QtBase(motor.Motor):
+
+ #app = QtGui.QApplication([])
+
+ def __init__(self):
+ motor.Motor.__init__(self)
+ self.canvas = QtGui.QPainter()
+ self.setMouseTracking(True)
+ self.fps = fps.FPS(60, True)
+ self.pausa_habilitada = False
+ self.depurador = depurador.Depurador(self.obtener_lienzo(), self.fps)
+ self.mouse_x = 0
+ self.mouse_y = 0
+ self.camara_x = 0
+ self.camara_y = 0
+
+ def iniciar_ventana(self, ancho, alto, titulo, pantalla_completa):
+ self.ancho = ancho
+ self.alto = alto
+ self.ancho_original = ancho
+ self.alto_original = alto
+ self.titulo = titulo
+ self.centrar_ventana()
+ self.setWindowTitle(self.titulo)
+
+ if pantalla_completa:
+ self.showFullScreen()
+ else:
+ self.show()
+
+ # Activa la invocacion al evento timerEvent.
+ self.startTimer(1000/60.0)
+
+ def pantalla_completa(self):
+ self.showFullScreen()
+
+ def pantalla_modo_ventana(self):
+ self.showNormal()
+
+ def esta_en_pantalla_completa(self):
+ return self.isFullScreen()
+
+ def alternar_pantalla_completa(self):
+ """Permite cambiar el modo de video.
+
+ Si está en modo ventana, pasa a pantalla completa y viceversa.
+ """
+ if self.esta_en_pantalla_completa():
+ self.pantalla_modo_ventana()
+ else:
+ self.pantalla_completa()
+
+ def centro_fisico(self):
+ "Centro de la ventana para situar el punto (0, 0)"
+ return self.ancho_original/2, self.alto_original/2
+
+ def obtener_area(self):
+ return (self.ancho_original, self.alto_original)
+
+ def centrar_ventana(self):
+ escritorio = QtGui.QDesktopWidget().screenGeometry()
+ self.setGeometry(
+ (escritorio.width()-self.ancho)/2,
+ (escritorio.height()-self.alto)/2, self.ancho, self.alto)
+
+ def obtener_actor(self, imagen, x, y):
+ return QtActor(imagen, x, y)
+
+ def obtener_texto(self, texto, magnitud):
+ return QtTexto(texto, magnitud, self)
+
+ def obtener_grilla(self, ruta, columnas, filas):
+ return QtGrilla(ruta, columnas, filas)
+
+ def actualizar_pantalla(self):
+ self.ventana.update()
+
+ def definir_centro_de_la_camara(self, x, y):
+ self.camara_x = x
+ self.camara_y = y
+
+ def obtener_centro_de_la_camara(self):
+ return (self.camara_x, self.camara_y)
+
+ def cargar_sonido(self, ruta):
+ return QtSonido(ruta)
+
+ def cargar_imagen(self, ruta):
+ return QtImagen(ruta)
+
+ def obtener_lienzo(self):
+ return QtLienzo()
+
+ def obtener_superficie(self, ancho, alto):
+ return QtSuperficie(ancho, alto)
+
+ def ejecutar_bucle_principal(self, mundo, ignorar_errores):
+ #sys.exit(self.app.exec_())
+ pass
+
+ def paintEvent(self, event):
+ self.canvas.begin(self)
+
+ self.canvas.setClipping(True)
+ self.canvas.setClipRect(0, 0, self.alto * self.ancho_original / self.alto_original, self.alto)
+
+ alto = self.alto / float(self.alto_original)
+ self.canvas.scale(alto, alto)
+
+
+ self.canvas.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, False)
+ self.canvas.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)
+ self.canvas.setRenderHint(QtGui.QPainter.Antialiasing, False)
+
+ self.depurador.comienza_dibujado(self)
+
+ for actor in actores.todos:
+ try:
+ actor.dibujar(self)
+ except Exception as e:
+ print e
+ actor.eliminar()
+
+ self.depurador.dibuja_al_actor(self, actor)
+
+ self.depurador.termina_dibujado(self)
+ self.canvas.end()
+
+ def timerEvent(self, event):
+
+ if not self.pausa_habilitada:
+ try:
+ self.realizar_actualizacion_logica()
+ except Exception as e:
+ print e
+
+ # Invoca el dibujado de la pantalla.
+ self.update()
+
+
+ def realizar_actualizacion_logica(self):
+ for x in range(self.fps.actualizar()):
+ if not self.pausa_habilitada:
+ eventos.actualizar.send("Qt::timerEvent")
+
+ for actor in actores.todos:
+ actor.pre_actualizar()
+ actor.actualizar()
+
+ def resizeEvent(self, event):
+ self.ancho = event.size().width()
+ self.alto = event.size().height()
+
+ def mousePressEvent(self, e):
+ escala = self.escala()
+ x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala)
+ eventos.click_de_mouse.send("Qt::mousePressEvent", x=x, y=y, dx=0, dy=0)
+
+ def mouseReleaseEvent(self, e):
+ escala = self.escala()
+ x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala)
+ eventos.termina_click.send("Qt::mouseReleaseEvent", x=x, y=y, dx=0, dy=0)
+
+ def wheelEvent(self, e):
+ eventos.mueve_rueda.send("ejecutar", delta=e.delta() / 120)
+
+ def mouseMoveEvent(self, e):
+ escala = self.escala()
+ x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala)
+ dx, dy = x - self.mouse_x, y - self.mouse_y
+ eventos.mueve_mouse.send("Qt::mouseMoveEvent", x=x, y=y, dx=dx, dy=dy)
+ self.mouse_x = x
+ self.mouse_y = y
+
+ def keyPressEvent(self, event):
+ codigo_de_tecla = self.obtener_codigo_de_tecla_normalizado(event.key())
+
+ if event.key() == QtCore.Qt.Key_Escape:
+ eventos.pulsa_tecla_escape.send("Qt::keyPressEvent")
+ if event.key() == QtCore.Qt.Key_P:
+ self.alternar_pausa()
+ if event.key() == QtCore.Qt.Key_F:
+ self.alternar_pantalla_completa()
+
+ eventos.pulsa_tecla.send("Qt::keyPressEvent", codigo=codigo_de_tecla, texto=event.text())
+
+ def keyReleaseEvent(self, event):
+ codigo_de_tecla = self.obtener_codigo_de_tecla_normalizado(event.key())
+ eventos.suelta_tecla.send("Qt::keyReleaseEvent", codigo=codigo_de_tecla, texto=event.text())
+
+ def obtener_codigo_de_tecla_normalizado(self, tecla_qt):
+ teclas = {
+ QtCore.Qt.Key_Left: simbolos.IZQUIERDA,
+ QtCore.Qt.Key_Right: simbolos.DERECHA,
+ QtCore.Qt.Key_Up: simbolos.ARRIBA,
+ QtCore.Qt.Key_Down: simbolos.ABAJO,
+ QtCore.Qt.Key_Space: simbolos.SELECCION,
+ QtCore.Qt.Key_Return: simbolos.SELECCION,
+ QtCore.Qt.Key_F1: simbolos.F1,
+ QtCore.Qt.Key_F2: simbolos.F2,
+ QtCore.Qt.Key_F3: simbolos.F3,
+ QtCore.Qt.Key_F4: simbolos.F4,
+ QtCore.Qt.Key_F5: simbolos.F5,
+ QtCore.Qt.Key_F6: simbolos.F6,
+ QtCore.Qt.Key_F7: simbolos.F7,
+ QtCore.Qt.Key_F8: simbolos.F8,
+ QtCore.Qt.Key_F9: simbolos.F9,
+ QtCore.Qt.Key_F10: simbolos.F10,
+ QtCore.Qt.Key_F11: simbolos.F11,
+ QtCore.Qt.Key_F12: simbolos.F12,
+ }
+
+ if teclas.has_key(tecla_qt):
+ return teclas[tecla_qt]
+ else:
+ return tecla_qt
+
+ def escala(self):
+ "Obtiene la proporcion de cambio de escala de la pantalla"
+ return self.alto / float(self.alto_original)
+
+ def obtener_area_de_texto(self, texto, magnitud=10):
+ ancho = 0
+ alto = 0
+
+ fuente = QtGui.QFont()
+ fuente.setPointSize(magnitud)
+ metrica = QtGui.QFontMetrics(fuente)
+
+ lineas = texto.split('\n')
+
+ for linea in lineas:
+ ancho = max(ancho, metrica.width(linea))
+ alto += metrica.height()
+
+ return ancho, alto
+
+ def alternar_pausa(self):
+ if self.pausa_habilitada:
+ self.pausa_habilitada = False
+ self.actor_pausa.eliminar()
+ else:
+ self.pausa_habilitada = True
+ self.actor_pausa = actores.Pausa()
+
+ def ocultar_puntero_del_mouse(self):
+ bitmap = QtGui.QBitmap(1, 1)
+ nuevo_cursor = QtGui.QCursor(bitmap, bitmap)
+ self.setCursor(QtGui.QCursor(nuevo_cursor))
+
+class Qt(QtBase, QWidget):
+
+ def __init__(self):
+ QWidget.__init__(self)
+ QtBase.__init__(self)
+
+class QtGL(QtBase, QGLWidget):
+
+ def __init__(self):
+ if not QGLWidget:
+ print "Lo siento, OpenGL no esta disponible..."
+
+ QGLWidget.__init__(self)
+ QtBase.__init__(self)
+ self._pintar_fondo_negro()
+
+ def _pintar_fondo_negro(self):
+ color = QtGui.QColor(99, 0, 0)
+ self.setStyleSheet("QWidget { background-color: %s }" % color.name())
+
+
+if QGLWidget == object:
+ QtGL = Qt
diff --git a/pilas/mundo.py b/pilas/mundo.py
new file mode 100644
index 0000000..ab0de59
--- /dev/null
+++ b/pilas/mundo.py
@@ -0,0 +1,74 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import pytweener
+from pilas import eventos
+from pilas import tareas
+from pilas import control
+from pilas import fisica
+from pilas import escenas
+from pilas import colisiones
+from pilas import camara
+
+
+class Mundo(object):
+ """Representa un objeto unico que mantiene en funcionamiento al motor.
+
+ Mundo tiene como responsabilidad iniciar los componentes del
+ motor y mantener el bucle de juego.
+ """
+
+ def __init__(self, motor, ancho, alto, titulo, fps=60, economico=True,
+ gravedad=(0, -90), pantalla_completa=False):
+ self.motor = motor
+ self.motor.iniciar_ventana(ancho, alto, titulo, pantalla_completa)
+
+ self.tweener = pytweener.Tweener()
+ self.tareas = tareas.Tareas()
+ self.control = control.Control()
+ self.colisiones = colisiones.Colisiones()
+ self.camara = camara.Camara(self)
+
+ eventos.actualizar.conectar(self.actualizar_simuladores)
+ self.fisica = fisica.Fisica(motor.obtener_area(), gravedad=gravedad)
+ self.escena_actual = None
+
+ def reiniciar(self):
+ self.fisica.reiniciar()
+
+ def actualizar_simuladores(self, evento):
+ self.tweener.update(16)
+ self.tareas.actualizar(1/60.0)
+ self.fisica.actualizar()
+ self.colisiones.verificar_colisiones()
+
+ def terminar(self):
+ import sys
+ sys.exit(0)
+
+ def ejecutar_bucle_principal(self, ignorar_errores=False):
+ "Mantiene en funcionamiento el motor completo."
+ self.motor.ejecutar_bucle_principal(self, ignorar_errores)
+
+ def definir_escena(self, escena_nueva):
+ "Cambia la escena que se muestra en pantalla"
+
+ if self.escena_actual:
+ self.escena_actual.terminar()
+
+ self.escena_actual = escena_nueva
+ escena_nueva.iniciar()
+
+ def agregar_tarea_una_vez(self, time_out, function, *params):
+ return self.tareas.una_vez(time_out, function, params)
+
+ def agregar_tarea_siempre(self, time_out, function, *params):
+ return self.tareas.siempre(time_out, function, params)
+
+ def agregar_tarea(self, time_out, funcion, *parametros):
+ return self.tareas.condicional(time_out, funcion, parametros)
diff --git a/pilas/pilasversion.py b/pilas/pilasversion.py
new file mode 100644
index 0000000..47f7053
--- /dev/null
+++ b/pilas/pilasversion.py
@@ -0,0 +1 @@
+VERSION = '0.59'
diff --git a/pilas/pytweener.py b/pilas/pytweener.py
new file mode 100644
index 0000000..ff66a62
--- /dev/null
+++ b/pilas/pytweener.py
@@ -0,0 +1,739 @@
+# pyTweener
+#
+# Tweening functions for python
+#
+# Heavily based on caurina Tweener: http://code.google.com/p/tweener/
+#
+# Released under M.I.T License - see above url
+# Python version by Ben Harling 2009
+import math
+
+class Tweener(object):
+ def __init__(self, duration = 0.5, tween = None):
+ """Tweener
+ This class manages all active tweens, and provides a factory for
+ creating and spawning tween motions."""
+ self.currentTweens = []
+ self.defaultTweenType = tween or Easing.Linear.easeNone
+ self.defaultDuration = duration or 1.0
+
+ def hasTweens(self):
+ return len(self.currentTweens) > 0
+
+ def addTweenNoArgs(self, obj, function, initial_value, value, **kwargs):
+ "Similar a addTween, solo que se especifica la funcion y el valor de forma explicita."
+ args = {function: value, 'initial_value': initial_value}
+
+ if "tweenTime" in kwargs:
+ t_time = kwargs.pop("tweenTime")
+ else: t_time = self.defaultDuration
+
+ if "tweenType" in kwargs:
+ t_type = kwargs.pop("tweenType")
+ else: t_type = self.defaultTweenType
+
+ if "onCompleteFunction" in kwargs:
+ t_completeFunc = kwargs.pop("onCompleteFunction")
+ else: t_completeFunc = None
+
+ if "onUpdateFunction" in kwargs:
+ t_updateFunc = kwargs.pop("onUpdateFunction")
+ else: t_updateFunc = None
+
+ if "tweenDelay" in kwargs:
+ t_delay = kwargs.pop("tweenDelay")
+ else: t_delay = 0
+
+ if kwargs:
+ raise ValueError("No puede llamar a esta funcion con argumentos nombrados, use addTween en su lugar.")
+
+ tw = Tween(obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **args)
+ if tw:
+ self.currentTweens.append( tw )
+ return tw
+
+ def addTween(self, obj, **kwargs):
+ """ addTween( object, **kwargs) -> tweenObject or False
+
+ Example:
+ tweener.addTween( myRocket, throttle=50, setThrust=400, tweenTime=5.0, tweenType=tweener.OUT_QUAD )
+
+ You must first specify an object, and at least one property or function with a corresponding
+ change value. The tween will throw an error if you specify an attribute the object does
+ not possess. Also the data types of the change and the initial value of the tweened item
+ must match. If you specify a 'set' -type function, the tweener will attempt to get the
+ starting value by call the corresponding 'get' function on the object. If you specify a
+ property, the tweener will read the current state as the starting value. You add both
+ functions and property changes to the same tween.
+
+ in addition to any properties you specify on the object, these keywords do additional
+ setup of the tween.
+
+ tweenTime = the duration of the motion
+ tweenType = one of the predefined tweening equations or your own function
+ onCompleteFunction = specify a function to call on completion of the tween
+ onUpdateFunction = specify a function to call every time the tween updates
+ tweenDelay = specify a delay before starting.
+ """
+ if "tweenTime" in kwargs:
+ t_time = kwargs.pop("tweenTime")
+ else: t_time = self.defaultDuration
+
+ if "tweenType" in kwargs:
+ t_type = kwargs.pop("tweenType")
+ else: t_type = self.defaultTweenType
+
+ if "onCompleteFunction" in kwargs:
+ t_completeFunc = kwargs.pop("onCompleteFunction")
+ else: t_completeFunc = None
+
+ if "onUpdateFunction" in kwargs:
+ t_updateFunc = kwargs.pop("onUpdateFunction")
+ else: t_updateFunc = None
+
+ if "tweenDelay" in kwargs:
+ t_delay = kwargs.pop("tweenDelay")
+ else: t_delay = 0
+
+ tw = Tween( obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **kwargs )
+ if tw:
+ self.currentTweens.append( tw )
+ return tw
+
+ def removeTween(self, tweenObj):
+ if tweenObj in self.currentTweens:
+ tweenObj.complete = True
+ #self.currentTweens.remove( tweenObj )
+
+ def getTweensAffectingObject(self, obj):
+ """Get a list of all tweens acting on the specified object
+ Useful for manipulating tweens on the fly"""
+ tweens = []
+ for t in self.currentTweens:
+ if t.target is obj:
+ tweens.append(t)
+ return tweens
+
+ def removeTweeningFrom(self, obj):
+ """Stop tweening an object, without completing the motion
+ or firing the completeFunction"""
+ for t in self.currentTweens:
+ if t.target is obj:
+ t.complete = True
+
+ def finish(self):
+ #go to last frame for all tweens
+ for t in self.currentTweens:
+ t.update(t.duration)
+ self.currentTweens = []
+
+ def update(self, timeSinceLastFrame):
+ removable = []
+ for t in self.currentTweens:
+ t.update(timeSinceLastFrame)
+
+ if t.complete:
+ removable.append(t)
+
+ for t in removable:
+ self.currentTweens.remove(t)
+
+
+class Tween(object):
+ def __init__(self, obj, tduration, tweenType, completeFunction, updateFunction, delay, **kwargs):
+ """Tween object:
+ Can be created directly, but much more easily using Tweener.addTween( ... )
+ """
+ #print obj, tduration, kwargs
+ self.duration = tduration
+ self.delay = delay
+ self.target = obj
+ self.tween = tweenType
+ self.tweenables = kwargs
+ self.delta = 0
+ self.completeFunction = completeFunction
+ self.updateFunction = updateFunction
+ self.complete = False
+ self.tProps = []
+ self.tFuncs = []
+ self.paused = self.delay > 0
+ self.decodeArguments()
+
+ def decodeArguments(self):
+ """Internal setup procedure to create tweenables and work out
+ how to deal with each"""
+
+ if len(self.tweenables) == 0:
+ # nothing to do
+ print "TWEEN ERROR: No Tweenable properties or functions defined"
+ self.complete = True
+ return
+
+ assert(len(self.tweenables) == 2)
+
+ initial_value = self.tweenables.pop('initial_value')
+
+
+ for k, v in self.tweenables.items():
+
+ # check that its compatible
+ if not hasattr( self.target, k):
+ print "TWEEN ERROR: " + str(self.target) + " has no function " + k
+ self.complete = True
+ break
+
+ prop = func = False
+ startVal = 0
+ newVal = v
+
+ try:
+ startVal = self.target.__dict__[k]
+ prop = k
+ propName = k
+
+ except:
+ func = getattr( self.target, k)
+ funcName = k
+
+ if func:
+ try:
+ getFunc = getattr(self.target, funcName.replace("set", "get") )
+ startVal = getFunc()
+ print getfunc
+ except:
+ # no start value, assume its 0
+ # but make sure the start and change
+ # dataTypes match :)
+ startVal = newVal * 0
+
+ startVal = initial_value
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newFunc = [ k, func, tweenable]
+
+ #setattr(self, funcName, newFunc[2])
+ self.tFuncs.append( newFunc )
+
+
+ if prop:
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newProp = [ k, prop, tweenable]
+ self.tProps.append( newProp )
+
+ """
+ for k, v in self.tweenables.items():
+
+ # check that its compatible
+ if not hasattr( self.target, k):
+ print "TWEEN ERROR: " + str(self.target) + " has no function " + k
+ self.complete = True
+ break
+
+ prop = func = False
+ startVal = 0
+ newVal = v
+
+ try:
+ startVal = self.target.__dict__[k]
+ prop = k
+ propName = k
+
+ except:
+ func = getattr( self.target, k)
+ funcName = k
+
+ if func:
+ try:
+ getFunc = getattr(self.target, funcName.replace("set", "get") )
+ startVal = getFunc()
+ print getfunc
+ except:
+ # no start value, assume its 0
+ # but make sure the start and change
+ # dataTypes match :)
+ startVal = newVal * 0
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newFunc = [ k, func, tweenable]
+
+ #setattr(self, funcName, newFunc[2])
+ self.tFuncs.append( newFunc )
+
+
+ if prop:
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newProp = [ k, prop, tweenable]
+ self.tProps.append( newProp )
+ """
+
+
+ def pause( self, numSeconds=-1 ):
+ """Pause this tween
+ do tween.pause( 2 ) to pause for a specific time
+ or tween.pause() which pauses indefinitely."""
+ self.paused = True
+ self.delay = numSeconds
+
+ def resume( self ):
+ """Resume from pause"""
+ if self.paused:
+ self.paused=False
+
+ def update(self, ptime):
+ """Update this tween with the time since the last frame
+ if there is an update function, it is always called
+ whether the tween is running or paused"""
+
+ if self.complete:
+ return
+
+ if self.paused:
+ if self.delay > 0:
+ self.delay = max( 0, self.delay - ptime )
+ if self.delay == 0:
+ self.paused = False
+ self.delay = -1
+ if self.updateFunction:
+ self.updateFunction()
+ return
+
+ self.delta = min(self.delta + ptime, self.duration)
+
+
+ for propName, prop, tweenable in self.tProps:
+ self.target.__dict__[prop] = self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration )
+ for funcName, func, tweenable in self.tFuncs:
+ func( self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) )
+
+
+ if self.delta == self.duration:
+ self.complete = True
+ if self.completeFunction:
+ self.completeFunction()
+
+ if self.updateFunction:
+ self.updateFunction()
+
+
+
+ def getTweenable(self, name):
+ """Return the tweenable values corresponding to the name of the original
+ tweening function or property.
+
+ Allows the parameters of tweens to be changed at runtime. The parameters
+ can even be tweened themselves!
+
+ eg:
+
+ # the rocket needs to escape!! - we're already moving, but must go faster!
+ twn = tweener.getTweensAffectingObject( myRocket )[0]
+ tweenable = twn.getTweenable( "thrusterPower" )
+ tweener.addTween( tweenable, change=1000.0, tweenTime=0.4, tweenType=tweener.IN_QUAD )
+
+ """
+ ret = None
+ for n, f, t in self.tFuncs:
+ if n == name:
+ ret = t
+ return ret
+ for n, p, t in self.tProps:
+ if n == name:
+ ret = t
+ return ret
+ return ret
+
+ def Remove(self):
+ """Disables and removes this tween
+ without calling the complete function"""
+ self.complete = True
+
+
+class Tweenable:
+ def __init__(self, start, change):
+ """Tweenable:
+ Holds values for anything that can be tweened
+ these are normally only created by Tweens"""
+ self.startValue = start
+ self.change = change
+
+
+"""Robert Penner's easing classes ported over from actionscript by Toms Baugis (at gmail com).
+There certainly is room for improvement, but wanted to keep the readability to some extent.
+
+================================================================================
+ Easing Equations
+ (c) 2003 Robert Penner, all rights reserved.
+ This work is subject to the terms in
+ http://www.robertpenner.com/easing_terms_of_use.html.
+================================================================================
+
+TERMS OF USE - EASING EQUATIONS
+
+Open source under the BSD License.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used
+ to endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+class Easing:
+ class Back:
+ @staticmethod
+ def easeIn(t, b, c, d, s = 1.70158):
+ t = t / d
+ return c * t**2 * ((s+1) * t - s) + b
+
+ @staticmethod
+ def easeOut (t, b, c, d, s = 1.70158):
+ t = t / d - 1
+ return c * (t**2 * ((s + 1) * t + s) + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d, s = 1.70158):
+ t = t / (d * 0.5)
+ s = s * 1.525
+
+ if t < 1:
+ return c * 0.5 * (t**2 * ((s + 1) * t - s)) + b
+
+ t = t - 2
+ return c / 2 * (t**2 * ((s + 1) * t + s) + 2) + b
+
+ class Bounce:
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d
+ if t < 1 / 2.75:
+ return c * (7.5625 * t**2) + b
+ elif t < 2 / 2.75:
+ t = t - 1.5 / 2.75
+ return c * (7.5625 * t**2 + 0.75) + b
+ elif t < 2.5 / 2.75:
+ t = t - 2.25 / 2.75
+ return c * (7.5625 * t**2 + .9375) + b
+ else:
+ t = t - 2.625 / 2.75
+ return c * (7.5625 * t**2 + 0.984375) + b
+
+ @staticmethod
+ def easeIn (t, b, c, d):
+ return c - Easing.Bounce.easeOut(d-t, 0, c, d) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ if t < d * 0.5:
+ return Easing.Bounce.easeIn (t * 2, 0, c, d) * .5 + b
+
+ return Easing.Bounce.easeOut (t * 2 -d, 0, c, d) * .5 + c*.5 + b
+
+
+
+ class Circ:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return -c * (math.sqrt(1 - t**2) - 1) + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * math.sqrt(1 - t**2) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return -c * 0.5 * (math.sqrt(1 - t**2) - 1) + b
+
+ t = t - 2
+ return c*0.5 * (math.sqrt(1 - t**2) + 1) + b
+
+
+ class Cubic:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**3 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * (t**3 + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**3 + b
+
+ t = t - 2
+ return c * 0.5 * (t**3 + 2) + b
+
+
+ class Elastic:
+ @staticmethod
+ def easeIn (t, b, c, d, a = 0, p = 0):
+ if t==0: return b
+
+ t = t / d
+ if t == 1: return b+c
+
+ if not p: p = d * .3;
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ t = t - 1
+ return - (a * math.pow(2, 10 * t) * math.sin((t*d-s) * (2 * math.pi) / p)) + b
+
+
+ @staticmethod
+ def easeOut (t, b, c, d, a = 0, p = 0):
+ if t == 0: return b
+
+ t = t / d
+ if (t == 1): return b + c
+
+ if not p: p = d * .3;
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ return a * math.pow(2,-10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) + c + b
+
+
+ @staticmethod
+ def easeInOut (t, b, c, d, a = 0, p = 0):
+ if t == 0: return b
+
+ t = t / (d * 0.5)
+ if t == 2: return b + c
+
+ if not p: p = d * (.3 * 1.5)
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ if (t < 1):
+ t = t - 1
+ return -.5 * (a * math.pow(2, 10 * t) * math.sin((t * d - s) * (2 * math.pi) / p)) + b
+
+ t = t - 1
+ return a * math.pow(2, -10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) * .5 + c + b
+
+
+ class Expo:
+ @staticmethod
+ def easeIn(t, b, c, d):
+ if t == 0:
+ return b
+ else:
+ return c * math.pow(2, 10 * (t / d - 1)) + b - c * 0.001
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ if t == d:
+ return b + c
+ else:
+ return c * (-math.pow(2, -10 * t / d) + 1) + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ if t==0:
+ return b
+ elif t==d:
+ return b+c
+
+ t = t / (d * 0.5)
+
+ if t < 1:
+ return c * 0.5 * math.pow(2, 10 * (t - 1)) + b
+
+ return c * 0.5 * (-math.pow(2, -10 * (t - 1)) + 2) + b
+
+
+ class Linear:
+ @staticmethod
+ def easeNone(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeIn(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ return c * t / d + b
+
+
+ class Quad:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**2 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d
+ return -c * t * (t-2) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**2 + b
+
+ t = t - 1
+ return -c * 0.5 * (t * (t - 2) - 1) + b
+
+
+ class Quart:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**4 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return -c * (t**4 - 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**4 + b
+
+ t = t - 2
+ return -c * 0.5 * (t**4 - 2) + b
+
+
+ class Quint:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**5 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * (t**5 + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**5 + b
+
+ t = t - 2
+ return c * 0.5 * (t**5 + 2) + b
+
+ class Sine:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ return -c * math.cos(t / d * (math.pi / 2)) + c + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ return c * math.sin(t / d * (math.pi / 2)) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ return -c * 0.5 * (math.cos(math.pi * t / d) - 1) + b
+
+
+ class Strong:
+ @staticmethod
+ def easeIn(t, b, c, d):
+ return c * (t/d)**5 + b
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ return c * ((t / d - 1)**5 + 1) + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ t = t / (d * 0.5)
+
+ if t < 1:
+ return c * 0.5 * t**5 + b
+
+ t = t - 2
+ return c * 0.5 * (t**5 + 2) + b
+
+
+
+class TweenTestObject:
+ def __init__(self):
+ self.pos = 20
+ self.rot = 50
+
+ def update(self):
+ print self.pos, self.rot
+
+ def setRotation(self, rot):
+ self.rot = rot
+
+ def getRotation(self):
+ return self.rot
+
+ def complete(self):
+ print "I'm done tweening now mommy!"
+
+
+if __name__=="__main__":
+ import time
+ T = Tweener()
+ tst = TweenTestObject()
+ mt = T.addTween( tst, setRotation=500.0, tweenTime=2.5, tweenType=T.OUT_QUAD,
+ pos=-200, tweenDelay=0.4, onCompleteFunction=tst.complete,
+ onUpdateFunction=tst.update )
+ s = time.clock()
+ changed = False
+ while T.hasTweens():
+ tm = time.clock()
+ d = tm - s
+ s = tm
+ T.update( d )
+ if mt.delta > 1.0 and not changed:
+
+ tweenable = mt.getTweenable( "setRotation" )
+
+ T.addTween( tweenable, change=-1000, tweenTime=0.7 )
+ T.addTween( mt, duration=-0.2, tweenTime=0.2 )
+ changed = True
+ #print mt.duration,
+ print tst.getRotation(), tst.pos
+ time.sleep(0.06)
+ print tst.getRotation(), tst.pos
diff --git a/pilas/red.py b/pilas/red.py
new file mode 100644
index 0000000..7d28fdb
--- /dev/null
+++ b/pilas/red.py
@@ -0,0 +1,41 @@
+# -*- encoding: utf-8 -*-
+# pilas engine - a video game framework.
+#
+# copyright 2010 - hugo ruscitti
+# license: lgplv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# website - http://www.pilas-engine.com.ar
+
+import SocketServer
+
+
+
+def iniciar_servidor():
+
+
+ class EchoRequestHandler(SocketServer.BaseRequestHandler ):
+ def setup(self):
+ print self.client_address, 'connected!'
+ self.request.send('hi ' + str(self.client_address) + '\n')
+
+ def handle(self):
+ data = 'dummy'
+ while data:
+ data = self.request.recv(1024)
+ print "ha llegado el mensaje:", data
+ self.request.send(data)
+
+ if data.strip() == 'bye':
+ return
+
+ def finish(self):
+ print self.client_address, 'disconnected!'
+ self.request.send('bye ' + str(self.client_address) + '\n')
+
+
+ #server host is a tuple ('host', port)
+ puerto = 50008
+ print "iniciando el modo servidor en el puerto %d" %(puerto)
+
+ servidor = SocketServer.ThreadingTCPServer(('', puerto), EchoRequestHandler)
+ servidor.serve_forever()
diff --git a/pilas/simbolos.py b/pilas/simbolos.py
new file mode 100644
index 0000000..4c5afed
--- /dev/null
+++ b/pilas/simbolos.py
@@ -0,0 +1,19 @@
+IZQUIERDA = 1
+DERECHA = 2
+ARRIBA = 3
+ABAJO = 4
+BOTON = 5
+SELECCION = 6
+
+F1='F1'
+F2='F2'
+F3='F3'
+F4='F4'
+F5='F5'
+F6='F6'
+F7='F7'
+F8='F8'
+F9='F9'
+F10='F10'
+F11='F11'
+F12='F12'
diff --git a/pilas/sonidos.py b/pilas/sonidos.py
new file mode 100644
index 0000000..dadc1e9
--- /dev/null
+++ b/pilas/sonidos.py
@@ -0,0 +1,36 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+import os
+
+
+def cargar(ruta):
+ """Carga un sonido para reproducir, donde el argumento ``ruta`` indica cual es el archivo.
+
+ Por ejemplo::
+
+ import pilas
+
+ risa = pilas.sonidos.cargar("risa.ogg")
+
+ En caso de éxito retorna el objeto Sound, que se puede
+ reproducir usando el método ``reproducir()``, por ejemplo::
+
+ risa.reproducir()
+
+ El directorio de búsqueda del sonido sigue el siguiente orden:
+
+ * primero busca en el directorio actual.
+ * luego en 'data'.
+ * por último en el directorio estándar de la biblioteca.
+
+ En caso de error genera una excepción de tipo IOError.
+ """
+ ruta = pilas.utils.obtener_ruta_al_recurso(ruta)
+ return pilas.mundo.motor.cargar_sonido(ruta)
diff --git a/pilas/tareas.py b/pilas/tareas.py
new file mode 100644
index 0000000..c94c2e8
--- /dev/null
+++ b/pilas/tareas.py
@@ -0,0 +1,111 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+class Tarea(object):
+
+ def __init__(self, time_out, dt, funcion, parametros, una_vez):
+ """
+
+ Parametros:
+
+ - time_out: el tiempo absoluto para ejecutar la tarea.
+ - dt: la frecuencia de ejecución.
+ - funcion: la funcion a invocar.
+ - parametros: una lista de argumentos para la funcion anterior.
+ - una_vez: indica si la funcion se tiene que ejecutar una sola vez.
+ """
+
+ self.time_out = time_out
+ self.dt = dt
+ self.funcion = funcion
+ self.parametros = parametros
+ self.una_vez = una_vez
+ self.activa = True
+
+ def ejecutar(self):
+ return self.funcion(*self.parametros)
+
+ def eliminar(self):
+ self.activa = False
+
+class TareaCondicional(Tarea):
+
+ def ejecutar(self):
+ retorno = Tarea.ejecutar(self)
+
+ if not retorno:
+ self.una_vez = True
+
+class Tareas(object):
+ """Contenedor de tareas a ejecutar por tiempo.
+
+ El Tareas es un planificador de tareas, permite que
+ podamos ejecutar funciones y métodos luego de transcurrido
+ el tiempo que queramos.
+
+ Por ejemplo, si se quiere que el planificardor ejecute
+ una función dentro de dos segundos podemos escribir:
+
+ pilas.mundo.agregar_tarea(2, hola)
+
+ o bien, especificando argumentos para esa función:
+
+ pilas.mundo.agregar_tarea(4, hola, 'persona')
+
+ La función que se especifique como segundo argumento
+ tiene que retornar True o False. Si retorna True será
+ colocada nuevamente en la cola de tareas una vez que se
+ ejecute (esto es útil para crear bucles).
+ """
+
+ def __init__(self):
+ self.tareas_planificadas = []
+ self.contador_de_tiempo = 0
+
+ def actualizar(self, dt):
+ "Actualiza los contadores de tiempo y ejecuta las tareas pendientes."
+ self.contador_de_tiempo += dt
+ to_remove = []
+
+ for tarea in self.tareas_planificadas:
+ if self.contador_de_tiempo > tarea.time_out:
+ if tarea.activa:
+ tarea.ejecutar()
+
+ if tarea.una_vez:
+ self.tareas_planificadas.remove(tarea)
+ else:
+ w = self.contador_de_tiempo - tarea.time_out
+ parte_entera = int((w)/float(tarea.dt))
+ resto = w - (parte_entera * tarea.dt)
+
+ for x in range(parte_entera):
+ tarea.ejecutar()
+
+ tarea.time_out += tarea.dt + (parte_entera * tarea.dt) - resto
+ else:
+ self.tareas_planificadas.remove(tarea)
+
+ def _agregar(self, tarea):
+ "Agrega una nueva tarea para ejecutarse luego."
+ self.tareas_planificadas.append(tarea)
+
+ def una_vez(self, time_out, function, params=[]):
+ tarea = Tarea(self.contador_de_tiempo + time_out, time_out, function, params, True)
+ self._agregar(tarea)
+ return tarea
+
+ def siempre(self, time_out, function, params=[]):
+ tarea = Tarea(self.contador_de_tiempo + time_out, time_out, function, params, False)
+ self._agregar(tarea)
+ return tarea
+
+ def condicional(self, time_out, function, params=[]):
+ tarea = TareaCondicional(self.contador_de_tiempo + time_out, time_out, function, params, False)
+ self._agregar(tarea)
+ return tarea
diff --git a/pilas/test.html b/pilas/test.html
new file mode 100644
index 0000000..557db03
--- /dev/null
+++ b/pilas/test.html
@@ -0,0 +1 @@
+Hello World
diff --git a/pilas/test/test.py b/pilas/test/test.py
new file mode 100644
index 0000000..27a0fc8
--- /dev/null
+++ b/pilas/test/test.py
@@ -0,0 +1,4 @@
+import os
+
+if __name__ == '__main__':
+ os.system("py.test -v --tb=short")
diff --git a/pilas/test/test_actores.py b/pilas/test/test_actores.py
new file mode 100644
index 0000000..7b8221b
--- /dev/null
+++ b/pilas/test/test_actores.py
@@ -0,0 +1,73 @@
+import pilas
+
+def test_posicion_de_los_actores():
+ pilas.iniciar()
+ mono = pilas.actores.Mono()
+
+ # el actor comienza en el centro de la ventana
+ assert mono.x == 0
+ assert mono.y == 0
+
+ # un cambio de posicion sencillo
+ mono.x = 100
+ mono.y = 100
+ assert mono.x == 100
+ assert mono.y == 100
+
+ # rotacion
+ assert mono.rotacion == 0
+
+ mono.rotacion = 180
+ assert mono.rotacion == 180
+
+ # Verificnado que las rotaciones siempre estan entre 0 y 360
+ mono.rotacion = 361
+ assert mono.rotacion == 1
+
+ mono.rotacion = -10
+ assert mono.rotacion == 350
+
+ # Analizando el actor existira en la escena
+ assert mono in pilas.actores.todos
+
+ # Escalas
+ assert mono.escala == 1
+
+ mono.escala = 0
+ assert mono.escala == 0
+
+ mono.escala = 0.5
+ assert mono.escala == 0.5
+
+ mono.escala = 5
+ assert mono.escala == 5
+
+ # verificando que el mono se elimina de la escena.
+ mono.eliminar()
+ assert not (mono in pilas.actores.todos)
+
+
+def test_correlacion_de_posiciones():
+ mono = pilas.actores.Mono()
+
+ assert mono.x == 0
+ assert mono.y == 0
+
+ mono.izquierda = mono.izquierda - 100
+ assert mono.x == -100
+
+ mono.derecha = mono.derecha + 100
+ assert mono.x == 0
+
+ mono.arriba = mono.arriba + 100
+ assert mono.y == 100
+
+ mono.abajo = mono.abajo - 100
+ assert mono.y == 0
+
+ mono.eliminar()
+
+def test_colisiones_contra_un_punto():
+ mono = pilas.actores.Mono()
+ assert mono.colisiona_con_un_punto(0, 0)
+ assert not mono.colisiona_con_un_punto(200, 200)
diff --git a/pilas/test/test_anterior.py b/pilas/test/test_anterior.py
new file mode 100644
index 0000000..7ac7121
--- /dev/null
+++ b/pilas/test/test_anterior.py
@@ -0,0 +1,117 @@
+# -*- encoding: utf-8 -*-
+import pilas
+
+def test_existe_mundo():
+ pilas.iniciar()
+ assert pilas.mundo
+
+def test_cargar_imagenes():
+ pilas.iniciar()
+ original_image = pilas.imagenes.cargar('mono.png')
+
+ actor = pilas.actores.Actor(original_image)
+ actors_image = actor.imagen
+
+ assert original_image == actors_image
+
+def test_planificador():
+ pilas.iniciar()
+ pilas.mundo.agregar_tarea_una_vez(2, None)
+ pilas.mundo.agregar_tarea_una_vez(2, None, (1, 2, 3))
+
+def test_interpolacion():
+ pilas.iniciar()
+ a = pilas.interpolar([0, 100])
+ assert a.values == [0, 100]
+
+ # Invierte la interpolacion.
+ a = -a
+ assert a.values == [100, 0]
+
+def test_actor_texto():
+ pilas.iniciar()
+ texto = pilas.actores.Texto("Hola")
+ assert texto.texto == "Hola"
+
+ # verificando que el tamaño inicial es de 30 y el color negro
+ assert texto.magnitud == 30
+
+def test_habilidades():
+ texto = pilas.actores.Texto("Hola")
+
+ # Vincula la clase Text con un componente.
+ component = pilas.habilidades.AumentarConRueda
+ texto.aprender(component)
+
+ # Se asegura que el componente pasa a ser de la superclase.
+ assert component == texto.habilidades[0].__class__
+
+def test_existen_los_atajos():
+ assert pilas.atajos
+
+def test_Ejes():
+ ejes = pilas.actores.Ejes()
+ assert ejes
+
+
+def test_Grilla():
+ grilla = pilas.imagenes.cargar_grilla("fondos/volley.png", 10, 10)
+ assert grilla
+ grilla.avanzar()
+
+def test_Fondo():
+ un_fondo = pilas.fondos.Tarde()
+ assert un_fondo
+
+def test_Control():
+ control = pilas.mundo.control
+
+ assert control.izquierda
+ assert control.derecha
+ assert control.arriba
+ assert control.abajo
+ assert control.boton
+
+
+def test_Distancias():
+ assert 0 == pilas.utils.distancia(0, 0)
+ assert 10 == pilas.utils.distancia(0, 10)
+ assert 10 == pilas.utils.distancia(0, -10)
+ assert 10 == pilas.utils.distancia(-10, 0)
+
+ assert 0 == pilas.utils.distancia_entre_dos_puntos((0, 0), (0, 0))
+ assert 10 == pilas.utils.distancia_entre_dos_puntos((0, 0), (10, 0))
+ assert 10 == pilas.utils.distancia_entre_dos_puntos((0, 0), (0, 10))
+ assert 10 == pilas.utils.distancia_entre_dos_puntos((10, 10), (0, 10))
+
+
+def test__posiciones_del_texto():
+ m = pilas.actores.Texto("Hola")
+ assert m.x == 0
+
+ ancho = m.obtener_ancho()
+ algo = m.obtener_alto()
+
+ # Verifica que la izquierda del actor esté asociada a la
+ # posición 'x'.
+ m.izquierda = m.izquierda - 50
+ assert m.x == -50
+
+ m.izquierda = m.izquierda - 50
+ assert m.x == -100
+
+ # Analiza si la parte derecha del actor esta vinculada a 'x'
+ m.derecha = m.derecha + 50
+ assert m.x == -50
+
+ # Verifica si la posicion superior e inferior alteran a 'y'
+ assert m.y == 0
+ m.arriba = m.arriba - 100
+ assert m.y == -100
+
+ m.abajo = m.abajo + 100
+ assert m.y == 0
+
+if __name__ == '__main__':
+ pilas.iniciar()
+ unittest.main()
diff --git a/pilas/test/test_interface.py b/pilas/test/test_interface.py
new file mode 100644
index 0000000..f76dd81
--- /dev/null
+++ b/pilas/test/test_interface.py
@@ -0,0 +1,30 @@
+import pilas
+
+def test_todos_los_objetos_de_interfaz_se_pueden_crear():
+ pilas.iniciar()
+
+ deslizador = pilas.interfaz.Deslizador()
+ assert deslizador
+ assert deslizador.progreso == 0
+
+ boton = pilas.interfaz.Boton()
+ assert boton
+
+ ingreso = pilas.interfaz.IngresoDeTexto()
+ assert ingreso
+
+ try:
+ pilas.interfaz.ListaSeleccion()
+ except TypeError:
+ assert True # Se espera esta excepcion, porque un argumento es obligatorio
+
+ lista = pilas.interfaz.ListaSeleccion([('uno')])
+ assert lista
+
+ try:
+ pilas.interfaz.Selector()
+ except TypeError:
+ assert True # el argumento texto es obligatorio.
+
+ selector = pilas.interfaz.Selector("hola")
+ assert selector
diff --git a/pilas/test/test_posiciones.py b/pilas/test/test_posiciones.py
new file mode 100644
index 0000000..69f9cae
--- /dev/null
+++ b/pilas/test/test_posiciones.py
@@ -0,0 +1,246 @@
+import pilas
+
+def test_posiciones_de_los_actores():
+ pilas.iniciar()
+
+ caja = pilas.actores.Actor("caja.png")
+
+ # +------------+
+ # | |
+ # | |
+ # | x |
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.alto == 48
+ assert caja.ancho == 48
+
+ assert caja.arriba == 24
+ assert caja.abajo == -24
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+
+def test_escala_reducida():
+ pilas.iniciar()
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 0.5
+
+ # +------------+ # La caja resultado
+ # | | # es la interior.
+ # | +----+ |
+ # | | | |
+ # | +----+ |
+ # | |
+ # +------------+
+
+ assert caja.alto == 24
+ assert caja.ancho == 24
+
+ assert caja.x == 0
+ assert caja.y == 0
+
+ assert caja.arriba == 12
+ assert caja.abajo == -12
+ assert caja.izquierda == -12
+ assert caja.derecha == 12
+
+def test_escala_ampliada():
+ pilas.iniciar()
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 2
+
+ assert caja.alto == 48*2
+ assert caja.ancho == 48*2
+
+ assert caja.x == 0
+ assert caja.y == 0
+
+ assert caja.arriba == 48
+ assert caja.abajo == -48
+ assert caja.izquierda == -48
+ assert caja.derecha == 48
+
+
+def test_cambio_horizontal_de_centro():
+ pilas.iniciar()
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 1
+ caja.centro = ("izquierda", "centro")
+ # +------------+
+ # | |
+ # | |
+ # |x |
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.alto == 48
+ assert caja.ancho == 48
+
+ assert caja.arriba == 24
+ assert caja.abajo == -24
+ assert caja.izquierda == 0
+ assert caja.derecha == 48
+
+ caja.centro = ("derecha", "centro")
+ # +------------+
+ # | |
+ # | |
+ # | x|
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.arriba == 24
+ assert caja.abajo == -24
+ assert caja.izquierda == -48
+ assert caja.derecha == 0
+
+
+def test_cambiocentrovertical():
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 1
+ caja.centro = ("centro", "arriba")
+ # +------------+
+ # | x |
+ # | |
+ # | |
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.alto == 48
+ assert caja.ancho == 48
+
+ assert caja.abajo == -48
+ assert caja.arriba == 0
+
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+ caja.centro = ("centro", "abajo")
+ # +------------+
+ # | |
+ # | |
+ # | |
+ # | |
+ # | x |
+ # +------------+
+
+ assert caja.arriba == 48
+ assert caja.abajo == 0
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+def test_cambiocentroverticalyhorizontalnocentrado():
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 1
+
+ caja.centro = (10, 10)
+
+ # +------------+
+ # | |
+ # | x |
+ # | |
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.alto == 48
+ assert caja.ancho == 48
+
+ assert caja.abajo == -38
+ assert caja.arriba == 10
+
+ assert caja.izquierda == -10
+ assert caja.derecha == 38
+
+def test_cambiocentroverticalyhorizontalnocentradoconreducciondeescala():
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 0.5
+
+ caja.centro = (10, 10)
+
+ # +------------+
+ # | |
+ # | x |
+ # | |
+ # | |
+ # | |
+ # +------------+
+
+ assert caja.alto == 24
+ assert caja.ancho == 24
+
+ assert caja.abajo == -38/2
+ assert caja.arriba == 5
+
+ assert caja.izquierda == -5
+ assert caja.derecha == 38/2
+
+def test_cambiodeposicionhorizontalconescala():
+ caja = pilas.actores.Actor("caja.png")
+ caja.escala = 1
+
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+ # Prueba dos cambios que no tendrian que afectar
+ caja.izquierda = -24
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+ caja.derecha = 24
+ assert caja.izquierda == -24
+ assert caja.derecha == 24
+
+ caja.escala = 0.5
+
+ assert caja.izquierda == -12
+ assert caja.derecha == 12
+
+ # Prueba dos cambios que no tendrian que afectar
+ caja.izquierda = -20
+ assert caja.izquierda == -20
+ assert caja.derecha == -20 + 24
+
+ caja.derecha = 0
+ assert caja.izquierda == -24
+ assert caja.derecha == 0
+
+def test_cambiodeposicionverticalconescala():
+ caja = pilas.actores.Actor("caja.png")
+ caja.centro = ("centro", "centro")
+ caja.escala = 1
+
+ assert caja.area == 48
+ assert caja.arriba == 24
+ assert caja.abajo == -24
+
+ caja.arriba = 0
+ assert caja.arriba == 0
+ assert caja.abajo == -48
+
+ caja.abajo = 0
+ assert caja.arriba == 48
+ assert caja.abajo == 0
+
+
+ caja.escala = 0.5
+ assert caja.area, (24 == 24)
+ caja.x, caja.y = (0, 0)
+
+ assert caja.arriba == 12
+ assert caja.abajo == -12
+
+ # Prueba dos cambios que no tendrian que afectar
+ caja.izquierda = -20
+ assert caja.izquierda == -20
+ assert caja.derecha == -20 + 24
+
+ caja.derecha = 0
+ assert caja.izquierda == -24
+ assert caja.derecha == 0
+
diff --git a/pilas/test/test_ver_codigo.py b/pilas/test/test_ver_codigo.py
new file mode 100644
index 0000000..9cefe10
--- /dev/null
+++ b/pilas/test/test_ver_codigo.py
@@ -0,0 +1,7 @@
+import pilas
+
+def test_ver_codigo():
+ pilas.iniciar()
+ assert pilas.ver(pilas, False, True)
+ assert pilas.ver(pilas.habilidades, False, True)
+ assert pilas.ver(pilas.habilidades.Arrastrable, False, True)
diff --git a/pilas/utils.py b/pilas/utils.py
new file mode 100644
index 0000000..b2fb93a
--- /dev/null
+++ b/pilas/utils.py
@@ -0,0 +1,246 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+import os
+import shutil
+import interpolaciones
+import sys
+import subprocess
+import math
+
+import pilas
+import xmlreader
+
+
+PATH = os.path.dirname(os.path.abspath(__file__))
+
+
+def cargar_autocompletado():
+ "Carga los modulos de python para autocompletar desde la consola interactiva."
+ try:
+ import rlcompleter
+ import readline
+
+ readline.parse_and_bind("tab: complete")
+ except ImportError:
+ print "No se puede cargar el autocompletado, instale readline..."
+
+def hacer_flotante_la_ventana():
+ "Hace flotante la ventana para i3 (el manejador de ventanas que utiliza hugo...)"
+ try:
+ subprocess.call(['i3-msg', 't'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ pass
+
+def es_interpolacion(an_object):
+ "Indica si un objeto se comporta como una colisión."
+
+ return isinstance(an_object, interpolaciones.Interpolacion)
+
+
+def obtener_ruta_al_recurso(ruta):
+ """Busca la ruta a un archivo de recursos.
+
+ Los archivos de recursos (como las imagenes) se buscan en varios
+ directorios (ver docstring de image.load), así que esta
+ función intentará dar con el archivo en cuestión.
+ """
+
+ dirs = ['./', os.path.dirname(sys.argv[0]), 'data', PATH, PATH + '/data']
+
+
+ for x in dirs:
+ full_path = os.path.join(x, ruta)
+ #DEBUG: print "buscando en: '%s'" %(full_path)
+
+ if os.path.exists(full_path):
+ return full_path
+
+ # Si no ha encontrado el archivo lo reporta.
+ raise IOError("El archivo '%s' no existe." %(ruta))
+
+
+def esta_en_sesion_interactiva():
+ "Indica si pilas se ha ejecutado desde una consola interactiva de python."
+ import sys
+ try:
+ cursor = sys.ps1
+ return True
+ except AttributeError:
+ try:
+ in_ipython = sys.ipcompleter
+ return True
+ except AttributeError:
+ if sys.stdin.__class__.__module__.startswith("idle"):
+ return True
+
+ return False
+
+def distancia(a, b):
+ "Retorna la distancia entre dos numeros."
+ return abs(b - a)
+
+def distancia_entre_dos_puntos((x1, y1), (x2, y2)):
+ "Retorna la distancia entre dos puntos en dos dimensiones."
+ return math.sqrt(distancia(x1, x2) ** 2 + distancia(y1, y2) ** 2)
+
+def distancia_entre_dos_actores(a, b):
+ return distancia_entre_dos_puntos((a.x, a.y), (b.x, b.y))
+
+def colisionan(a, b):
+ "Retorna True si dos actores estan en contacto."
+ return distancia_entre_dos_actores(a, b) < a.radio_de_colision + b.radio_de_colision
+
+def crear_juego():
+ nombre = raw_input("Indica el nombre del juego: ")
+ shutil.copytree(PATH + "/data/juegobase", nombre)
+
+ print "Se ha creado el directorio '%s'" %(nombre)
+ print "Ingresa en el directorio y econtrarás los archivos iniciales del juego."
+
+
+def interpolable(f):
+ "Decorador que se aplica a un metodo para que permita animaciones de interpolaciones."
+
+ def inner(*args, **kwargs):
+ value = args[1]
+
+ # Si le indican dos argumentos, el primer sera
+ # el valor de la interpolacion y el segundo la
+ # velocidad.
+ if isinstance(value, tuple) and len(value) == 2:
+ duracion = value[1]
+ value = value[0]
+ else:
+ duracion = 1
+
+ if isinstance(value, list):
+ value = interpolar(value, duracion=duracion)
+ elif isinstance(value, xrange):
+ value = interpolar(list(value), duracion=duracion)
+
+ if es_interpolacion(value):
+ value.apply(args[0], function=f.__name__)
+ else:
+ f(args[0], value, **kwargs)
+
+ return inner
+
+def hacer_coordenada_mundo(x, y):
+ dx, dy = pilas.mundo.motor.centro_fisico()
+ return (x + dx, dy - y)
+
+def hacer_coordenada_pantalla_absoluta(x, y):
+ dx, dy = pilas.mundo.motor.centro_fisico()
+ return (x + dx, dy - y)
+
+def listar_actores_en_consola():
+ todos = pilas.actores.todos
+
+ print "Hay %d actores en la escena:" %(len(todos))
+ print ""
+
+ for s in todos:
+ print "\t", s
+
+ print ""
+
+def obtener_angulo_entre(punto_a, punto_b):
+ (x, y) = punto_a
+ (x1, y1) = punto_b
+ return math.degrees(math.atan2(y1 - y, x1 -x))
+
+def convertir_de_posicion_relativa_a_fisica(x, y):
+ dx, dy = pilas.mundo.motor.centro_fisico()
+ return (x + dx, dy - y)
+
+def convertir_de_posicion_fisica_relativa(x, y):
+ dx, dy = pilas.mundo.motor.centro_fisico()
+ return (x - dx, dy - y)
+
+def interpolar(valor_o_valores, duracion=1, demora=0, tipo='lineal'):
+ """Retorna un objeto que representa cambios de atributos progresivos.
+
+ El resultado de esta función se puede aplicar a varios atributos
+ de los actores, por ejemplo::
+
+ bomba = pilas.actores.Bomba()
+ bomba.escala = pilas.interpolar(3)
+
+ Esta función también admite otros parámetros cómo:
+
+ - duracion: es la cantidad de segundos que demorará toda la interpolación.
+ - demora: cuantos segundos se deben esperar antes de iniciar.
+ - tipo: es el algoritmo de la interpolación, puede ser 'lineal'.
+ """
+
+
+ import interpolaciones
+
+ algoritmos = {
+ 'lineal': interpolaciones.Lineal,
+ }
+
+ if algoritmos.has_key('lineal'):
+ clase = algoritmos[tipo]
+ else:
+ raise ValueError("El tipo de interpolacion %s es invalido" %(tipo))
+
+ # Permite que los valores de interpolacion sean un numero o una lista.
+ if not isinstance(valor_o_valores, list):
+ valor_o_valores = [valor_o_valores]
+
+ return clase(valor_o_valores, duracion, demora)
+
+def obtener_area():
+ "Retorna el area que ocupa la ventana"
+ return pilas.mundo.motor.obtener_area()
+
+def obtener_bordes():
+ ancho, alto = pilas.mundo.motor.obtener_area()
+ return -ancho/2, ancho/2, alto/2, -alto/2
+
+def obtener_area_de_texto(texto):
+ "Informa el ancho y alto que necesitara un texto para imprimirse."
+ return pilas.mundo.motor.obtener_area_de_texto(texto)
+
+def realizar_pruebas():
+ print "Realizando pruebas de dependencias:"
+ print ""
+
+ print "Box 2D:",
+
+ try:
+ import Box2D as box2d
+ print "OK, versión", box2d.__version__
+ except ImportError:
+ print "Error -> no se encuentra pybox2d."
+
+ print "pygame:",
+
+ try:
+ import pygame
+ print "OK, versión", pygame.__version__
+ except ImportError:
+ print "Error -> no se encuentra pygame."
+
+ print "pyqt:",
+
+ try:
+ from PyQt4 import Qt
+ print "OK, versión", Qt.PYQT_VERSION_STR
+ except ImportError:
+ print "Error -> no se encuentra pyqt."
+
+ print "pyqt con aceleracion:",
+
+ try:
+ from PyQt4 import QtOpenGL
+ from PyQt4.QtOpenGL import QGLWidget
+ print "OK"
+ except ImportError:
+ print "Error -> no se encuentra pyqt4gl."
diff --git a/pilas/ventana.py b/pilas/ventana.py
new file mode 100644
index 0000000..b53e7d2
--- /dev/null
+++ b/pilas/ventana.py
@@ -0,0 +1,16 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+import pilas
+
+modo_depuracion = False
+eje_coordenadas = None
+
+def iniciar(ancho, alto, titulo):
+ ventana = pilas.motor.crear_ventana(ancho, alto, titulo)
+ return ventana
diff --git a/pilas/video/__init__.py b/pilas/video/__init__.py
new file mode 100644
index 0000000..c08c026
--- /dev/null
+++ b/pilas/video/__init__.py
@@ -0,0 +1,18 @@
+# -*- encoding: utf-8 -*-
+# Pilas engine - A video game framework.
+#
+# Copyright 2010 - Hugo Ruscitti
+# License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
+#
+# Website - http://www.pilas-engine.com.ar
+
+
+'''
+import video
+import pilas
+
+todos = []
+
+from video import DeCamara
+from video import DePelicula
+'''
diff --git a/pilas/video/video.py b/pilas/video/video.py
new file mode 100644
index 0000000..cdd739d
--- /dev/null
+++ b/pilas/video/video.py
@@ -0,0 +1,95 @@
+# -*- encoding: utf-8 -*-
+'''
+
+import pilas
+try:
+ import opencv
+ from opencv import highgui
+except ImportError:
+ opencv = None
+
+import os
+
+try:
+ from PySFML import sf
+except ImportError:
+ pass
+
+class MissingOpencv(Exception):
+ def __init__(self):
+ self.value = "Open CV no esta instalado, obtengalo en http://opencv.willowgarage.com"
+
+ def __str__(self):
+ return repr(self.value)
+
+def error(biblioteca, web):
+ print "Error, no ecuentra la biblioteca '%s' (de %s)" %(biblioteca, web)
+
+def no_opencv():
+ from pilas.utils import esta_en_sesion_interactiva
+ if esta_en_sesion_interactiva():
+ error('opencv', 'http://opencv.willowgarage.com')
+ else:
+ raise MissingOpencv()
+
+class DeCamara(pilas.actores.Actor):
+ """
+ Nos permite poner en pantalla el video proveniente de la camara web.
+
+ """
+ def __init__(self, ancho=640, alto=480):
+ if opencv is None:
+ no_opencv()
+ return
+ import webcam
+ self.camara = webcam.CamaraWeb
+ self.ultimo_numero_de_cuadro = 0
+ pilas.actores.Actor.__init__(self, 'fondos/pasto.png')
+ pilas.mundo.agregar_tarea_siempre(0.15,self.actualizar_video)
+
+ def actualizar_video(self):
+ cuadro, numero_de_cuadro = self.camara.obtener_imagen(self.ultimo_numero_de_cuadro)
+ self.ultimo_numero_de_cuadro = numero_de_cuadro
+ self.imagen.LoadFromPixels(640, 480, cuadro)
+ return True
+
+class VideoDeArchivo(object):
+ def __init__(self, ruta):
+ if opencv is None:
+ no_opencv()
+ return
+ if not os.path.isfile(ruta):
+ raise IOError('El archiyo no existe')
+ self._camara = highgui.cvCreateFileCapture(ruta)
+ self.fps = highgui.cvGetCaptureProperty(self._camara, highgui.CV_CAP_PROP_FPS)
+ self.altura = highgui.cvGetCaptureProperty(self._camara, highgui.CV_CAP_PROP_FRAME_HEIGHT)
+ self.ancho =highgui.cvGetCaptureProperty(self._camara, highgui.CV_CAP_PROP_FRAME_WIDTH)
+ super(VideoDeArchivo, self).__init__()
+
+ def obtener_imagen(self):
+ imagen_ipl = highgui.cvQueryFrame(self._camara)
+ imagen_ipl = opencv.cvGetMat(imagen_ipl)
+ return opencv.adaptors.Ipl2PIL(imagen_ipl).convert('RGBA').tostring()
+
+
+class DePelicula(pilas.actores.Actor):
+ """
+ Nos permite poner en pantalla un video desde un archivo.
+ Toma como parametro la ruta del video.
+ """
+ def __init__(self, path, ancho=640, alto=480):
+ self._camara = VideoDeArchivo(path)
+ pilas.actores.Actor.__init__(self)
+ self._altura_cuadro = self._camara.altura
+ self._ancho_cuadro = self._camara.ancho
+ subrect = self._actor.GetSubRect()
+ subrect.Right = self._ancho_cuadro
+ subrect.Bottom = self._altura_cuadro
+ self._actor.SetSubRect(subrect)
+ self.centro = ('centro', 'centro')
+ pilas.mundo.agregar_tarea_siempre(1/self._camara.fps,self.actualizar_video)
+
+ def actualizar_video(self):
+ self.imagen.LoadFromPixels(self._ancho_cuadro, self._altura_cuadro, self._camara.obtener_imagen())
+ return True
+'''
diff --git a/pilas/video/webcam.py b/pilas/video/webcam.py
new file mode 100644
index 0000000..b463aee
--- /dev/null
+++ b/pilas/video/webcam.py
@@ -0,0 +1,37 @@
+'''
+import pilas
+try:
+ import Image
+ import opencv
+ from opencv import highgui
+
+ GLOBALCAM=highgui.cvCreateCameraCapture(0)
+
+ for algo in range(30):
+ ULTIMO_CUADRO_BASURA = highgui.cvQueryFrame(GLOBALCAM)
+
+ ULTIMO_CUADRO_BASURA = opencv.adaptors.Ipl2PIL(opencv.cvGetMat(ULTIMO_CUADRO_BASURA)).convert('RGBA')
+except ImportError:
+ print "Falta la biblioteca opencv o PIL"
+ pass
+
+
+class __camara_buffer(object):
+ def __init__(self):
+ self._ultimo_numero_de_cuadro = 0
+ self._camera = GLOBALCAM
+ self._ultimo_cuadro = ULTIMO_CUADRO_BASURA.tostring()
+
+ def _obtener_imagen_de_camara(self):
+ imagen_ipl = highgui.cvQueryFrame(self._camera)
+ imagen_ipl = opencv.cvGetMat(imagen_ipl)
+ self._ultimo_cuadro = opencv.adaptors.Ipl2PIL(imagen_ipl).convert('RGBA').tostring()
+
+ def obtener_imagen(self, numero_de_cuadro=0):
+ if numero_de_cuadro == self._ultimo_numero_de_cuadro:
+ self._obtener_imagen_de_camara()
+ self._ultimo_numero_de_cuadro += 1
+ return self._ultimo_cuadro, self._ultimo_numero_de_cuadro
+
+CamaraWeb = __camara_buffer()
+'''
diff --git a/pilas/window_base.py b/pilas/window_base.py
new file mode 100644
index 0000000..7f99bc3
--- /dev/null
+++ b/pilas/window_base.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'data/window.ui'
+#
+# Created: Sun Sep 25 16:42:42 2011
+# by: PyQt4 UI code generator 4.8.3
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+
+try:
+ _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+ _fromUtf8 = lambda s: s
+
+class Ui_Window(object):
+ def setupUi(self, Window):
+ Window.setObjectName(_fromUtf8("Window"))
+ Window.resize(503, 477)
+ self.verticalLayout = QtGui.QVBoxLayout(Window)
+ self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.graphicsView = QtGui.QGraphicsView(Window)
+ brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
+ brush.setStyle(QtCore.Qt.NoBrush)
+ self.graphicsView.setBackgroundBrush(brush)
+ self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
+ self.verticalLayout.addWidget(self.graphicsView)
+ self.plainTextEdit = QtGui.QPlainTextEdit(Window)
+ self.plainTextEdit.setObjectName(_fromUtf8("plainTextEdit"))
+ self.verticalLayout.addWidget(self.plainTextEdit)
+
+ self.retranslateUi(Window)
+ QtCore.QMetaObject.connectSlotsByName(Window)
+
+ def retranslateUi(self, Window):
+ Window.setWindowTitle(QtGui.QApplication.translate("Window", "Form", None, QtGui.QApplication.UnicodeUTF8))
+ self.plainTextEdit.setPlainText(QtGui.QApplication.translate("Window", "import blabla\n"
+"", None, QtGui.QApplication.UnicodeUTF8))
+
diff --git a/pilas/xmlreader.py b/pilas/xmlreader.py
new file mode 100644
index 0000000..02fa8d3
--- /dev/null
+++ b/pilas/xmlreader.py
@@ -0,0 +1,33 @@
+from xml.dom import minidom
+
+class XmlNode:
+ """An XML node represents a single field in an XML document."""
+
+ def __init__(self, domElement):
+ """Construct an XML node from a DOM element."""
+ self.elem = domElement
+
+ def getData(self):
+ """Extract data from a DOM node."""
+ for child in self.elem.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ return str(child.data)
+ return None
+
+ def getAttributeValue(self, name):
+ """Returns the value of the attribute having the specified name."""
+ return str(self.elem.attributes[name].value)
+
+ def getChild(self, tag):
+ """Returns the first child node having the specified tag."""
+ return XmlNode(self.elem.getElementsByTagName(tag)[0])
+
+ def getChildren(self, tag):
+ """Returns a list of child nodes having the specified tag."""
+ return [XmlNode(x) for x in self.elem.getElementsByTagName(tag)]
+
+
+def makeRootNode(xmlFileName):
+ """Creates the root node from an XML file."""
+ return XmlNode(minidom.parse(xmlFileName))
+
diff --git a/pilas_plug.py b/pilas_plug.py
new file mode 100644
index 0000000..29b369b
--- /dev/null
+++ b/pilas_plug.py
@@ -0,0 +1,44 @@
+"""
+
+This command provides a plug to embed Qt.
+
+The X11 window ID is passed as the unique parameter.
+
+"""
+
+import os
+import sys
+
+base = os.environ['SUGAR_BUNDLE_PATH']
+
+qtpath = os.path.join(base, 'qt')
+sys.path.append(qtpath)
+
+# del sys.path[sys.path.index('/usr/lib/python2.7/site-packages')]
+
+from PyQt4.QtGui import QApplication
+from PyQt4.QtCore import QString
+from PyQt4.QtGui import QHBoxLayout
+from PyQt4.QtGui import QLineEdit
+from PyQt4.QtGui import QX11EmbedWidget
+
+import pilas
+from pilas import aplicacion
+
+app = QApplication(sys.argv)
+
+parent_window_id = int(sys.argv[1])
+screen_width = int(sys.argv[2])
+screen_height = int(sys.argv[3])
+
+window = QX11EmbedWidget()
+window.embedInto(parent_window_id)
+window.show()
+
+hbox = QHBoxLayout(window)
+pilas_height = 2.0 / 3 * screen_height
+pilas_width = 2.0 / 3 * screen_width
+pilas_widget = aplicacion.Window(parent=window, pilas_width=pilas_width, pilas_height=pilas_height)
+hbox.addWidget(pilas_widget)
+
+sys.exit(app.exec_())
diff --git a/qt/PyQt4/Qt.so b/qt/PyQt4/Qt.so
new file mode 100644
index 0000000..7eb8239
--- /dev/null
+++ b/qt/PyQt4/Qt.so
Binary files differ
diff --git a/qt/PyQt4/QtCore.so b/qt/PyQt4/QtCore.so
new file mode 100644
index 0000000..e6327c8
--- /dev/null
+++ b/qt/PyQt4/QtCore.so
Binary files differ
diff --git a/qt/PyQt4/QtGui.so b/qt/PyQt4/QtGui.so
new file mode 100644
index 0000000..7765d64
--- /dev/null
+++ b/qt/PyQt4/QtGui.so
Binary files differ
diff --git a/qt/PyQt4/__init__.py b/qt/PyQt4/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/qt/PyQt4/__init__.py
diff --git a/qt/lib/libQtCore.so.4 b/qt/lib/libQtCore.so.4
new file mode 100644
index 0000000..90d24ee
--- /dev/null
+++ b/qt/lib/libQtCore.so.4
Binary files differ
diff --git a/qt/lib/libQtGui.so.4 b/qt/lib/libQtGui.so.4
new file mode 100644
index 0000000..6a06081
--- /dev/null
+++ b/qt/lib/libQtGui.so.4
Binary files differ
diff --git a/qt/sip.so b/qt/sip.so
new file mode 100644
index 0000000..7e3e68a
--- /dev/null
+++ b/qt/sip.so
Binary files differ
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..5548d9a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+import sys
+from setuptools import setup
+from setuptools import find_packages
+from pilas import pilasversion
+
+def error(biblioteca, web):
+ print "Error, no se encuentra la biblioteca '%s' (de %s)" %(biblioteca, web)
+ sys.exit(1)
+
+
+try:
+ import Box2D
+except ImportError:
+ error("box2d", "http://code.google.com/p/pybox2d")
+
+
+setup(
+ name='pilas',
+ version=pilasversion.VERSION,
+ description='A simple to use video game framework.',
+ author='Hugo Ruscitti',
+ author_email='hugoruscitti@gmail.com',
+ install_requires=[
+ 'setuptools',
+ ],
+ packages=['pilas', 'pilas.actores', 'pilas.dispatch',
+ 'pilas.ejemplos', 'pilas.motores',
+ 'pilas.interfaz', 'pilas.video',
+ 'pilas.cargador'],
+ url='http://www.pilas-engine.com.ar',
+ include_package_data = True,
+ package_data = {
+ 'images': ['pilas/data/*', 'pilas/ejemplos/data/*',
+ 'pilas/data/fondos/*', 'pilas/data/juegobase/*',
+ 'pilas/data/juegobase/data/*',
+ 'pilas/cargador/ejemplos',
+ 'pilas/cargador/data'],
+ },
+
+ scripts=['bin/pilas'],
+
+ classifiers = [
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Education',
+ 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
+ 'Natural Language :: Spanish',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Games/Entertainment',
+ 'Topic :: Education',
+ 'Topic :: Software Development :: Libraries',
+ ],
+ )
+
diff --git a/setup_xo.py b/setup_xo.py
new file mode 100755
index 0000000..c24196e
--- /dev/null
+++ b/setup_xo.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2009, Simon Schampijer
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sugar.activity import bundlebuilder
+
+bundlebuilder.start()
diff --git a/utils/actualizar_pypi.py b/utils/actualizar_pypi.py
new file mode 100644
index 0000000..3068185
--- /dev/null
+++ b/utils/actualizar_pypi.py
@@ -0,0 +1,15 @@
+import os
+
+def renombrar_instalador_para_windows():
+ lista = os.listdir('dist/')
+ instalador_windows = [item for item in lista if item.endswith(".exe") and 'linux' in item]
+ nombre = "dist/" + instalador_windows[0]
+ os.rename(nombre, nombre.replace("linux-i686", "win32"))
+
+
+os.system("python setup.py sdist")
+os.system("python setup.py bdist_wininst")
+renombrar_instalador_para_windows()
+
+os.system("python setup.py sdist upload")
+os.system("python setup.py bdist_wininst upload")
diff --git a/utils/generar_resumen_api.py b/utils/generar_resumen_api.py
new file mode 100644
index 0000000..028df6f
--- /dev/null
+++ b/utils/generar_resumen_api.py
@@ -0,0 +1,59 @@
+# -*- encoding: utf-8 -*-
+# Genera el contenido del archivo pilas.rst que describe
+# toda la api de pilas y se agrega al manual como anexo.
+
+import os
+import re
+
+
+content = []
+
+for x in os.walk("../pilas"):
+
+ for filename in x[2]:
+ if filename.endswith(".py") and not filename.startswith("__"):
+ fullname = x[0] + '/' + filename[:-3]
+
+ fullname = fullname.replace("../", "")
+ fullname = fullname.replace("/", ".")
+
+ if "test" not in fullname and "__" not in fullname and "rope" not in fullname and "cargador" not in fullname:
+ title = fullname
+ content.append(title)
+ content.append("-" * len(title))
+
+ content.append("")
+ content.append("")
+
+ archivo = open(os.path.join(x[0], filename), "rt")
+
+ contenido = archivo.readlines()
+
+ for linea in contenido:
+ class_name = re.match("class (.+)\((\S*)\):", linea)
+
+ if class_name:
+ content.append("")
+ content.append("")
+ content.append("class **%s** (%s)" %(class_name.group(1), class_name.group(2)))
+ content.append("")
+
+ method = re.match("\s*def (.+)\((\S+)\):", linea)
+
+ if method:
+ argumentos = method.group(2).replace("*", "x")
+ argumentos = argumentos.replace("self", "")
+ content.append("- **%s** (%s)" %(method.group(1), argumentos))
+
+
+
+ archivo.close()
+ content.append("")
+
+
+
+print '\n'.join(content)
+print "Actualizando el archivo 'resumen_api.rst"
+archivo = open("resumen_api.rst", "wt")
+archivo.write("\n".join(content))
+archivo.close()
diff --git a/utils/resumen_api.pdf b/utils/resumen_api.pdf
new file mode 100644
index 0000000..dd48c11
--- /dev/null
+++ b/utils/resumen_api.pdf
Binary files differ
diff --git a/utils/resumen_api.rst b/utils/resumen_api.rst
new file mode 100644
index 0000000..373b3e2
--- /dev/null
+++ b/utils/resumen_api.rst
@@ -0,0 +1,1183 @@
+pilas.comportamientos
+---------------------
+
+
+
+
+class **Comportamiento** (object)
+
+- **actualizar** ()
+- **terminar** ()
+
+
+class **Girar** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Saltar** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Avanzar** (Comportamiento)
+
+- **actualizar** ()
+
+pilas.tareas
+------------
+
+
+
+
+class **Tarea** (object)
+
+- **ejecutar** ()
+- **eliminar** ()
+
+
+class **TareaCondicional** (Tarea)
+
+- **ejecutar** ()
+
+
+class **Tareas** (object)
+
+- **__init__** ()
+
+pilas.imagenes
+--------------
+
+
+- **cargar** (ruta)
+
+pilas.grupo
+-----------
+
+
+
+
+class **Grupo** (list)
+
+- **desordenar** ()
+- **limpiar** ()
+
+pilas.lienzo
+------------
+
+
+
+pilas.xmlreader
+---------------
+
+
+- **getData** ()
+- **makeRootNode** (xmlFileName)
+
+pilas.interpolaciones
+---------------------
+
+
+
+
+class **Interpolacion** (object)
+
+
+
+class **Lineal** (Interpolacion)
+
+- **__neg__** ()
+
+pilas.red
+---------
+
+
+- **setup** ()
+- **handle** ()
+- **finish** ()
+
+pilas.escenas
+-------------
+
+
+
+
+class **Escena** (object)
+
+- **__init__** ()
+- **iniciar** ()
+- **terminar** ()
+
+
+class **Normal** (Escena)
+
+
+pilas.atajos
+------------
+
+
+- **crear_grupo** (xk)
+
+pilas.fondos
+------------
+
+
+
+
+class **Fondo** (pilas.actores.Actor)
+
+
+
+class **Volley** (Fondo)
+
+- **__init__** ()
+
+
+class **Pasto** (Fondo)
+
+- **__init__** ()
+
+
+class **Selva** (Fondo)
+
+- **__init__** ()
+
+
+class **Tarde** (Fondo)
+
+- **__init__** ()
+
+
+class **Espacio** (Fondo)
+
+- **__init__** ()
+
+
+class **Noche** (Fondo)
+
+- **__init__** ()
+
+
+class **Color** (Fondo)
+
+
+pilas.control
+-------------
+
+
+
+
+class **Control** (object)
+
+- **__init__** ()
+- **actualizar** ()
+- **__init__** ()
+- **__str__** ()
+
+pilas.simbolos
+--------------
+
+
+
+pilas.fisica
+------------
+
+
+
+
+class **Fisica** (object)
+
+- **crear_bordes_del_escenario** ()
+- **reiniciar** ()
+- **cuando_suelta_el_mouse** ()
+- **actualizar** ()
+- **_procesar_figuras_a_eliminar** ()
+- **eliminar_suelo** ()
+- **eliminar_techo** ()
+- **eliminar_paredes** ()
+
+
+class **Figura** (object)
+
+- **obtener_x** ()
+- **obtener_y** ()
+- **obtener_rotacion** ()
+- **obtener_velocidad_lineal** ()
+- **detener** ()
+- **eliminar** ()
+
+
+class **Circulo** (Figura)
+
+
+
+class **Rectangulo** (Figura)
+
+
+
+class **ConstanteDeMovimiento** ()
+
+- **eliminar** ()
+
+
+class **ConstanteDeDistancia** ()
+
+- **eliminar** ()
+
+pilas.eventos
+-------------
+
+
+
+pilas.utils
+-----------
+
+
+- **es_interpolacion** (an_object)
+- **obtener_ruta_al_recurso** (ruta)
+- **interpolable** (f)
+- **obtener_area_de_texto** (texto)
+
+pilas.colisiones
+----------------
+
+
+- **__init__** ()
+- **verificar_colisiones** ()
+
+pilas.fps
+---------
+
+
+- **actualizar** ()
+- **obtener_cuadros_por_segundo** ()
+
+
+class **FPS** (object)
+
+- **actualizar** ()
+- **obtener_cuadros_por_segundo** ()
+
+pilas.sonidos
+-------------
+
+
+- **cargar** (ruta)
+
+pilas.colores
+-------------
+
+
+
+
+class **Color** (object)
+
+- **obtener** ()
+- **__str__** ()
+- **obtener_componentes** ()
+
+pilas.mundo
+-----------
+
+
+
+
+class **Mundo** (object)
+
+- **reiniciar** ()
+- **terminar** ()
+
+pilas.pytweener
+---------------
+
+
+
+
+class **Tweener** (object)
+
+- **hasTweens** ()
+- **finish** ()
+
+
+class **Tween** (object)
+
+- **decodeArguments** ()
+- **Remove** ()
+- **__init__** ()
+- **update** ()
+- **getRotation** ()
+- **complete** ()
+
+pilas.habilidades
+-----------------
+
+
+
+
+class **Habilidad** (object)
+
+- **actualizar** ()
+- **eliminar** ()
+
+
+class **RebotarComoPelota** (Habilidad)
+
+- **eliminar** ()
+
+
+class **RebotarComoCaja** (Habilidad)
+
+- **eliminar** ()
+
+
+class **ColisionableComoPelota** (RebotarComoPelota)
+
+- **actualizar** ()
+- **eliminar** ()
+
+
+class **SeguirAlMouse** (Habilidad)
+
+
+
+class **AumentarConRueda** (Habilidad)
+
+
+
+class **SeguirClicks** (Habilidad)
+
+
+
+class **Arrastrable** (Habilidad)
+
+- **comienza_a_arrastrar** ()
+- **termina_de_arrastrar** ()
+- **_el_receptor_tiene_fisica** ()
+
+
+class **MoverseConElTeclado** (Habilidad)
+
+
+
+class **PuedeExplotar** (Habilidad)
+
+- **eliminar_y_explotar** ()
+
+
+class **SeMantieneEnPantalla** (Habilidad)
+
+- **actualizar** ()
+
+
+class **PisaPlataformas** (Habilidad)
+
+- **actualizar** ()
+- **eliminar** ()
+
+
+class **Imitar** (Habilidad)
+
+- **actualizar** ()
+- **eliminar** ()
+
+pilas.estudiante
+----------------
+
+
+- **__init__** ()
+- **eliminar_habilidades** ()
+- **eliminar_comportamientos** ()
+- **actualizar_habilidades** ()
+- **actualizar_comportamientos** ()
+- **_adoptar_el_siguiente_comportamiento** ()
+
+pilas.pilasversion
+------------------
+
+
+
+pilas.ventana
+-------------
+
+
+
+pilas.camara
+------------
+
+
+
+
+class **Camara** (object)
+
+- **_get_x** ()
+- **_get_y** ()
+
+pilas.depurador
+---------------
+
+
+
+
+class **Depurador** (object)
+
+
+
+class **ModoDepurador** (object)
+
+- **orden_de_tecla** ()
+
+
+class **ModoPuntosDeControl** (ModoDepurador)
+
+
+
+class **ModoRadiosDeColision** (ModoDepurador)
+
+
+
+class **ModoArea** (ModoDepurador)
+
+
+
+class **ModoPosicion** (ModoDepurador)
+
+
+
+class **ModoFisica** (ModoDepurador)
+
+
+
+class **ModoInformacionDeSistema** (ModoDepurador)
+
+
+pilas.dispatch.saferef
+----------------------
+
+
+
+
+class **BoundMethodWeakref** (object)
+
+- **__str__** ()
+- **__call__** ()
+
+
+class **BoundNonDescriptorMethodWeakref** (BoundMethodWeakref)
+
+- **foo** ()
+- **__call__** ()
+
+pilas.dispatch.dispatcher
+-------------------------
+
+
+
+
+class **DictObj** (object)
+
+- **__str__** ()
+- **_make_id** (target)
+
+
+class **Signal** (object)
+
+- **esta_conectado** ()
+- **imprimir_funciones_conectadas** ()
+
+pilas.actores.pelota
+--------------------
+
+
+
+
+class **Pelota** (Actor)
+
+
+pilas.actores.animado
+---------------------
+
+
+
+
+class **Animado** (Actor)
+
+
+pilas.actores.nave
+------------------
+
+
+
+
+class **Nave** (Animacion)
+
+- **actualizar** ()
+- **eliminar_disparos_innecesarios** ()
+- **disparar** ()
+- **avanzar** ()
+
+pilas.actores.pausa
+-------------------
+
+
+
+
+class **Pausa** (Actor)
+
+
+pilas.actores.opcion
+--------------------
+
+
+
+
+class **Opcion** (Texto)
+
+- **seleccionar** ()
+
+pilas.actores.estrella
+----------------------
+
+
+
+
+class **Estrella** (Actor)
+
+
+pilas.actores.piedra
+--------------------
+
+
+
+
+class **Piedra** (Actor)
+
+- **actualizar** ()
+
+pilas.actores.aceituna
+----------------------
+
+
+
+
+class **Aceituna** (Actor)
+
+- **normal** ()
+- **reir** ()
+- **burlarse** ()
+- **gritar** ()
+- **saltar** ()
+
+pilas.actores.texto
+-------------------
+
+
+
+
+class **Texto** (Actor)
+
+- **obtener_texto** ()
+- **obtener_magnitud** ()
+- **obtener_color** ()
+
+pilas.actores.banana
+--------------------
+
+
+
+
+class **Banana** (Actor)
+
+- **abrir** ()
+- **cerrar** ()
+
+pilas.actores.globoelegir
+-------------------------
+
+
+
+
+class **GloboElegir** (Globo)
+
+
+pilas.actores.ejes
+------------------
+
+
+
+
+class **Ejes** (Actor)
+
+
+pilas.actores.cooperativista
+----------------------------
+
+
+
+
+class **Cooperativista** (Actor)
+
+
+
+class **Esperando** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Caminando** (Comportamiento)
+
+- **__init__** ()
+- **actualizar** ()
+- **avanzar_animacion** ()
+
+
+class **Saltando** (Comportamiento)
+
+- **__init__** ()
+- **actualizar** ()
+
+pilas.actores.dialogo
+---------------------
+
+
+- **iniciar** ()
+- **obtener_siguiente_dialogo_o_funcion** ()
+- **_eliminar_dialogo_actual** ()
+
+pilas.actores.disparo
+---------------------
+
+
+
+
+class **Disparo** (Animacion)
+
+- **actualizar** ()
+- **avanzar** ()
+
+pilas.actores.bomba
+-------------------
+
+
+
+
+class **Bomba** (Animacion)
+
+- **explotar** ()
+
+pilas.actores.pizarra
+---------------------
+
+
+
+
+class **Pizarra** (Actor)
+
+
+pilas.actores.tortuga
+---------------------
+
+
+
+
+class **Tortuga** (Actor)
+
+- **actualizar** ()
+- **dibujar_linea_desde_el_punto_anterior** ()
+- **bajalapiz** ()
+- **subelapiz** ()
+- **get_color** ()
+
+pilas.actores.mono
+------------------
+
+
+
+
+class **Mono** (Actor)
+
+- **sonreir** ()
+- **gritar** ()
+- **normal** ()
+
+pilas.actores.puntaje
+---------------------
+
+
+
+
+class **Puntaje** (Texto)
+
+- **obtener** ()
+
+pilas.actores.utils
+-------------------
+
+
+- **insertar_como_nuevo_actor** (actor)
+- **eliminar_un_actor** (actor)
+
+pilas.actores.mano
+------------------
+
+
+
+
+class **CursorMano** (Actor)
+
+- **_cargar_imagenes** ()
+
+pilas.actores.cursordisparo
+---------------------------
+
+
+
+
+class **CursorDisparo** (Actor)
+
+
+pilas.actores.actor
+-------------------
+
+
+- **obtener_centro** ()
+- **obtener_posicion** ()
+- **get_x** ()
+- **get_z** ()
+- **get_y** ()
+- **get_scale** ()
+- **get_rotation** ()
+- **get_espejado** ()
+- **get_transparencia** ()
+- **get_imagen** ()
+- **get_fijo** ()
+- **eliminar** ()
+- **destruir** ()
+- **actualizar** ()
+- **pre_actualizar** ()
+- **get_izquierda** ()
+- **get_derecha** ()
+- **get_abajo** ()
+- **get_arriba** ()
+- **obtener_rotacion** ()
+- **obtener_imagen** ()
+- **obtener_ancho** ()
+- **obtener_alto** ()
+- **__str__** ()
+- **obtener_escala** ()
+- **esta_fuera_de_la_pantalla** ()
+- **_eliminar_anexados** ()
+
+pilas.actores.explosion
+-----------------------
+
+
+
+
+class **Explosion** (Animacion)
+
+
+pilas.actores.animacion
+-----------------------
+
+
+
+
+class **Animacion** (Animado)
+
+- **obtener_velocidad_de_animacion** ()
+- **actualizar** ()
+
+pilas.actores.globo
+-------------------
+
+
+
+
+class **Globo** (Actor)
+
+- **eliminar** ()
+
+pilas.actores.temporizador
+--------------------------
+
+
+
+
+class **Temporizador** (Texto)
+
+- **funcion_vacia** ()
+- **_restar_a_contador** ()
+- **iniciar** ()
+
+pilas.actores.pingu
+-------------------
+
+
+
+
+class **Pingu** (Actor)
+
+
+
+class **Esperando** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Caminando** (Comportamiento)
+
+- **__init__** ()
+- **actualizar** ()
+- **avanzar_animacion** ()
+
+
+class **Saltando** (Comportamiento)
+
+- **__init__** ()
+- **actualizar** ()
+
+pilas.actores.menu
+------------------
+
+
+
+
+class **Menu** (Actor)
+
+- **activar** ()
+- **desactivar** ()
+- **seleccionar_primer_opcion** ()
+- **actualizar** ()
+- **seleccionar_opcion_actual** ()
+- **_deshabilitar_opcion_actual** ()
+
+pilas.actores.moneda
+--------------------
+
+
+
+
+class **Moneda** (Animacion)
+
+
+pilas.actores.martian
+---------------------
+
+
+
+
+class **Martian** (Actor)
+
+- **actualizar** ()
+- **crear_disparo** ()
+- **puede_saltar** ()
+
+
+class **Esperando** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Caminando** (Comportamiento)
+
+- **__init__** ()
+- **actualizar** ()
+- **avanzar_animacion** ()
+
+
+class **Saltando** (Comportamiento)
+
+- **actualizar** ()
+
+
+class **Disparar** (Comportamiento)
+
+- **actualizar** ()
+- **avanzar_animacion** ()
+
+pilas.actores.caja
+------------------
+
+
+
+
+class **Caja** (Actor)
+
+
+pilas.actores.boton
+-------------------
+
+
+
+
+class **Boton** (Actor)
+
+- **desconectar_normal_todo** ()
+- **desconectar_presionado_todo** ()
+- **desconectar_sobre_todo** ()
+- **ejecutar_funciones_normal** ()
+- **ejecutar_funciones_press** ()
+- **ejecutar_funciones_over** ()
+- **activar** ()
+- **desactivar** ()
+- **pintar_normal** ()
+- **pintar_sobre** ()
+
+pilas.actores.mapa
+------------------
+
+
+
+
+class **Mapa** (Actor)
+
+- **reiniciar** ()
+- **eliminar** ()
+- **_eliminar_bloques** ()
+
+pilas.actores.entradadetexto
+----------------------------
+
+
+
+
+class **EntradaDeTexto** (Actor)
+
+- **_actualizar_cursor** ()
+- **_actualizar_imagen** ()
+
+pilas.motores.motor_qt
+----------------------
+
+
+
+
+class **BaseActor** (object)
+
+- **__init__** ()
+- **obtener_posicion** ()
+- **obtener_escala** ()
+- **obtener_transparencia** ()
+- **obtener_rotacion** ()
+
+
+class **QtImagen** (object)
+
+- **ancho** ()
+- **alto** ()
+- **centro** ()
+- **avanzar** ()
+- **__str__** ()
+
+
+class **QtGrilla** (QtImagen)
+
+- **ancho** ()
+- **alto** ()
+- **avanzar** ()
+- **obtener_cuadro** ()
+
+
+class **QtTexto** (QtImagen)
+
+- **ancho** ()
+- **alto** ()
+
+
+class **QtLienzo** (QtImagen)
+
+- **__init__** ()
+
+
+class **QtSuperficie** (QtImagen)
+
+- **limpiar** ()
+
+
+class **QtActor** (BaseActor)
+
+- **obtener_imagen** ()
+- **reproducir** ()
+
+
+class **QtBase** (motor.Motor)
+
+- **__init__** ()
+- **pantalla_completa** ()
+- **pantalla_modo_ventana** ()
+- **esta_en_pantalla_completa** ()
+- **alternar_pantalla_completa** ()
+- **centro_fisico** ()
+- **obtener_area** ()
+- **centrar_ventana** ()
+- **actualizar_pantalla** ()
+- **obtener_centro_de_la_camara** ()
+- **obtener_lienzo** ()
+- **realizar_actualizacion_logica** ()
+- **escala** ()
+- **alternar_pausa** ()
+- **ocultar_puntero_del_mouse** ()
+- **__init__** ()
+- **__init__** ()
+- **_pintar_fondo_negro** ()
+
+pilas.motores.motor
+-------------------
+
+
+
+
+class **Motor** (object)
+
+- **__init__** ()
+- **ocultar_puntero_del_mouse** ()
+- **mostrar_puntero_del_mouse** ()
+- **cerrar_ventana** ()
+- **centrar_ventana** ()
+- **procesar_y_emitir_eventos** ()
+- **obtener_centro_de_la_camara** ()
+
+pilas.video.video
+-----------------
+
+
+
+
+class **MissingOpencv** (Exception)
+
+- **__init__** ()
+- **__str__** ()
+
+
+class **DeCamara** (pilas.actores.Actor)
+
+- **actualizar_video** ()
+
+
+class **VideoDeArchivo** (object)
+
+- **obtener_imagen** ()
+
+
+class **DePelicula** (pilas.actores.Actor)
+
+- **actualizar_video** ()
+
+pilas.video.webcam
+------------------
+
+
+
+
+class **__camara_buffer** (object)
+
+- **__init__** ()
+- **_obtener_imagen_de_camara** ()
+
+pilas.ejemplos.piezas
+---------------------
+
+
+
+
+class **Piezas** (pilas.escenas.Normal)
+
+
+
+class **Pieza** (pilas.actores.Animado)
+
+- **soltar_todas_las_piezas_del_grupo** ()
+- **soltar** ()
+- **__repr__** ()
+- **get_x** ()
+- **get_y** ()
+- **mostrar_arriba_todas_las_piezas** ()
+- **mostrar_abajo_todas_las_piezas** ()
+
+pilas.ejemplos.listaseleccion
+-----------------------------
+
+
+- **cuando_selecciona** (opcion)
+
+pilas.ejemplos.fisica
+---------------------
+
+
+
+
+class **ColisionesFisicas** (pilas.escenas.Normal)
+
+- **__init__** ()
+
+pilas.ejemplos.colisiones
+-------------------------
+
+
+
+
+class **Colisiones** (pilas.escenas.Normal)
+
+- **__init__** ()
+- **crear_personajes** ()
+
+pilas.interfaz.lista_seleccion
+------------------------------
+
+
+
+
+class **ListaSeleccion** (Actor)
+
+
+pilas.interfaz.selector
+-----------------------
+
+
+
+
+class **Selector** (pilas.actores.Actor)
+
+- **_cargar_imagenes** ()
+- **pintar_texto** ()
+- **deseleccionar** ()
+- **seleccionar** ()
+- **alternar_seleccion** ()
+
+pilas.interfaz.deslizador
+-------------------------
+
+
+
+
+class **Deslizador** (Actor)
+
+
+pilas.interfaz.boton
+--------------------
+
+
+
+
+class **Boton** (pilas.actores.Actor)
+
+- **_crear_imagenes_de_botones** ()
+
+pilas.interfaz.ingreso_de_texto
+-------------------------------
+
+
+
+
+class **IngresoDeTexto** (pilas.actores.Actor)
+
+- **_actualizar_cursor** ()
+- **cualquier_caracter** ()
+- **solo_numeros** ()
+- **solo_letras** ()
+- **_actualizar_imagen** ()
+
+pilas.data.juegobase.ejecutar
+-----------------------------
+
+