diff options
author | flavio <fdanesse@gmail.com> | 2011-11-26 23:51:04 (GMT) |
---|---|---|
committer | flavio <fdanesse@gmail.com> | 2011-11-26 23:51:04 (GMT) |
commit | d2bdd86293f62663389894d9d6100c829ddd68fc (patch) | |
tree | f38821aa03564673e229d7883449b2af5022f570 |
-rw-r--r-- | Enlace_Red_JAMtank.py | 179 | ||||
-rw-r--r-- | Graficos_JAMtank.py | 299 | ||||
-rw-r--r-- | Imagenes/JAMtank.png | bin | 0 -> 399222 bytes | |||
-rw-r--r-- | Imagenes/Tanque.bmp | bin | 0 -> 6778 bytes | |||
-rw-r--r-- | Imagenes/Tanque_Enemigo.bmp | bin | 0 -> 6778 bytes | |||
-rw-r--r-- | Imagenes/Teclado.png | bin | 0 -> 32239 bytes | |||
-rw-r--r-- | Imagenes/bala.bmp | bin | 0 -> 374 bytes | |||
-rw-r--r-- | Imagenes/explosion.png | bin | 0 -> 1255 bytes | |||
-rw-r--r-- | Imagenes/fondo.png | bin | 0 -> 153544 bytes | |||
-rw-r--r-- | Imagenes/tank1.png | bin | 0 -> 864 bytes | |||
-rw-r--r-- | Imagenes/tank2.png | bin | 0 -> 938 bytes | |||
-rw-r--r-- | JAMtank.py | 98 | ||||
-rw-r--r-- | Jugador_JAMtank.py | 225 | ||||
-rw-r--r-- | Manejador_de_Eventos.py | 96 | ||||
-rw-r--r-- | Sonidos/Juego.ogg | bin | 0 -> 87134 bytes | |||
-rw-r--r-- | Sonidos/Menu.ogg | bin | 0 -> 90525 bytes | |||
-rw-r--r-- | Sonidos/Tanque-M60.ogg | bin | 0 -> 17352 bytes | |||
-rw-r--r-- | Sonidos/explosion.ogg | bin | 0 -> 22121 bytes | |||
-rw-r--r-- | Sonidos/motor.ogg | bin | 0 -> 13787 bytes | |||
-rw-r--r-- | TCP_Server_thread_Stream.py | 234 | ||||
-rw-r--r-- | Variables.py | 84 | ||||
-rw-r--r-- | activity/JAMtank.svg | 141 | ||||
-rw-r--r-- | activity/activity.info | 7 | ||||
-rw-r--r-- | gtk_JAMtank_Ventana.py | 335 | ||||
-rw-r--r-- | setup.py | 3 |
25 files changed, 1701 insertions, 0 deletions
diff --git a/Enlace_Red_JAMtank.py b/Enlace_Red_JAMtank.py new file mode 100644 index 0000000..e905c75 --- /dev/null +++ b/Enlace_Red_JAMtank.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Enlace_Red_JAMtank.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import gc +gc.enable() + +import socket, time +import os +import sys + +from Jugador_JAMtank import Jugador_JAMtank + +TERMINATOR = "\r\n\r\n" + +class Enlace_Red_JAMtank(): + def __init__(self, ip="localhost", PUERTO=5000, protagonista=None, objetos_graficos=None, nombre=None): + # El socket con el server + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # La direccion de escucha del server + self.direccion_servidor = (ip, PUERTO) + self.socket.connect(self.direccion_servidor) + self.socket.setblocking(0) + + # Datos del Jugador Local + self.nombre = nombre # identifica al jugador local en el server + self.protagonista = protagonista # es el jugador local + + # Debe informarse de los cambios a los objetos gráficos + self.objetos_graficos = objetos_graficos + + # Diccionario de jugadores en el juego + self.jugadores = {} # Diccionario=> key=nombre:value=Jugador_JAMtank(pygame.sprite.Sprite) + + # Datos entrantes y salientes + self.buffer_de_salida = None + + # Latencia en la red + self.TIEMPO_ENVIO = 0.0 + self.TIEMPO_RECIBO = 0.0 + self.LATENCIA = 0.0 + + def desconectarse (self): + # cierra el socket, el server debe hacer finish + print "Desconectandose de la Red . . ." + self.socket.close() + time.sleep(0.2) + + def enviar_datos(self): + # Envia datos al server, el buffer es llenado desde JAMtank + #print "Enviando Datos en Enlace_Red_JAMtank: ", self.buffer_de_salida + # calculo de latencia + self.TIEMPO_ENVIO = time.time() + try: + self.socket.send(self.buffer_de_salida) + except socket.error, e: + #print "Error en Cliente al Enviar Datos: ", self.buffer_de_salida, "Error %s" % (e) + return + + self.buffer_de_salida = "" + + def get_jugadores_puntos(self): + # devuelve una lista puntos-nombre para armar un ranking + lista = [] + for jugador in self.jugadores.values(): + puntaje, nombre = jugador.puntaje, jugador.nombre + lista.append( (puntaje, nombre) ) + return lista + + def recibir_datos(self): + # recibe los datos del server (incluyendo los propios) + buffer_de_entrada = None + mensajes = None + try: + buffer_de_entrada = self.socket.recv(512) + mensajes = list(buffer_de_entrada.split(TERMINATOR)) + self.procesar_datos(mensajes) + except socket.error, e: + #print "Error en Cliente al Recibir Datos: ", buffer_de_entrada, "Error %s" % (e) + return + + ''' + # calculo de latencia + self.TIEMPO_RECIBO = time.time() + tiempo = self.TIEMPO_RECIBO-self.TIEMPO_ENVIO + if tiempo > self.LATENCIA: + self.LATENCIA = tiempo + print self.LATENCIA ''' + + def procesar_datos(self, lista_mesajes): + # Se recibe una lista de mensajes y se procesan según su tipo + + for mensaje in lista_mesajes: # para todos los mensajes + + valores = mensaje.split() # separar los datos + + if "T" in valores: + # Recibe los datos de posición de un tanque + try: + nombre, angulo, x, y = int(valores[0]), int(valores[2]), int(valores[3]), int(valores[4]) + except: + return + + if nombre not in self.jugadores.keys(): + # Si es un nuevo jugador, lo agrega al diccionario + if nombre == self.nombre: + # Si es el jugador local es el protagonista + self.jugadores[nombre] = self.protagonista + else: + # Si no es el jugador local + img = self.objetos_graficos.directorio_de_imagenes+"Tanque_Enemigo.bmp" + self.jugadores[nombre] = Jugador_JAMtank(imagen=img,nombre=nombre, angulo=angulo, x=x, y=y,resolucion_monitor=self.objetos_graficos.resolucion_monitor, + directorio_de_imagenes=self.objetos_graficos.directorio_de_imagenes) + self.jugadores[nombre].setup() + + self.objetos_graficos.tanques.add(self.jugadores[nombre]) + self.objetos_graficos.set_play_motor() + + # Ranking + #self.objetos_graficos.set_menu_jugadores(self.get_jugadores_puntos()) + + # Actualiza la posicion del tanque + if nombre != self.nombre: + self.jugadores[nombre].set_posicion(angulo=angulo, x=x, y=y) # actualiza la posición + + + elif "DES" in valores: + # un jugador se ha desconectado + nombre = int(valores[1]) + self.jugadores[nombre].kill() + del self.jugadores[nombre] + + elif "B" in valores: + # Recibe los datos de una bala + try: + nombre, angulo, x, y = int(valores[0]), int(valores[2]), int(valores[3]), int(valores[4]) + except: + return + self.jugadores[nombre].crear_disparo(nombre, angulo, (x,y)) + self.objetos_graficos.todas_las_balas.add(self.jugadores[nombre].bala) + self.objetos_graficos.set_play_bala() + + if nombre == self.nombre: + # si la bala es propia, se informa de que se recibió, cuando la bala desaparezca, se pondrá en None + self.protagonista.estado_bala = "recibida" + + elif "D" in valores: + # Recibe el puntaje de un jugador + # self.puntos = "%s %s %s %s%s" % (self.nombre, "D", energia, puntaje, TERMINATOR) + try: + nombre, energia, puntaje = int(valores[0]), int(valores[2]), int(valores[3]) + except: + return + self.jugadores[nombre].set_puntaje(energia, puntaje) + + elif "P" in valores: + pass + else: + if valores: + print "recibiendo cosas raras en el cliente:", valores + pass diff --git a/Graficos_JAMtank.py b/Graficos_JAMtank.py new file mode 100644 index 0000000..b338133 --- /dev/null +++ b/Graficos_JAMtank.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Graficos_JAMtank.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import pygame +from pygame.locals import * + +import gc +gc.enable() + +BLANCO = pygame.Color ("white")#(255, 255, 255, 1) +AMARILLO = pygame.Color ("yellow")#(255, 255, 0, 1) +ROJO = pygame.Color ("red")#(255, 0, 0, 1) + +# para las balas +MAGENTA = (255, 0, 255) +VELOCIDAD = 8 +from math import sin, cos, radians +from pygame.sprite import Sprite + +class Graficos_JAMtank(): + ''' Maneja todos los objetos gráficos y de audio del juego. ''' + + def __init__(self, resolucion_monitor, directorio_de_imagenes, directorio_de_sonidos): + # Monitor + self.resolucion_monitor = resolucion_monitor + + # Ventana y Escenario + self.directorio_de_imagenes = directorio_de_imagenes + self.fondo = None + self.ventana = None + + # Sonido + self.directorio_de_sonidos = directorio_de_sonidos + self.sonido_disparo = None + self.sonido_motor = None + self.sonido_juego = None + + # sprites y grupos de sprits + self.tanques = pygame.sprite.RenderUpdates() # todos los tanques en el juego + self.todas_las_balas = pygame.sprite.RenderUpdates() # todas las balas en el juego + self.textos = pygame.sprite.RenderUpdates() # información en pantalla para puntaje y energia de tanques + self.protagonista = None + self.explosiones = pygame.sprite.RenderUpdates() + + # Fuente y textos del menú energía y puntaje + self.fuente_menu = None + self.img_puntaje = pygame.sprite.Sprite() + self.img_energia = pygame.sprite.Sprite() + self.textos_jugadores = pygame.sprite.RenderUpdates() + + def set_menu_puntaje(self): + # Crea el menu de energía y puntaje del jugador local + puntos = 1000 + energia = 100 + + titulo_puntaje = pygame.sprite.Sprite() + titulo_energia = pygame.sprite.Sprite() + + x = 5 + titulo_puntaje.image = self.fuente_menu.render("Puntos:", 1, (BLANCO)) + titulo_puntaje.rect = titulo_puntaje.image.get_rect() + titulo_puntaje.rect.x = x + titulo_puntaje.rect.y = 0 + self.textos.add(titulo_puntaje) + + x += int(titulo_puntaje.rect.width + 10) + self.img_puntaje.image = self.fuente_menu.render(str(puntos), 1, (AMARILLO)) + self.img_puntaje.rect = self.img_puntaje.image.get_rect() + self.img_puntaje.rect.x = x + self.img_puntaje.rect.y = 0 + self.textos.add(self.img_puntaje) + + x += int(self.img_puntaje.rect.width + 10) + titulo_energia.image = self.fuente_menu.render("Energia:", 1, (BLANCO)) + titulo_energia.rect = titulo_energia.image.get_rect() + titulo_energia.rect.x = x + titulo_energia.rect.y = 0 + self.textos.add(titulo_energia) + + x += int(titulo_energia.rect.width + 10) + self.img_energia.image = self.fuente_menu.render(str(energia), 1, (AMARILLO)) + self.img_energia.rect = self.img_energia.image.get_rect() + self.img_energia.rect.x = x + self.img_energia.rect.y = 0 + self.textos.add(self.img_energia) + + def set_menu_jugadores(self, lista): + # Crea el menu de energía y puntaje del jugador local + for texto in self.textos_jugadores: + texto.kill() + + #lista.sort() + ancho_pantalla = self.resolucion_monitor[0] + distancia = 10 + y = 0 + for elemento in lista: + puntos, nombre = elemento + + img_nombre = pygame.sprite.Sprite() + img_puntaje = pygame.sprite.Sprite() + + img_puntaje.image = self.fuente_menu.render(str(puntos), 1, (AMARILLO)) + img_puntaje.rect = img_puntaje.image.get_rect() + img_nombre.image = self.fuente_menu.render(str(nombre), 1, (AMARILLO)) + img_nombre.rect = img_nombre.image.get_rect() + + img_puntaje.rect.x = ancho_pantalla - img_puntaje.rect.w - distancia - img_nombre.rect.w + img_puntaje.rect.y = y + + img_nombre.rect.x = ancho_pantalla - img_nombre.rect.w + img_nombre.rect.y = y + + self.textos_jugadores.add(img_nombre) + self.textos_jugadores.add(img_puntaje) + + y += 20 + + def set_menu_ip_server(self, ip): + # Para mostrar la ip del servidor del juego + texto = "IP del Servidor: %s" %(ip) + + img_texto = pygame.sprite.Sprite() + img_texto.image = self.fuente_menu.render(str(texto), 1, (BLANCO)) + img_texto.rect = img_texto.image.get_rect() + + img_texto.rect.x = 0 + img_texto.rect.y = 860 + + self.textos.add(img_texto) + + def actualiza_puntaje(self, puntos=0, energia=100): + # Actualiza en pantalla el puntaje y la energia del jugador local + self.img_puntaje.image = self.fuente_menu.render(str(puntos), 1, (AMARILLO)) + color = AMARILLO + if energia < 50: color = ROJO + self.img_energia.image = self.fuente_menu.render(str(energia), 1, (color)) + + def actualizar(self): + # dibuja los sprites y el fondo + + # Actualiza la imagen de los tanques + self.tanques.clear(self.ventana, self.fondo) + cambios = self.tanques.draw(self.ventana) + + # Actualiza la imagen del menú + self.textos.clear(self.ventana, self.fondo) + cambios.extend( self.textos.draw(self.ventana) ) + + # Actualiza las balas en pantalla + if self.todas_las_balas: + self.todas_las_balas.clear(self.ventana, self.fondo) + for bala in self.todas_las_balas: + x, y = bala.Actualizar() + if x < 0 or x > self.resolucion_monitor[0] or y < 0 or y > self.resolucion_monitor[1]: + # Si se sale de la pantalla se elimina + + for tanque in self.tanques: + # buscar entre los tanques al que tenga el mismo nombre y poner bala en None + if tanque.nombre == bala.autor: + tanque.bala = None + bala.kill() + else: + # si no se sale de la pantalla ver si le pega a alguien + self.detectar_colisiones(bala) + cambios.extend( self.todas_las_balas.draw(self.ventana) ) + + # Actualiza las explosiones + if self.explosiones: + self.explosiones.clear(self.ventana, self.fondo) + for explosion in self.explosiones: + explosion.Actualizar() + cambios.extend( self.explosiones.draw(self.ventana) ) + + # Actualiza la imagen del ranking + self.textos_jugadores.clear(self.ventana, self.fondo) + cambios.extend( self.textos_jugadores.draw(self.ventana) ) + + pygame.display.update(cambios) # dibuja el personaje y las balas + + '''# efecto sello + self.tanques.draw(self.ventana) + pygame.display.update()''' + + def detectar_colisiones(self, bala): + # Detecta las colisiones entre balas y tanques. + for tanque in self.tanques: + if bala.autor != tanque.nombre: + # si la bala no pertenece al tanque que está tocando. + if bala.rect.colliderect(tanque.rect): + # Si la bala toca al tanque + + if tanque == self.protagonista: + # Si me pegan a mi, pierdo energia + self.protagonista.energia -= 10 + self.protagonista.nuevos_datos_puntos = True + + elif bala.autor == self.protagonista.nombre: + # si yo le pego a otro, cobro punto + tanque.energia -= 10 + if tanque.energia < 1: + tanque.energia = 100 + self.protagonista.puntaje += 1 + self.protagonista.nuevos_datos_puntos = True + else: + # si se pegan entre ellos + pass + + # dibujar explosión + angulo, x, y = bala.get_posicion() + bala.kill() + self.explosiones.add(Explosion(x=x, y=y, image_explosion=self.image_explosion, sonido_explosion=self.sonido_explosion)) + + for tanque1 in self.tanques: + # buscar entre los tanques al que tenga el mismo nombre y poner bala en None + if tanque1.nombre == bala.autor: + tanque1.bala = None + else: + # si la bala pertenece a este tanque no hacer nada. + pass + + def set_play_motor(self): + self.sonido_motor.play(-1) + + def set_play_bala(self): + self.sonido_disparo.play() + + def iniciar_escenario(self): + # levanta el Escenario + print "Iniciando el Escenario de JAMtank" + self.ventana.blit(self.fondo, (0,0)) + pygame.display.update() + + def setup(self, Jugador_JAMtank=None): + # Configurar objetos gráficos y de sonido para el juego + print "Seteando Modo Gráfico, Sonidos y Fuentes para JAMtank" + pygame.init() + self.protagonista=Jugador_JAMtank + # Monitor y Ventana + pygame.display.set_mode(self.resolucion_monitor, 0, 0) + pygame.display.set_caption("JAMtank - fdanesse@hotmail.com - CeibalJAM! - Uruguay") + # Ventana y Escenario + self.fondo = pygame.transform.scale(pygame.image.load(self.directorio_de_imagenes+"fondo.png"), self.resolucion_monitor).convert_alpha() + self.ventana = pygame.display.get_surface() # la ventana del juego + # Mouse + pygame.mouse.set_visible(False) + # Sonido + pygame.mixer.init(44100, -16, 2, 2048) + pygame.mixer.music.set_volume(1.0)
+ self.sonido_disparo = pygame.mixer.Sound(self.directorio_de_sonidos+"Tanque-M60.ogg") + self.sonido_motor = pygame.mixer.Sound(self.directorio_de_sonidos+"motor.ogg") + self.sonido_juego = pygame.mixer.Sound(self.directorio_de_sonidos+"Juego.ogg") + self.sonido_juego.play(-1) + # Explosiones + self.sonido_explosion = pygame.mixer.Sound(self.directorio_de_sonidos+"explosion.ogg") + self.image_explosion = pygame.image.load(self.directorio_de_imagenes+"explosion.png").convert_alpha() + # Fuentes + pygame.font.init() + self.fuente_menu = pygame.font.Font(pygame.font.match_font('Purisa', True, False), 25) + + # http://efectos-de-sonido.anuncios-radio.com/gratis/ + # http://www.tuwebdeinformatica.com/Crearjuegos/massprites3.html + +class Explosion(pygame.sprite.Sprite):
+# Las Balas del tanque.
+ def __init__(self, x=0, y=0, image_explosion=None, sonido_explosion=None): + + pygame.sprite.Sprite.__init__(self) + + self.image = image_explosion
+ self.rect = self.image.get_rect() + self.rect.center = (x, y) + self.contador = 0 + self.sonido_explosion = sonido_explosion + self.sonido_explosion.play()
+ self.Actualizar() + + def Actualizar(self):
+ self.contador += 1 + if self.contador == 5: + self.kill() + diff --git a/Imagenes/JAMtank.png b/Imagenes/JAMtank.png Binary files differnew file mode 100644 index 0000000..ff5690d --- /dev/null +++ b/Imagenes/JAMtank.png diff --git a/Imagenes/Tanque.bmp b/Imagenes/Tanque.bmp Binary files differnew file mode 100644 index 0000000..45168b9 --- /dev/null +++ b/Imagenes/Tanque.bmp diff --git a/Imagenes/Tanque_Enemigo.bmp b/Imagenes/Tanque_Enemigo.bmp Binary files differnew file mode 100644 index 0000000..ece7165 --- /dev/null +++ b/Imagenes/Tanque_Enemigo.bmp diff --git a/Imagenes/Teclado.png b/Imagenes/Teclado.png Binary files differnew file mode 100644 index 0000000..ae56fec --- /dev/null +++ b/Imagenes/Teclado.png diff --git a/Imagenes/bala.bmp b/Imagenes/bala.bmp Binary files differnew file mode 100644 index 0000000..f6f0248 --- /dev/null +++ b/Imagenes/bala.bmp diff --git a/Imagenes/explosion.png b/Imagenes/explosion.png Binary files differnew file mode 100644 index 0000000..9d87bfd --- /dev/null +++ b/Imagenes/explosion.png diff --git a/Imagenes/fondo.png b/Imagenes/fondo.png Binary files differnew file mode 100644 index 0000000..b655013 --- /dev/null +++ b/Imagenes/fondo.png diff --git a/Imagenes/tank1.png b/Imagenes/tank1.png Binary files differnew file mode 100644 index 0000000..ea012a0 --- /dev/null +++ b/Imagenes/tank1.png diff --git a/Imagenes/tank2.png b/Imagenes/tank2.png Binary files differnew file mode 100644 index 0000000..e0c38a2 --- /dev/null +++ b/Imagenes/tank2.png diff --git a/JAMtank.py b/JAMtank.py new file mode 100644 index 0000000..a957c5a --- /dev/null +++ b/JAMtank.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# JAMtank.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay +# +# 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 + +import pygame +from pygame.locals import * + +import gc +gc.enable() + +import os + +# Constantes para todo el Juego +RESOLUCIONMONITOR = (1200,900) +DIRECTORIODEIMAGENES = os.getcwd()+"/Imagenes/" +DIRECTORIODESONIDOS = os.getcwd()+"/Sonidos/" +MINOMBRE = os.getpid() # me identifica en el servidor + +# importación de clases para objetos del juego en el cliente +from Graficos_JAMtank import Graficos_JAMtank +from Manejador_de_Eventos import Manejador_de_Eventos +from Jugador_JAMtank import Jugador_JAMtank +from Enlace_Red_JAMtank import Enlace_Red_JAMtank + +class JAMtank(): + ''' Motor del Juego. Máquina de estados. ''' + def __init__(self, ip="localhost", PUERTO=5000): + # Todo lo que deba ser dibujado se hace en esta clase + self.objetos_graficos = Graficos_JAMtank(RESOLUCIONMONITOR, DIRECTORIODEIMAGENES, DIRECTORIODESONIDOS) + # Todos los eventos se manejan en esta clase + self.objetos_de_eventos = Manejador_de_Eventos() + # El Jugador Local + self.protagonista = Jugador_JAMtank(imagen=DIRECTORIODEIMAGENES+"Tanque.bmp", nombre=MINOMBRE, + angulo=0, x=0, y=0, resolucion_monitor=RESOLUCIONMONITOR, + directorio_de_imagenes=DIRECTORIODEIMAGENES) # recordar que es un tanque sprite + + # Enlace con el servidor en la red + self.enlace_red = Enlace_Red_JAMtank(ip=ip, PUERTO=PUERTO, protagonista=self.protagonista, objetos_graficos=self.objetos_graficos, nombre=MINOMBRE) + self.reloj = pygame.time.Clock() + + self.Run() # ejecuta el juego. + + def Run(self): + # ----------------------- Comienza el juego --------------------------- + + self.objetos_graficos.setup(Jugador_JAMtank=self.protagonista) # Iniciando pygame + self.objetos_graficos.iniciar_escenario() # Levantando Escenario y Fondo + self.objetos_graficos.set_menu_puntaje() # Levantando Menús para Puntajes + + self.objetos_de_eventos.setup(Jugador_JAMtank=self.protagonista, Enlace_Red=self.enlace_red) # Configurando Eventos para el Jugador Local + self.protagonista.setup() # Configurando al jugador protagónico (el local) + self.objetos_graficos.set_menu_ip_server(self.enlace_red.direccion_servidor[0]) + pygame.time.wait(3) + self.estado = "En Juego" + + #contar = 0 + while self.estado == "En Juego": + self.reloj.tick(35) + + # Detectar eventos y calcular en consecuencia + self.objetos_de_eventos.detectar() + + # recolectar datos para enviar, recordar que los objetos se actualizan cuando los datos vuelven del server + self.enlace_red.buffer_de_salida = self.protagonista.get_datos_para_la_red() + + self.enlace_red.enviar_datos()# enviar datos + self.enlace_red.recibir_datos()# recibir datos + + # Actualiza los datos de puntaje y energia del jugador local en pantalla + self.objetos_graficos.actualiza_puntaje(puntos=self.protagonista.puntaje, energia=self.protagonista.energia) + + # ranking + self.objetos_graficos.set_menu_jugadores(self.enlace_red.get_jugadores_puntos()) + + # Dibuja los cambios en pantalla + self.objetos_graficos.actualizar() + pygame.time.wait(1) + +if __name__ == "__main__": + JAMtank("localhost") + diff --git a/Jugador_JAMtank.py b/Jugador_JAMtank.py new file mode 100644 index 0000000..4f81aa2 --- /dev/null +++ b/Jugador_JAMtank.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Jugador_JAMtank.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import pygame +from pygame.locals import * + +import os +from math import sin, cos, radians
+ +VELOCIDAD = 10 +INDICE_ROTACION = 5 +TERMINATOR = "\r\n\r\n" + +# para las balas +MAGENTA = (255, 0, 255) + +from math import sin, cos, radians +from pygame.sprite import Sprite + +class Jugador_JAMtank(pygame.sprite.Sprite): + ''' Clase Jugador, tanto para datos como objetos gráficos. + En esta clase no se dibuja, se calcula y guarda los datos del jugador y del sprite que lo representa''' + + def __init__(self, imagen=None, nombre=None, angulo=0, x=0, y=0, resolucion_monitor=(1200,900), directorio_de_imagenes=None): + + pygame.sprite.Sprite.__init__(self) + + self.directorio_de_imagenes = directorio_de_imagenes + + # monitor y ventana + self.ancho_monitor, self.alto_monitor = resolucion_monitor + self.imagen = imagen + self.imagen_original = None + + # Atributos del sprite + self.image = None + self.rect = None + + # datos para gráficos + self.angulo = 0 + self.x = self.ancho_monitor / 2 + self.y = self.alto_monitor / 2 + self.dx, self.dy = self.get_vector(self.angulo) # distancia en x,y + + # Datos no gráficos, puntajes del jugador + self.energia = 100 + self.puntaje = 0 + self.nuevos_datos_puntos = False + + # Balas de jugador local en juego. + self.estado_bala = None + self.bala = None + self.nuevo_mensaje_bala = None + + # Datos para la Red + self.nombre = nombre # self.nick + self.nuevo_mensaje_posicion = None # para comparar con el ultimo mensaje y si hay cambios enviar. + self.ultimo_mensaje_posicion = None + + def setup(self): + # Levanta los objetos gráficos del jugador. (Solo se ejecuta al principio del juego) + print "Seteando Jugador para JAMtank\n" + self.imagen_original = pygame.transform.scale(pygame.image.load(self.imagen), (50,50)).convert_alpha() + # Atributos del sprite + self.image = self.imagen_original.copy() + self.rect = self.image.get_rect() + self.nuevo_mensaje_posicion = "%s %s %s %s %s%s" % (self.nombre, "T", self.angulo, self.rect.x, self.rect.y, TERMINATOR) # sólo lo usa el local + + def set_posicion(self, angulo=0, x=0, y=0): + # Se reciben los datos desde el server, se actualiza la posición del tanque, se guardan los datos en ultimo mensaje + self.angulo = angulo + self.rect.x = x + self.rect.y = y + self.image = pygame.transform.rotate(self.imagen_original, -self.angulo) + + def get_datos_para_la_red(self): + # devuelve los nuevos datos formateados para ser enviados + buffer_de_salida = "" + if self.nuevo_mensaje_posicion != self.ultimo_mensaje_posicion: + # Si ha cambiado la posición o el angulo + self.ultimo_mensaje_posicion = self.nuevo_mensaje_posicion + buffer_de_salida += self.nuevo_mensaje_posicion + + if self.nuevo_mensaje_bala and self.estado_bala == "disparada": + # Si se ha disparado una bala + buffer_de_salida += self.nuevo_mensaje_bala + self.estado_bala = "enviada" + + if self.nuevos_datos_puntos: + # pasar puntaje y energía + mensaje_puntos = "%s %s %s%s" % ("D", self.energia, self.puntaje, TERMINATOR) + buffer_de_salida += mensaje_puntos + self.nuevos_datos_puntos = False + + if buffer_de_salida: + # Si hay datos + return buffer_de_salida + else: + return "%s%s" % ("P", TERMINATOR) + + def set_puntaje(self, energia, puntaje): + # actualiza puntaje y energía + self.energia, self.puntaje = energia, puntaje + if self.energia < 1: + self.energia = 100 + self.set_posicion(angulo=0, x=0, y=0) + self.nuevo_mensaje_posicion = "%s %s %s %s %s%s" % (self.nombre, "T", self.angulo, self.rect.x, self.rect.y, TERMINATOR) + + # ------------------- Inicio de Cálculos en Función de los Eventos Recibidos (esto no actualiza el sprite ----------------------- + def evento(self, tecla): + # El tanque local responde a la captura de teclas + # mandar girar, se suma o resta grados + if tecla == K_RIGHT: + self.angulo += int(0.7 * INDICE_ROTACION) + self.image = pygame.transform.rotate(self.imagen_original, -self.angulo) + elif tecla == K_LEFT: + self.angulo -= int(0.7 * INDICE_ROTACION) + self.image = pygame.transform.rotate(self.imagen_original, -self.angulo) + + # mandar mover, se calcula x e y segun la direccion de movimiento. + if tecla == K_UP: + self.dx, self.dy = self.get_vector(self.angulo) + self.actualizar_posicion() + elif tecla == K_DOWN: + x, y = self.get_vector(self.angulo) + self.dx = x * -1 + self.dy = y * -1 + self.actualizar_posicion() + + # Si hay eventos de posición, hay cambios y debe informarse + self.nuevo_mensaje_posicion = "%s %s %s %s %s%s" % (self.nombre, "T", self.angulo, self.rect.x, self.rect.y, TERMINATOR) + + # Disparar + if tecla == K_LCTRL: + if not self.bala and self.estado_bala == "recibida": + self.estado_bala = None + if not self.bala and self.estado_bala == None: + (x, y) = self.rect.center + self.nuevo_mensaje_bala = "%s %s %s %s %s%s" % (self.nombre, "B", self.angulo, x, y, TERMINATOR) + self.estado_bala = "disparada" + #print "Bala", self.estado_bala, self.nuevo_mensaje_bala + + + + def get_vector(self, angulo): + # Recibe un ángulo que da orientación al tanque + # Devuelve el incremento de puntos x,y en su desplazamiento + x = int(cos(radians(angulo)) * VELOCIDAD)
+ y = int(sin(radians(angulo)) * VELOCIDAD) + return x,y + + def actualizar_posicion(self): + # cambia la posicion del rectangulo + # Solo se ejecuta si el tanque se mueve hacia adelante o hacia atras + # no se ejecuta cuando está girando en un mismo lugar + x = self.x + self.dx
+ y = self.y + self.dy + + if x > 0 and x < self.ancho_monitor and y > 0 and y < self.alto_monitor: + self.x += self.dx + self.y += self.dy + self.rect.x = int(self.x) - self.rect.w / 2
+ self.rect.y = int(self.y) - self.rect.h / 2 + # ------------------- Fin de Cálculos en Función de los Eventos Recibidos ---------------------------- + + def crear_disparo(self, autor, angulo, origen_disparo): + # Genera un disparo + self.bala = Bala(autor=autor, angulo=angulo, origen_disparo=origen_disparo, directorio_de_imagenes=self.directorio_de_imagenes) + #print "crear disparo: ", autor, angulo, origen_disparo + +VELOCIDADBALA = 18 +class Bala(pygame.sprite.Sprite):
+# Las Balas del tanque.
+ def __init__(self, autor=None, angulo=0, origen_disparo=(0,0), directorio_de_imagenes=None): + + pygame.sprite.Sprite.__init__(self) + + self.directorio_de_imagenes = directorio_de_imagenes + self.autor = autor
+ self.imagen_original = pygame.image.load(self.directorio_de_imagenes+"bala.bmp") + self.image = self.imagen_original.copy()
+ self.image.set_colorkey(MAGENTA)
+ self.rect = self.image.get_rect() + + self.angulo = angulo + + (self.rect.x, self.rect.y) = origen_disparo + self.dx, self.dy = self.get_vector(angulo) +
+ self.Actualizar() + + def get_posicion(self): + # devuelve la posicion actual + return self.angulo, self.rect.x, self.rect.y + + def get_vector(self, angulo): + # Recibe un ángulo que da orientación al disparo + # Devuelve el incremento de puntos x,y + self.angulo = angulo + x = int(cos(radians(angulo)) * VELOCIDADBALA)
+ y = int(sin(radians(angulo)) * VELOCIDADBALA) + return x,y + + def Actualizar(self):
+ self.rect.x += self.dx
+ self.rect.y += self.dy + return self.rect.x, self.rect.y diff --git a/Manejador_de_Eventos.py b/Manejador_de_Eventos.py new file mode 100644 index 0000000..5e1b510 --- /dev/null +++ b/Manejador_de_Eventos.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Manejador_de_Eventos.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import pygame +from pygame.locals import * + +import gc +gc.enable() + +class Manejador_de_Eventos(): + ''' Maneja todos los eventos del juego. ''' + def __init__(self): + pass + + def setup(self, Jugador_JAMtank=None, Enlace_Red=None): + # Configurar objetos de eventos para el juego y asociarlos al jugador local + + self.red = Enlace_Red + print "Seteando Eventos de Usuario para JAMtank" + self.protagonista = Jugador_JAMtank + pygame.event.set_blocked([MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN, JOYAXISMOTION, JOYBALLMOTION, JOYHATMOTION, JOYBUTTONUP, JOYBUTTONDOWN, + KEYUP, VIDEORESIZE, VIDEOEXPOSE, USEREVENT, QUIT, ACTIVEEVENT]) # bloqueados + pygame.event.set_allowed([KEYDOWN]) # permitidos + pygame.key.set_repeat(15, 15) # repetición de teclas + + def detectar(self): + # detecta si hay eventos + hayTeclas = False + for event in pygame.event.get(): + if event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: + hayTeclas = True + if hayTeclas: + self.Manejar_Eventos() + pygame.event.clear() + + def Manejar_Eventos(self): + # Maneja los eventos del usuario + teclas = pygame.key.get_pressed() + + # girar en movimiento + if teclas[pygame.K_UP] and teclas[pygame.K_RIGHT]:# adelante - derecha + self.protagonista.evento(pygame.K_UP) + self.protagonista.evento(pygame.K_RIGHT) + elif teclas[pygame.K_UP] and teclas[pygame.K_LEFT]:# adelante - izquierda + self.protagonista.evento(pygame.K_UP) + self.protagonista.evento(pygame.K_LEFT) + elif teclas[pygame.K_DOWN] and teclas[pygame.K_RIGHT]:# atras - derecha + self.protagonista.evento(pygame.K_DOWN) + self.protagonista.evento(pygame.K_LEFT) + elif teclas[pygame.K_DOWN] and teclas[pygame.K_LEFT]:# atras - izquierda + self.protagonista.evento(pygame.K_DOWN) + self.protagonista.evento(pygame.K_RIGHT) + + # moverse sin girar + elif teclas[pygame.K_UP] and not teclas[pygame.K_DOWN]:# adelante + self.protagonista.evento(pygame.K_UP) + elif teclas[pygame.K_DOWN] and not teclas[pygame.K_UP]:# atras + self.protagonista.evento(pygame.K_DOWN) + + # girar sin moverse + elif teclas[pygame.K_RIGHT] and not teclas[pygame.K_LEFT]:# derecha + self.protagonista.evento(pygame.K_RIGHT) + elif teclas[pygame.K_LEFT] and not teclas[pygame.K_RIGHT]:# izquierda + self.protagonista.evento(pygame.K_LEFT) + + # Disparar. + if teclas[pygame.K_LCTRL]: + self.protagonista.evento(pygame.K_LCTRL) + # salir + elif teclas[pygame.K_ESCAPE]: + print "*** Cerrando pygame y Saliendo del Juego ***" + self.red.desconectarse() + pygame.quit() + import sys + sys.exit() + else: + pass + diff --git a/Sonidos/Juego.ogg b/Sonidos/Juego.ogg Binary files differnew file mode 100644 index 0000000..6d3a054 --- /dev/null +++ b/Sonidos/Juego.ogg diff --git a/Sonidos/Menu.ogg b/Sonidos/Menu.ogg Binary files differnew file mode 100644 index 0000000..d4fa3b3 --- /dev/null +++ b/Sonidos/Menu.ogg diff --git a/Sonidos/Tanque-M60.ogg b/Sonidos/Tanque-M60.ogg Binary files differnew file mode 100644 index 0000000..741528e --- /dev/null +++ b/Sonidos/Tanque-M60.ogg diff --git a/Sonidos/explosion.ogg b/Sonidos/explosion.ogg Binary files differnew file mode 100644 index 0000000..bf6e6a1 --- /dev/null +++ b/Sonidos/explosion.ogg diff --git a/Sonidos/motor.ogg b/Sonidos/motor.ogg Binary files differnew file mode 100644 index 0000000..66f3e0b --- /dev/null +++ b/Sonidos/motor.ogg diff --git a/TCP_Server_thread_Stream.py b/TCP_Server_thread_Stream.py new file mode 100644 index 0000000..93cf2ee --- /dev/null +++ b/TCP_Server_thread_Stream.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# TCP_Server_thread_Stream.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import time +import socket +import threading +import SocketServer + +import time +from SocketServer import StreamRequestHandler +from SocketServer import ThreadingTCPServer + +import Variables as ESPEJO + +class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.ThreadingTCPServer): + ''' Servidor Multihilo para conexiones simultáneas. ''' + pass + ''' + __init__(self, server_address, RequestHandlerClass) from TCPServer + fileno(self) from TCPServer + finish_request(self, request, client_address) from TCPServer + get_request(self) from TCPServer + handle_error(self, request, client_address) from TCPServer + handle_request(self) from TCPServer + process_request(self, request, client_address) from ThreadingMixIn + serve_forever(self) from TCPServer + server_activate(self) from TCPServer + server_bind(self) from TCPServer + verify_request(self, request, client_address) from TCPServer + ''' + ''' + def __init__(self, server_address, handler_class=None): + SocketServer.TCPServer.__init__(self, server_address, handler_class) + return + + def server_activate(self): + SocketServer.TCPServer.server_activate(self) + return + + def serve_forever(self): + while True: + self.handle_request() + return + + def handle_request(self): + return SocketServer.TCPServer.handle_request(self) + + def verify_request(self, request, client_address): + return SocketServer.TCPServer.verify_request(self, request, client_address) + + def process_request(self, request, client_address): + return SocketServer.TCPServer.process_request(self, request, client_address) + + def server_close(self): + return SocketServer.TCPServer.server_close(self) + + def finish_request(self, request, client_address): + return SocketServer.TCPServer.finish_request(self, request, client_address) + + def close_request(self, request_address): + return SocketServer.TCPServer.close_request(self, request_address) ''' + +class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler): + ''' Atiende y procesa todas las conexiones entrantes + __del__(self) from BaseRequestHandler + __init__(self, request, client_address, server) from BaseRequestHandler + finish(self) + handle(self) from BaseRequestHandler + setup(self) + def __init__(self, request, client_address, server): + SocketServer.StreamRequestHandler.__init__(self, request, client_address, server) + return ''' + + def handle(self): + # Acepta la conexión de un jugador + while 1: + try: + datos = self.request.recv(200) + self.recibir_datos(datos) + self.enviar_datos() + + except socket.error, e: + print "Error en Handle del Server: ", "Error %s" % (e) + self.request.close() + return + + def recibir_datos(self, datos): + # Recibe los datos del jugador conectado + mensajes = str(datos).split(ESPEJO.TERMINATOR) + #print "Datos Recibidos en Servidor: ", mensajes + + for mensaje in mensajes: + valores = mensaje.split() + if "T" in valores: + # Se recibe un tanque + try: + # para evitar que falle si no vienen todos los datos + nombre, angulo, x, y = int(valores[0]), int(valores[2]), int(valores[3]), int(valores[4]) + self.procesar_datos_tanques(nombre=nombre, angulo=angulo, x=x, y=y) + except: + return + + elif "B" in valores: + # Se recibe una bala + try: + angulo, x, y = int(valores[2]), int(valores[3]), int(valores[4]) + self.procesar_datos_balas(angulo=angulo, x=x, y=y) + except: + return + + elif "D" in valores: + # self.procesar_datos_puntos(mensaje) + # mensaje_puntos = "%s %s %s%s" % ("D", self.energia, self.puntaje, TERMINATOR) + energia, puntaje = int(valores[1]), int(valores[2]) + self.procesar_datos_puntos(energia=energia, puntaje=puntaje) + + elif "P" in valores: + # simple presencia en la red + pass + + else: + #print "Recibiendo Otras Cosas: ", mensaje, "***" + pass + + # ---------------------inicio PERSISTENCIA DE DATOS ----------------------------------- + def procesar_datos_puntos(self, energia=100, puntaje=0): + # hay cambios de puntaje o energía + ESPEJO.Tanques[self.client_address[0]].set_puntos(energia=energia, puntaje=puntaje) + ESPEJO.Tanques[self.client_address[0]].set_jugadores_por_informar_puntaje(ESPEJO.Tanques.keys()) + + def procesar_datos_balas(self, angulo=0, x=0, y=0): + # ha llegado una bala + ESPEJO.Tanques[self.client_address[0]].set_bala(angulo=angulo, x=x, y=y) # crea la bala + ESPEJO.Tanques[self.client_address[0]].set_jugadores_por_informar_disparo(ESPEJO.Tanques.keys()) + # pasa la lista de todos los jugadores para informar del disparo, a medida que se informa, se eliminan de la lista + + def procesar_datos_tanques(self, nombre=None, angulo=0, x=0, y=0): + # Actualiza los datos del tanque de este jugador. Si el Jugador no existe, lo agrega. + if nombre == None: + # si no hay nombre no puede procesarse + return + + if not self.client_address[0] in ESPEJO.Tanques.keys(): + # Si no existe, se agrega + ESPEJO.Tanques[self.client_address[0]] = ESPEJO.Tanque(nombre) + ESPEJO.Tanques[self.client_address[0]].set_datos_tanque(nombre=nombre, angulo=angulo, x=x, y=y) + else: + # Si existe se actualiza + ESPEJO.Tanques[self.client_address[0]].set_datos_tanque(nombre=nombre, angulo=angulo, x=x, y=y) + # ---------------------fin PERSISTENCIA DE DATOS ----------------------------------- + + def enviar_datos(self): + # A quien se conectó se le envían los datos de todos, también los suyos. + + mensaje = "" + for jugador in ESPEJO.Tanques.values(): # para todos los jugadores. + mensaje += jugador.get_datos_tanque() # obtener las posiciones de tanques. + + bala = jugador.get_bala() + if bala != None and self.client_address[0] in jugador.jugadores_por_informar_disparo: + # Si hay una bala en juego de este jugador y el que se ha conectado no ha sido informado: + mensaje += bala + jugador.jugadores_por_informar_disparo.remove(self.client_address[0]) # borrar de la lista, ya se le informó + if not jugador.jugadores_por_informar_disparo: jugador.bala = None + + if jugador.jugadores_por_informar_puntaje: + if self.client_address[0] in jugador.jugadores_por_informar_puntaje: + # Si hay cambios de puntaje y el que se ha conectado no ha sido informado: + mensaje += jugador.get_puntos() + jugador.jugadores_por_informar_puntaje.remove(self.client_address[0]) # borrar de la lista, ya se le informó + + if self.client_address[0] in ESPEJO.Tanques.keys(): + # evitar error de escaner de red de los clientes + if ESPEJO.Tanques[self.client_address[0]].desconectados: + # si hay información de desconexión de algún jugador + mensaje += ESPEJO.Tanques[self.client_address[0]].desconectados + ESPEJO.Tanques[self.client_address[0]].desconectados = "" + + if len(mensaje) > ESPEJO.CARGA: + ESPEJO.CARGA = len(mensaje) + print "Carga en la red: ", ESPEJO.CARGA + self.request.send(mensaje) + + def setup(self): + # Llamado antes de la handle() para realizar acciones de inicialización necesaria. La implementación predeterminada no hace nada. + #print "Server Setup" + #return SocketServer.BaseRequestHandler.setup(self) + pass + + def finish(self): + # Llamado despues de la handle() para realizar cualquier método de limpieza. La implementación predeterminada no hace nada. + if self.client_address[0] in ESPEJO.Tanques.keys(): + # si está en la lista, porque da error cuando los clientes escanean la red en busca de servers, pero no están en el juego aun. + print "Server Finish" + for jugador in ESPEJO.Tanques.values(): + # para informar a todos los jugadores sobre la desconexión de otro jugador + jugador.desconectados += "DES %s%s" % (ESPEJO.Tanques[self.client_address[0]].nombre, ESPEJO.TERMINATOR) + del ESPEJO.Tanques[self.client_address[0]] + #return SocketServer.BaseRequestHandler.finish(self) + return + + + +if __name__ == "__main__": + server = ThreadedTCPServer(("localhost", 5000), ThreadedTCPRequestHandler) + server.serve_forever() + + ''' + import SocketServer + class MyHandler(SocketServer.BaseRequestHandler): + def handle(self): + while 1: + dataReceived = self.request.recv(1024) + self.request.send(dataReceived) + myServer = SocketServer.ThreadingTCPServer(('',8881), MyHandler) + myServer.serve_forever( )''' diff --git a/Variables.py b/Variables.py new file mode 100644 index 0000000..7858e6f --- /dev/null +++ b/Variables.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Variables.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +TERMINATOR = "\r\n\r\n" +Tanques={} +CARGA = 0 + +class Tanque(): + def __init__(self, nombre): + + # Datos del Tanque + self.nombre = nombre + self.angulo, self.x, self.y = 0, 0, 0 + self.ultimo_angulo, self.ultima_x, self.ultima_y = 0, 0, 0 + + # persistencia balas + self.jugadores_por_informar_disparo = None + self.bala = None + + # persistencia puntos y energía + self.puntos = None + self.jugadores_por_informar_puntaje = None + + # Cuando un jugador se desconecta, se debe avisar a todos + self.desconectados = "" + +# ---------------- inicio DATOS POSICION TANQUE --------------------------- + def get_datos_tanque(self): + # devuelve los datos de posición del tanque. (Se llama desde enviar_datos() en class UDPServer_thread) + self.angulo, self.x, self.y = self.ultimo_angulo, self.ultima_x, self.ultima_y + mensaje_posicion = "%s %s %s %s %s%s" % (self.nombre, "T", self.angulo, self.x, self.y, TERMINATOR) + return mensaje_posicion + + def set_datos_tanque(self, nombre=None, angulo=0, x=0, y=0): + # guarda los datos de posición del tanque. (Se llama desde recibir_datos() en class UDPServer_thread) + self.ultimo_angulo, self.ultima_x, self.ultima_y = int(angulo), int(x), int(y) +# ---------------- fin DATOS POSICION TANQUE ------------------------------ + + def set_puntos(self, energia=100, puntaje=0): + # establece los datos iniciales de la bala + self.puntos = "%s %s %s %s%s" % (self.nombre, "D", energia, puntaje, TERMINATOR) + #print "Nuevo puntaje en Espejo: ", self.puntos + + def get_puntos(self): + # devuelve los datos de la bala + return self.puntos + + def set_jugadores_por_informar_puntaje(self, jugadores_por_informar_puntaje): + # Lista de jugadores a los que hay que informar sobre un disparo + self.jugadores_por_informar_puntaje = jugadores_por_informar_puntaje + +# ---------------- inicio DATOS BALAS ------------------------------------- + def set_bala(self, angulo=0, x=0, y=0): + # establece los datos iniciales de la bala + self.bala = "%s %s %s %s %s%s" % (self.nombre, "B", angulo, x, y, TERMINATOR) + + def get_bala(self): + # devuelve los datos de la bala + return self.bala + + def set_jugadores_por_informar_disparo(self, jugadores_por_informar_disparo): + # Lista de jugadores a los que hay que informar sobre un disparo + self.jugadores_por_informar_disparo = jugadores_por_informar_disparo + +# ---------------- FIN DATOS BALAS ------------------------------------- + diff --git a/activity/JAMtank.svg b/activity/JAMtank.svg new file mode 100644 index 0000000..618d8b9 --- /dev/null +++ b/activity/JAMtank.svg @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) --> + +<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" + enable-background="new 0 0 50.878 48.833" + height="48.833" + id="Icon" + inkscape:version="0.47pre4 r22446" + overflow="visible" + sodipodi:docname="JAMtank.svg" + version="1.1" + viewBox="0 0 50.878 48.833" + width="50.878" + xml:space="preserve"><metadata + id="metadata17"><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 /></cc:Work></rdf:RDF></metadata><defs + id="defs15"><inkscape:perspective + id="perspective19" + inkscape:persp3d-origin="25.438999 : 16.277667 : 1" + inkscape:vp_x="0 : 24.4165 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="50.877998 : 24.4165 : 1" + sodipodi:type="inkscape:persp3d" /><inkscape:perspective + id="perspective2841" + inkscape:persp3d-origin="0.5 : 0.33333333 : 1" + inkscape:vp_x="0 : 0.5 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="1 : 0.5 : 1" + sodipodi:type="inkscape:persp3d" /></defs><sodipodi:namedview + bordercolor="#666666" + borderopacity="1" + gridtolerance="10" + guidetolerance="10" + id="namedview13" + inkscape:current-layer="Icon" + inkscape:cx="-21.770898" + inkscape:cy="25.160153" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-height="690" + inkscape:window-maximized="1" + inkscape:window-width="1366" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:zoom="6.8346077" + objecttolerance="10" + pagecolor="#ffffff" + showgrid="false" /> + + + + + +<rect + height="7.1541862" + id="rect3805" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:0.92739450999999995;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="23.516075" + x="13.826815" + y="23.801159" /><rect + height="6.9323936" + id="rect3690" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:1.02922772999999990;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="14.801596" + x="18.184059" + y="13.943574" /><path + d="M 20.713446,19.939156 2.9140599,24.061119 48.255655,24.248482 29.51946,19.751793 20.713446,19.939156 z" + id="path3688" + style="fill:#ffffff;stroke:#004980;stroke-width:1.02922772999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><g + id="g3807" + style="fill:#ffffff;stroke:#004980;stroke-opacity:1" + transform="matrix(0.18547891,0,0,0.18547891,-69.705397,-46.721509)"><g + id="g3769" + style="fill:#ffffff;stroke:#004980;stroke-opacity:1"><rect + height="16.120926" + id="rect3692-84" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="384.96368" /><rect + height="16.120926" + id="rect3692-8-8" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="402.25772" /><rect + height="16.120926" + id="rect3692-6-9" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="419.01105" /></g><g + id="g3769-8" + style="fill:#ffffff;stroke:#004980;stroke-opacity:1" + transform="translate(-184.64788,-1.6142185)"><rect + height="16.120926" + id="rect3692-84-9" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="384.96368" /><rect + height="16.120926" + id="rect3692-8-8-8" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="402.25772" /><rect + height="16.120926" + id="rect3692-6-9-9" + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + width="57.578693" + x="577.28711" + y="419.01105" /></g></g> +<rect + style="fill:#ffffff;fill-opacity:1;stroke:#004980;stroke-width:1.02922785000000006;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3600" + width="10.115947" + height="3.9398949" + x="20.473646" + y="16.112465" /><path + d="m 21.672004,27.386792 1.961038,-8.023013 c 0.04585,-1.964554 3.743612,-2.0258 3.914271,-0.12471 l 1.414286,8.399268 -7.289595,-0.251545 z" + id="path3817" + sodipodi:nodetypes="ccccc" + style="fill:#ffffff;stroke:#004980;stroke-width:1.05081022000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /><path + d="m 528.30977,368.48892 a 26.263966,26.263966 0 1 1 -52.52793,0 26.263966,26.263966 0 1 1 52.52793,0 z" + id="path3686" + sodipodi:cx="502.04581" + sodipodi:cy="368.48892" + sodipodi:rx="26.263966" + sodipodi:ry="26.263966" + sodipodi:type="arc" + style="fill:#ffffff;stroke:#004980;stroke-width:11.30000019000000044;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + transform="matrix(0.13541442,0,0,0.13237365,-42.71265,-20.225608)" /></svg>
\ No newline at end of file diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..e2e721f --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,7 @@ +[Activity] +name = JAMtank +activity_version = 1 +service_name = org.laptop.JAMtank +icon = JAMtank +show_launcher = yes +class = gtk_JAMtank_Ventana.GTK_JAMtank diff --git a/gtk_JAMtank_Ventana.py b/gtk_JAMtank_Ventana.py new file mode 100644 index 0000000..f8947ca --- /dev/null +++ b/gtk_JAMtank_Ventana.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# gtk_JAMtank_Ventana.py por: +# Flavio Danesse <fdanesse@gmail.com> +# CeibalJAM! - Uruguay - Plan Ceibal +# +# 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 + +import pygame +from pygame.locals import * + +import gc +gc.enable() + +import socket +import SocketServer +import gobject +import threading + +import gtk +import pygtk +pygtk.require("2.0") + +from sugar.activity import activity + +import time +import string +import sys +import dbus +import os +import commands +import threading + +from JAMtank import JAMtank +from TCP_Server_thread_Stream import ThreadedTCPServer, ThreadedTCPRequestHandler + +DIRECTORIODEIMAGENES = os.getcwd()+"/Imagenes/" +DIRECTORIODESONIDOS = os.getcwd()+"/Sonidos/" +PUERTO = 5000 + +# ------ Inicio de Funciones de Presencia ---------- +OLPC_SESSION_BUS = "/tmp/olpc-session-bus" +PRESENCE_SERVICE = "org.laptop.Sugar.Presence" +PRESENCE_PATH = "/org/laptop/Sugar/Presence" +PRESENCE_IFACE = "org.laptop.Sugar.Presence" +BUDDY_IFACE = "org.laptop.Sugar.Presence.Buddy" + +def get_bus(): +# sugar presence + if os.path.exists(OLPC_SESSION_BUS): + address = "unix:path=%s" % OLPC_SESSION_BUS + return dbus.bus.BusConnection(address_or_type=address) + else: + if len(sys.argv) != 2: + sys.exit(1) + + if 'DBUS_SESSION_BUS_ADDRESS' in os.environ: + del os.environ['DBUS_SESSION_BUS_ADDRESS'] + + display = sys.argv[1] + os.environ['DISPLAY'] = display + return dbus.bus.BusConnection() +def get_Xos(): +# devuelve la ip y el nombre de todas las xo en la red +# En realidad devuelve el nick de todas las xo, la ip local y las ip en la mesh de las demás xo + bus = get_bus() + ps = bus.get_object(PRESENCE_SERVICE, PRESENCE_PATH) + ps_iface = dbus.Interface(ps, PRESENCE_IFACE) + buddies = map(lambda b: bus.get_object(PRESENCE_SERVICE, b), ps_iface.GetBuddies()) + + xos = [] + for buddy in buddies: + buddy_iface = dbus.Interface(buddy, BUDDY_IFACE) + try: + props = buddy_iface.GetProperties() + ip = "%s" % (props['ip4-address'].encode('utf-8')) + nick = "%s" % (props['nick'].encode('utf-8')) + xo = (ip, nick) + xos.append(xo) + + except dbus.DBusException: + pass + return xos +# ------ Fin de Funciones de Presencia ---------- + + +class GTK_JAMtank(activity.Activity): +# Interface gtk + def __init__(self, handle): + + activity.Activity.__init__(self, handle, False) + + self.set_title("-.JAMtank (versión 1).- -. CeibalJAM! 2010.-") + + pygame.mixer.init(44100, -16, 2, 2048) + pygame.mixer.music.set_volume(1.0)
+ self.sonido = pygame.mixer.Sound(DIRECTORIODESONIDOS+"Menu.ogg") + + self.jamtank = None + self.servidor = None + self.modo = None + self.estado = "Presentacion" + self.nick = None + self.lista_servidores = None + self.ip = None # ip local + self.ip_server = None # ip servidor + + # Canvas + self.contenedor_principal = gtk.VBox() + + # imagen Creditos + imagen = gtk.Image() + imagen.set_from_file(DIRECTORIODEIMAGENES + "JAMtank.png") + self.contenedor_principal.pack_start(imagen, False, False, 0) + + # Lista de Servidores Activos + # self.contenedor_principal.pack_start(gtk.Label("Servidores JAMtank Actualmente Activos en la Red"), False, False, 0) + + # Lista de Servidores Activos + self.caja_servers = gtk.VBox() + viewport = gtk.ScrolledWindow() + viewport.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + viewport.add_with_viewport(self.caja_servers) + + frame = gtk.Frame() + frame.add(viewport) + + # busca servidores JAMtank en la red + self.actualiza_servers(None) + + # Botones servidor local, actualizar lista de servidores y salir + caja_botones = gtk.VBox() + + boton_servidor = gtk.Button("Crear Nuevo Juego en Red") + boton_servidor.connect("clicked", self.set_Estado, "servidor", self.ip) + boton_servidor.connect_object("clicked", gtk.Widget.destroy, self) + + boton_scan = gtk.Button("Actualizar Lista de Juegos en la Red") + boton_scan.connect("clicked", self.actualiza_servers) + + boton_salir = gtk.Button("Salir de JAMtank") + boton_salir.connect_object("clicked", self.salir, None) + + caja_botones.pack_start(boton_servidor, True, True, 5) + caja_botones.pack_start(boton_scan, True, True, 5) + caja_botones.pack_end(boton_salir, True, True, 5) + + # imagen Teclado + imagen1 = gtk.Image() + imagen1.set_from_file(DIRECTORIODEIMAGENES + "Teclado.png") + + caja_secundaria = gtk.HBox() + caja_secundaria.pack_start(imagen1, False, False, 0) + caja_secundaria.pack_start(frame, True, True, 5) + caja_secundaria.pack_end(caja_botones, False, False, 5) + + self.contenedor_principal.pack_start(caja_secundaria, True, True, 0) + + # para introducir una IP conocida + #caja = gtk.HBox() + #etiqueta = gtk.Label("IP: ") + self.entrada = None + #conectar = gtk.Button("Conectarse a Esta IP") + #conectar.connect("clicked", self.get_connect_ip_conocida) + #caja.pack_start (etiqueta, False, False, 5) + #caja.pack_start (self.entrada, False, False, 5) + #caja.pack_start (conectar, False, False, 5) + #self.contenedor_principal.pack_start(caja, False, False, 5) + + self.set_canvas(self.contenedor_principal) + + self.show_all() + + self.sonido.play(loops=-1) + + # switch gtk pygame + reloj = gobject.timeout_add(500, self.get_Juego) + + def set_entrada_ip_conocida(self): + # objetos para agregar ip de servidor conocido + caja = gtk.HBox() + etiqueta = gtk.Label("Conectarse a (IP): ") + self.entrada = gtk.Entry() + conectar = gtk.Button("Jugar") + conectar.connect("clicked", self.get_connect_ip_conocida) + caja.pack_start (etiqueta, True, True, 5) + caja.pack_start (self.entrada, True, True, 5) + caja.pack_end (conectar, True, True, 5) + caja.show_all() + self.caja_servers.pack_start(caja, False, False, 5) + + def get_connect_ip_conocida(self, widget): + # Conecta a la ip ingresada + ip = self.entrada.get_text() + if ip: + self.set_Estado(None, "cliente", ip) + gtk.Widget.destroy(self) + + def actualiza_servers(self, widget): + # Verifica tener conexión a una red + + self.lista_servidores = [] + + # obtiene el nombre de la xo + XOs = get_Xos() + self.ip, self.nick = XOs[0] + + while not self.ip or self.ip == "localhost": + # obtiene el nombre de la xo + XOs = get_Xos() + self.ip, self.nick = XOs[0] + self.get_avisar_no_hay_ip() + + red = self.ip.split(".") + for x in range(2, 254): + ip = "%s.%s.%s.%s" % (red[0],red[1],red[2],x) + thread = threading.Thread( target=self.scan_red, args=( ip, PUERTO) )
+ thread.start() + time.sleep(0.02) + + self.get_lista_xos() + + def scan_red (self, ip, puerto): + # escanea la red en busca del puerto abierto para JAMtant + conexion = socket.socket() + try: + conexion.connect( (ip, puerto) ) + self.lista_servidores.append(ip) + except: + pass + conexion.close() + + def get_lista_xos(self): + # arma la lista de xo en la red con server JAMtank corriendo + + for child in self.caja_servers: + self.caja_servers.remove(child) + + self.set_entrada_ip_conocida() + + for ip in self.lista_servidores: + caja = gtk.HBox() + lab_ip = gtk.Label(ip) + conectar = gtk.Button("Jugar") + + conectar.connect("clicked", self.set_Estado, "cliente", ip) + conectar.connect_object("clicked", gtk.Widget.destroy, self) + + caja.pack_start(lab_ip, True, True, 5) + caja.pack_end(conectar, True, True, 5) + caja.show_all() + self.caja_servers.pack_start(caja, False, False, 5) + + def get_avisar_no_hay_ip(self): + # Crea un cuadro de dialogo para avisar que no está conectado a ninguna red. + + dialog = gtk.Dialog("Atención !!!", None, gtk.DIALOG_MODAL, None) + + etiqueta0 = gtk.Label("No estás conectado a ninguna Red.") + dialog.vbox.pack_start(etiqueta0, True, True, 5) + + etiqueta1 = gtk.Label("JAMtank es un juego en red, debes conectarte a una Red") + dialog.vbox.pack_start(etiqueta1, True, True, 5) + + dialog.add_button("OK", 1) + dialog.add_button("Cancelar", 2) + + dialog.show_all() + + if dialog.run() == 1: + pass + elif dialog.run() == 2: + # sale automaticamente + sys.exit() + + dialog.destroy() + + def delete_event(self, widget, event, data=None): + # Cierra la ventana gtk + return gtk.FALSE + + def salir(self, widget): + sys.exit() + + def set_Estado(self, widget, modo, ip): + # para poder llamar al juego luego de que se cierre la ventana gtk + self.sonido.stop() + pygame.quit() + self.ip_server = ip + self.modo = modo # Servidor o Cliente + self.estado = "Juego" + + #----------------------Juego pygame------------------------------------------------------------- + def get_Juego(self): + # Se llama al juego pygame + if self.estado == "Juego": + if self.modo == "servidor": + server = ThreadedTCPServer((self.ip, PUERTO), ThreadedTCPRequestHandler) + server.allow_reuse_address = True + server.socket.setblocking(0) + + server_thread = threading.Thread(target=server.serve_forever) + server_thread.setDaemon(True) + server_thread.start() + + self.jamtank = JAMtank(ip=self.ip, PUERTO=PUERTO) + + elif self.modo == "cliente": + self.jamtank = JAMtank(ip=self.ip_server, PUERTO=PUERTO) + + return False # no volver por acá + else: + pass + + return True # seguir viniendo por acá + #----------------------Juego pygame------------------------------------------------------------- + + +if __name__ == "__main__": + GTK_JAMtank() + + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6ea61a6 --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +#!/usr/bin/python +from sugar.activity import bundlebuilder +bundlebuilder.start() |