1 |
#!/usr/bin/env python |
#!/usr/bin/env python |
2 |
# |
# |
3 |
#Minë. Mundo Interactivo-Narrativo en Español [Tierra-Media] |
#Minë. Mundo Interactivo-Narrativo en Español [Tierra-Media] |
4 |
#Copyright (C) 2002 Pablo Ruiz Múzquiz |
#Copyright (C) 2002 Pablo Ruiz Múzquiz |
5 |
# |
# |
6 |
# |
# |
7 |
#This program is free software; you can redistribute it and/or modify |
#This program is free software; you can redistribute it and/or modify |
8 |
#it under the terms of the GNU General Public License as published by |
#it under the terms of the GNU General Public License as published by |
9 |
#the Free Software Foundation; either version 2 of the License, or |
#the Free Software Foundation; either version 2 of the License, or |
10 |
#(at your option) any later version. |
#(at your option) any later version. |
11 |
# |
# |
12 |
#This program is distributed in the hope that it will be useful, |
#This program is distributed in the hope that it will be useful, |
13 |
#but WITHOUT ANY WARRANTY; without even the implied warranty of |
#but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
#GNU General Public License for more details. |
#GNU General Public License for more details. |
16 |
# |
# |
17 |
#You should have received a copy of the GNU General Public License |
#You should have received a copy of the GNU General Public License |
18 |
#along with this program; if not, write to the Free Software |
#along with this program; if not, write to the Free Software |
19 |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 |
# |
# |
21 |
# |
# |
22 |
#Fichero: interfazpj.py |
#Fichero: interfazpj.py |
23 |
|
|
24 |
"""Clase InterfazPJ.""" |
"""Clase InterfazPJ.""" |
25 |
|
|
26 |
from controlador import * |
from controlador import * |
27 |
from connection import * |
from connection import * |
28 |
from dialogo import * |
from dialogo import * |
29 |
from dialogoinicial import * |
from dialogoinicial import * |
30 |
from mundo import * |
from mundo import * |
31 |
from utils import * |
from utils import * |
32 |
|
|
33 |
|
|
34 |
class InterfazPJ(Controlador): |
class InterfazPJ(Controlador): |
35 |
"""Interfaz que interactúa con el jugador a través de una conexion socket. |
"""Interfaz que interactúa con el jugador a través de una conexion socket. |
36 |
|
|
37 |
Controla directamente a una instancia de clase PersonajeJugador. Por ello, |
Controla directamente a una instancia de clase PersonajeJugador. Por ello, |
38 |
implementa el interfaz Controlador y recibe todos los eventos generados |
implementa el interfaz Controlador y recibe todos los eventos generados |
39 |
por los personajes. También recibe los eventos del socket. |
por los personajes. También recibe los eventos del socket. |
40 |
|
|
41 |
Una vez inicializada, crea un dialogo con el jugador y le pasa el control. |
Una vez inicializada, crea un dialogo con el jugador y le pasa el control. |
42 |
Todos los eventos recibidos se los pasa al dialogo actual, y éste responde a |
Todos los eventos recibidos se los pasa al dialogo actual, y éste responde a |
43 |
los mismos. La respuesta puede incluir cambiar a otro dialogo distinto. |
los mismos. La respuesta puede incluir cambiar a otro dialogo distinto. |
44 |
|
|
45 |
También se encarga de recibir los ticks del reloj global de Mundo, y pasárselos |
También se encarga de recibir los ticks del reloj global de Mundo, y pasárselos |
46 |
al diálogo si éste los acepta. |
al diálogo si éste los acepta. |
47 |
|
|
48 |
Contiene los siguientes atributos: |
Contiene los siguientes atributos: |
49 |
|
|
50 |
- __conn (Connection): la conexion por la cual se comunica |
- __conn (Connection): la conexion por la cual se comunica |
51 |
- __personaje (Personaje): el personaje que controlamos |
- __personaje (Personaje): el personaje que controlamos |
52 |
- __dialogo (Dialogo): el dialogo actual con el jugador |
- __dialogo (Dialogo): el dialogo actual con el jugador |
53 |
|
|
54 |
Invariantes: |
Invariantes: |
55 |
__conn != None |
__conn != None |
56 |
__dialogo != None |
__dialogo != None |
57 |
__personaje != None -> __personaje.controlador == self |
__personaje != None -> __personaje.controlador == self |
58 |
""" |
""" |
59 |
|
|
60 |
def __init__(self, conn): |
def __init__(self, conn): |
61 |
"""Crea un interfaz nuevo para una conexión de socket. |
"""Crea un interfaz nuevo para una conexión de socket. |
62 |
|
|
63 |
Requiere: |
Requiere: |
64 |
conn != None |
conn != None |
65 |
""" |
""" |
66 |
self.__conn = conn |
self.__conn = conn |
67 |
self.__conn.receive_callback = self.comando_recibido |
self.__conn.receive_callback = self.comando_recibido |
68 |
self.__conn.error_callback = self.error_de_conexion |
self.__conn.error_callback = self.error_de_conexion |
69 |
self.__personaje = None |
self.__personaje = None |
70 |
self.__dialogo = DialogoInicial(self, self.__conn) |
self.__dialogo = DialogoInicial(self, self.__conn) |
71 |
|
|
72 |
Mundo_suscribirse_al_tick(self.tick_recibido) |
Mundo_suscribirse_al_tick(self.tick_recibido) |
73 |
|
|
74 |
|
|
75 |
def __del__(self): |
def __del__(self): |
76 |
"""Realiza ajustes automáticos al ser destruido.""" |
"""Realiza ajustes automáticos al ser destruido.""" |
77 |
self.desconectar() |
self.desconectar() |
78 |
Mundo_borrarse_del_tick(self.tick_recibido) |
Mundo_borrarse_del_tick(self.tick_recibido) |
79 |
|
|
80 |
|
|
81 |
def conn(self): |
def conn(self): |
82 |
return self.__conn |
return self.__conn |
83 |
|
|
84 |
|
|
85 |
def personaje(self): |
def personaje(self): |
86 |
return self.__personaje |
return self.__personaje |
87 |
|
|
88 |
|
|
89 |
def controlar_personaje(self, personaje): |
def controlar_personaje(self, personaje): |
90 |
"""Pasar a estar controlando un personaje. |
"""Pasar a estar controlando un personaje. |
91 |
|
|
92 |
Requiere: |
Requiere: |
93 |
self.__personaje == None |
self.__personaje == None |
94 |
personaje != None |
personaje != None |
95 |
""" |
""" |
96 |
self.__personaje = personaje |
self.__personaje = personaje |
97 |
self.__personaje.controlar(self) |
self.__personaje.controlar(self) |
98 |
|
|
99 |
|
|
100 |
def liberar_personaje(self): |
def liberar_personaje(self): |
101 |
"""Soltar al personaje actual y dejarlo libre. |
"""Soltar al personaje actual y dejarlo libre. |
102 |
|
|
103 |
Requiere: |
Requiere: |
104 |
self.__personaje != None |
self.__personaje != None |
105 |
""" |
""" |
106 |
escribir("La conexión desde " + self.__conn.ip + " ha liberado al personaje " + self.__personaje.id) |
escribir("La conexión desde " + self.__conn.ip + " ha liberado al personaje " + self.__personaje.id) |
107 |
self.__personaje.liberar() |
self.__personaje.liberar() |
108 |
self.__personaje = None |
self.__personaje = None |
109 |
|
|
110 |
|
|
111 |
def terminar(self): |
def terminar(self): |
112 |
"""Soltar al personaje y volver al estado inicial.""" |
"""Soltar al personaje y volver al estado inicial.""" |
113 |
if self.__personaje != None: |
if self.__personaje != None: |
114 |
self.liberar_personaje() |
self.liberar_personaje() |
115 |
|
|
116 |
if not isinstance(self.__dialogo, DialogoInicial): |
if not isinstance(self.__dialogo, DialogoInicial): |
117 |
self.__dialogo = DialogoInicial(self, self.__conn) |
self.__dialogo = DialogoInicial(self, self.__conn) |
118 |
|
|
119 |
|
|
120 |
def desconectar(self): |
def desconectar(self): |
121 |
"""Soltar al personaje, desconectar la conexión y destruir el interfaz.""" |
"""Soltar al personaje, desconectar la conexión y destruir el interfaz.""" |
122 |
if self.__personaje != None: |
if self.__personaje != None: |
123 |
self.liberar_personaje() |
self.liberar_personaje() |
124 |
|
|
125 |
if self.__dialogo != None: |
if self.__dialogo != None: |
126 |
self.__dialogo = None |
self.__dialogo = None |
127 |
|
|
128 |
if self.__conn.is_open: |
if self.__conn.is_open: |
129 |
# Desconectar ordenadamente. |
# Desconectar ordenadamente. |
130 |
self.__conn.send("Adiós, vuelve pronto...") |
self.__conn.send("Adiós, vuelve pronto...") |
131 |
sleep(1) |
sleep(1) |
132 |
self.__conn.close() |
self.__conn.close() |
133 |
escribir("Se ha cerrado normalmente la conexión desde " + self.__conn.ip) |
escribir("Se ha cerrado normalmente la conexión desde " + self.__conn.ip) |
134 |
|
|
135 |
# En este punto la conexión por socket está cerrada, y el handler |
# En este punto la conexión por socket está cerrada, y el handler |
136 |
# se ocupa de destruir el InterfazPJ. |
# se ocupa de destruir el InterfazPJ. |
137 |
|
|
138 |
|
|
139 |
|
|
140 |
# --------------------------------------- |
# --------------------------------------- |
141 |
# Eventos del controlador de un personaje |
# Eventos del controlador de un personaje |
142 |
# --------------------------------------- |
# --------------------------------------- |
143 |
|
|
144 |
def entra_personaje(self, personaje): |
def entra_personaje(self, personaje): |
145 |
self.__dialogo.entra_personaje(personaje) |
self.__dialogo.entra_personaje(personaje) |
146 |
|
|
147 |
|
|
148 |
def sale_personaje(self, personaje, nombre_sala_destino): |
def sale_personaje(self, personaje, nombre_sala_destino): |
149 |
self.__dialogo.sale_personaje(personaje, nombre_sala_destino) |
self.__dialogo.sale_personaje(personaje, nombre_sala_destino) |
150 |
|
|
151 |
|
|
152 |
def habla_personaje(self, personaje, mensaje): |
def habla_personaje(self, personaje, mensaje): |
153 |
self.__dialogo.habla_personaje(personaje, mensaje) |
self.__dialogo.habla_personaje(personaje, mensaje) |
154 |
|
|
155 |
|
|
156 |
def susurra_personaje(self, personaje, mensaje): |
def susurra_personaje(self, personaje, mensaje): |
157 |
self.__dialogo.susurra_personaje(personaje, mensaje) |
self.__dialogo.susurra_personaje(personaje, mensaje) |
158 |
|
|
159 |
|
|
160 |
def grita_personaje(self, personaje, mensaje): |
def grita_personaje(self, personaje, mensaje): |
161 |
self.__dialogo.grita_personaje(personaje, mensaje) |
self.__dialogo.grita_personaje(personaje, mensaje) |
162 |
|
|
163 |
|
|
164 |
def actua_personaje(self, personaje, accion): |
def actua_personaje(self, personaje, accion): |
165 |
self.__dialogo.actua_personaje(personaje, accion) |
self.__dialogo.actua_personaje(personaje, accion) |
166 |
|
|
167 |
|
|
168 |
def actua_individual_personaje(self, personaje, accion): |
def actua_individual_personaje(self, personaje, accion): |
169 |
self.__dialogo.actua_individual_personaje(personaje, accion) |
self.__dialogo.actua_individual_personaje(personaje, accion) |
170 |
|
|
171 |
|
|
172 |
def actua_uno_mismo(self, accion): |
def actua_uno_mismo(self, accion): |
173 |
self.__dialogo.actua_uno_mismo(accion) |
self.__dialogo.actua_uno_mismo(accion) |
174 |
|
|
175 |
|
|
176 |
def actua_global_personaje(self, personaje, accion): |
def actua_global_personaje(self, personaje, accion): |
177 |
self.__dialogo.actua_global_personaje(personaje, accion) |
self.__dialogo.actua_global_personaje(personaje, accion) |
178 |
|
|
179 |
|
|
180 |
def suceso_entorno(self, mensaje): |
def suceso_entorno(self, mensaje): |
181 |
self.__dialogo.suceso_entorno(mensaje) |
self.__dialogo.suceso_entorno(mensaje) |
182 |
|
|
183 |
|
|
184 |
def ataca_personaje(self, personaje, result): |
def ataca_personaje(self, personaje, result): |
185 |
self.__dialogo.ataca_personaje(personaje, result) |
self.__dialogo.ataca_personaje(personaje, result) |
186 |
|
|
187 |
|
|
188 |
def movimiento_automatico(self): |
def movimiento_automatico(self): |
189 |
self.__dialogo.movimiento_automatico() |
self.__dialogo.movimiento_automatico() |
190 |
|
|
191 |
|
|
192 |
# ------------------------------ |
# ------------------------------ |
193 |
# Tick del reloj global de Mundo |
# Tick del reloj global de Mundo |
194 |
# ------------------------------ |
# ------------------------------ |
195 |
|
|
196 |
def tick_recibido(self, tick_ahora, tick_anterior): |
def tick_recibido(self, tick_ahora, tick_anterior): |
197 |
"""Responde al tick llamando al diálogo si éste implementa la función.""" |
"""Responde al tick llamando al diálogo si éste implementa la función.""" |
198 |
|
|
199 |
try: |
try: |
200 |
|
|
201 |
# Luego llamamos a la funcion correspondiente segun estado |
# Luego llamamos a la funcion correspondiente segun estado |
202 |
nombre_func = "tick_" + str(self.__dialogo.estado()) |
nombre_func = "tick_" + str(self.__dialogo.estado()) |
203 |
func = getattr(self.__dialogo, nombre_func, None) |
func = getattr(self.__dialogo, nombre_func, None) |
204 |
if func != None: |
if func != None: |
205 |
nuevo_dialogo = func(tick_ahora, tick_anterior) |
nuevo_dialogo = func(tick_ahora, tick_anterior) |
206 |
|
|
207 |
# Si hay que cambiar de dialogo, hacerlo |
# Si hay que cambiar de dialogo, hacerlo |
208 |
if nuevo_dialogo != None: |
if nuevo_dialogo != None: |
209 |
self.__dialogo = nuevo_dialogo |
self.__dialogo = nuevo_dialogo |
210 |
|
|
211 |
except: |
except: |
212 |
|
|
213 |
# No deben ocurrir excepciones no tratadas salvo por un bug del |
# No deben ocurrir excepciones no tratadas salvo por un bug del |
214 |
# programa. Aquí se interceptan para que un bug afecte, si es posible, |
# programa. Aquí se interceptan para que un bug afecte, si es posible, |
215 |
# sólo a la conexión que lo ha hecho saltar, pero no se caiga el |
# sólo a la conexión que lo ha hecho saltar, pero no se caiga el |
216 |
# servidor entero. |
# servidor entero. |
217 |
# |
# |
218 |
# Si ocurre esto, desconectamos al jugador de su personaje, pero |
# Si ocurre esto, desconectamos al jugador de su personaje, pero |
219 |
# mantenemos la conexión abierta por si quiere volverlo a intentar. |
# mantenemos la conexión abierta por si quiere volverlo a intentar. |
220 |
|
|
221 |
escribir("Excepción imprevista durante el tick del reloj global.") |
escribir("Excepción imprevista durante el tick del reloj global.") |
222 |
escribir(" -> conexión desde " + self.__conn.ip) |
escribir(" -> conexión desde " + self.__conn.ip) |
223 |
escribir(" -> tick ahora " + str(tick_ahora)) |
escribir(" -> tick ahora " + str(tick_ahora)) |
224 |
escribir(" -> tick anterior " + str(tick_anterior)) |
escribir(" -> tick anterior " + str(tick_anterior)) |
225 |
escribir(" -> dialogo " + str(self.__dialogo.__class__)) |
escribir(" -> dialogo " + str(self.__dialogo.__class__)) |
226 |
if self.__personaje != None: |
if self.__personaje != None: |
227 |
escribir(" -> personaje " + self.__personaje.id) |
escribir(" -> personaje " + self.__personaje.id) |
228 |
escribir_excepcion() |
escribir_excepcion() |
229 |
|
|
230 |
try: |
try: |
231 |
|
|
232 |
self.__conn.send("\n\nHa ocurrido un error imprevisto en Mine, probablemente se trate") |
self.__conn.send("\n\nHa ocurrido un error imprevisto en Mine, probablemente se trate") |
233 |
self.__conn.send("de un fallo en el programa. Por favor, ponte en contacto con los") |
self.__conn.send("de un fallo en el programa. Por favor, ponte en contacto con los") |
234 |
self.__conn.send("administadores del juego y explicales, si es posible, toda la") |
self.__conn.send("administadores del juego y explicales, si es posible, toda la") |
235 |
self.__conn.send("información que tengas sobre lo que estabas haciendo en el momento") |
self.__conn.send("información que tengas sobre lo que estabas haciendo en el momento") |
236 |
self.__conn.send("del error. Gracias, y disculpa por las molestias.") |
self.__conn.send("del error. Gracias, y disculpa por las molestias.") |
237 |
|
|
238 |
self.terminar() |
self.terminar() |
239 |
|
|
240 |
except: |
except: |
241 |
|
|
242 |
# Si no se puede salir del personaje normalmente, tiramos la |
# Si no se puede salir del personaje normalmente, tiramos la |
243 |
# conexión del todo. |
# conexión del todo. |
244 |
escribir("Problema gordo: no se puede mantener la conexión. La tiramos...") |
escribir("Problema gordo: no se puede mantener la conexión. La tiramos...") |
245 |
self.desconectar() |
self.desconectar() |
246 |
|
|
247 |
|
|
248 |
# ------------------ |
# ------------------ |
249 |
# Eventos del socket |
# Eventos del socket |
250 |
# ------------------ |
# ------------------ |
251 |
|
|
252 |
def comando_recibido(self, conn): |
def comando_recibido(self, conn): |
253 |
"""Responde a la recepcion de un mensaje del usuario.""" |
"""Responde a la recepcion de un mensaje del usuario.""" |
254 |
|
|
255 |
cmd_list = [] |
cmd_list = [] |
256 |
|
|
257 |
try: |
try: |
258 |
|
|
259 |
# Primero lo convertimos en una lista para manejar las palabras |
# Primero lo convertimos en una lista para manejar las palabras |
260 |
# mas comodamente. De paso, nos cargamos el \n del final. |
# mas comodamente. De paso, nos cargamos el \n del final. |
261 |
cmd_list = conn.receive().split() |
cmd_list = conn.receive().split() |
262 |
|
|
263 |
# Luego llamamos a la funcion correspondiente segun estado |
# Luego llamamos a la funcion correspondiente segun estado |
264 |
nombre_func = "comando_" + str(self.__dialogo.estado()) |
nombre_func = "comando_" + str(self.__dialogo.estado()) |
265 |
func = getattr(self.__dialogo, nombre_func) |
func = getattr(self.__dialogo, nombre_func) |
266 |
nuevo_dialogo = func(cmd_list) |
nuevo_dialogo = func(cmd_list) |
267 |
|
|
268 |
# Si hay que cambiar de dialogo, hacerlo |
# Si hay que cambiar de dialogo, hacerlo |
269 |
if nuevo_dialogo != None: |
if nuevo_dialogo != None: |
270 |
self.__dialogo = nuevo_dialogo |
self.__dialogo = nuevo_dialogo |
271 |
|
|
272 |
except: |
except: |
273 |
|
|
274 |
# No deben ocurrir excepciones no tratadas salvo por un bug del |
# No deben ocurrir excepciones no tratadas salvo por un bug del |
275 |
# programa. Aquí se interceptan para que un bug afecte, si es posible, |
# programa. Aquí se interceptan para que un bug afecte, si es posible, |
276 |
# sólo a la conexión que lo ha hecho saltar, pero no se caiga el |
# sólo a la conexión que lo ha hecho saltar, pero no se caiga el |
277 |
# servidor entero. |
# servidor entero. |
278 |
# |
# |
279 |
# Si ocurre esto, desconectamos al jugador de su personaje, pero |
# Si ocurre esto, desconectamos al jugador de su personaje, pero |
280 |
# mantenemos la conexión abierta por si quiere volverlo a intentar. |
# mantenemos la conexión abierta por si quiere volverlo a intentar. |
281 |
|
|
282 |
escribir("Excepción imprevista durante la ejecución de un comando.") |
escribir("Excepción imprevista durante la ejecución de un comando.") |
283 |
escribir(" -> conexión desde " + self.__conn.ip) |
escribir(" -> conexión desde " + self.__conn.ip) |
284 |
escribir(" -> comando " + " ".join(cmd_list)) |
escribir(" -> comando " + " ".join(cmd_list)) |
285 |
escribir(" -> dialogo " + str(self.__dialogo.__class__)) |
escribir(" -> dialogo " + str(self.__dialogo.__class__)) |
286 |
if self.__personaje != None: |
if self.__personaje != None: |
287 |
escribir(" -> personaje " + self.__personaje.id) |
escribir(" -> personaje " + self.__personaje.id) |
288 |
escribir_excepcion() |
escribir_excepcion() |
289 |
|
|
290 |
try: |
try: |
291 |
|
|
292 |
self.__conn.send("\n\nHa ocurrido un error imprevisto en Mine, probablemente se trate") |
self.__conn.send("\n\nHa ocurrido un error imprevisto en Mine, probablemente se trate") |
293 |
self.__conn.send("de un fallo en el programa. Por favor, ponte en contacto con los") |
self.__conn.send("de un fallo en el programa. Por favor, ponte en contacto con los") |
294 |
self.__conn.send("administadores del juego y explicales, si es posible, toda la") |
self.__conn.send("administadores del juego y explicales, si es posible, toda la") |
295 |
self.__conn.send("información que tengas sobre lo que estabas haciendo en el momento") |
self.__conn.send("información que tengas sobre lo que estabas haciendo en el momento") |
296 |
self.__conn.send("del error. Gracias, y disculpa por las molestias.") |
self.__conn.send("del error. Gracias, y disculpa por las molestias.") |
297 |
|
|
298 |
self.terminar() |
self.terminar() |
299 |
|
|
300 |
except: |
except: |
301 |
|
|
302 |
# Si no se puede salir del personaje normalmente, tiramos la |
# Si no se puede salir del personaje normalmente, tiramos la |
303 |
# conexión del todo. |
# conexión del todo. |
304 |
escribir("Problema gordo: no se puede mantener la conexión. La tiramos...") |
escribir("Problema gordo: no se puede mantener la conexión. La tiramos...") |
305 |
self.desconectar() |
self.desconectar() |
306 |
|
|
307 |
|
|
308 |
def error_de_conexion(self, conn): |
def error_de_conexion(self, conn): |
309 |
"""Responde al evento de perdida de conexion por el socket.""" |
"""Responde al evento de perdida de conexion por el socket.""" |
310 |
escribir("Se ha caido bruscamente la conexion con " + self.__conn.ip) |
escribir("Se ha caido bruscamente la conexion con " + self.__conn.ip) |
311 |
self.desconectar() |
self.desconectar() |
312 |
|
|
313 |
|
|
314 |
# Código para pruebas unitarias del módulo. |
# Código para pruebas unitarias del módulo. |
315 |
|
|
316 |
if (__name__ == '__main__'): |
if (__name__ == '__main__'): |
317 |
print "No hay código para pruebas unitarias del módulo." |
print "No hay código para pruebas unitarias del módulo." |
318 |
print "Para probar, ejecutar mine.py y conectarse via telnet." |
print "Para probar, ejecutar mine.py y conectarse via telnet." |
319 |
|
|