25 |
from interfazpnj import * |
from interfazpnj import * |
26 |
from mundo import * |
from mundo import * |
27 |
from modulo import * |
from modulo import * |
28 |
|
from guiontemporizado import GuionTemporizado |
29 |
from xml.dom import minidom |
from xml.dom import minidom |
30 |
import copy |
import copy |
31 |
from cronomine import CronoMine |
from cronomine import CronoMine |
64 |
len(_fragmentos) >= 0 |
len(_fragmentos) >= 0 |
65 |
self.visita[i].activo <--> len(self._fragmentos) > 0 |
self.visita[i].activo <--> len(self._fragmentos) > 0 |
66 |
""" |
""" |
67 |
|
|
68 |
VERSION_DESC_CONVERSACIONES = '2.0.0' |
VERSION_DESC_CONVERSACIONES = '2.0.0' |
69 |
|
|
70 |
|
|
71 |
DIR_DESC_CONVERSACIONES = 'conversaciones' |
DIR_DESC_CONVERSACIONES = 'conversaciones' |
72 |
DIR_DESC_GENERICAS = 'generico' |
DIR_DESC_GENERICAS = 'generico' |
75 |
|
|
76 |
TEMAS_ESPECIALES = [ 'iniciativa_propia', 'tema_agotado', 'tema_no_encontrado', 'fuera_de_tema' ] |
TEMAS_ESPECIALES = [ 'iniciativa_propia', 'tema_agotado', 'tema_no_encontrado', 'fuera_de_tema' ] |
77 |
|
|
78 |
|
|
79 |
|
#Constructor |
80 |
|
|
81 |
def __init__(self, id_conversacion): |
def __init__(self, id_conversacion): |
82 |
InterfazPNJ.__init__(self) |
InterfazPNJ.__init__(self) |
83 |
|
trazar("Probando la funcion trazar()") |
84 |
escribir("Cargando el pnj hablante " + id_conversacion) |
escribir("Cargando el pnj hablante " + id_conversacion) |
85 |
#Control de iniciativa |
#Control de iniciativa |
86 |
self.__temporizador = None |
self.__temporizador = None # Controla la siguiente activación de la iniciativa |
87 |
self.__interlocutor = None |
self.__interlocutor = None # Último personaje con el que habló |
88 |
self.__velocidad = 0.05 # TO DO: parametrizar la velocidad de reaccion del PNJ |
self.__velocidad = 0.05 # TO DO: parametrizar la velocidad de reaccion del PNJ |
89 |
self.__keywords = [] # TO DO: añadir una lista de keywords que el PNJ "entiende" en el diálogo entre PJs |
self.__keywords = [] # TO DO: añadir una lista de keywords que el PNJ "entiende" en el diálogo entre PJs |
90 |
|
|
91 |
self.id_conversacion = id_conversacion |
self.id_conversacion = id_conversacion |
92 |
self.__guion_activo = None |
self.__guion_activo = None # Guion del diálogo que está en marcha |
93 |
|
self.__contextos = ['default'] |
94 |
|
self.__historia = {} |
95 |
|
|
96 |
self.__temas = {} |
self.__temas = {} |
97 |
self.__temas_especiales = {} |
self.__temas_especiales = {} |
103 |
else: |
else: |
104 |
trazar("Inicializado el tema " + tema, "pnj") |
trazar("Inicializado el tema " + tema, "pnj") |
105 |
|
|
|
def __ruta_conversacion(self, id_conversacion=None): |
|
|
if not id_conversacion: id_conversacion = self.id_conversacion |
|
|
return os.path.join(Modulo.DIR_DESC_MODULOS, id_conversacion, InterfazPNJHablante.DIR_DESC_CONVERSACIONES) |
|
|
|
|
|
def __ruta_especial(self, id_conversacion=None): |
|
|
if not id_conversacion: id_conversacion = self.id_conversacion |
|
|
return os.path.join(Modulo.DIR_DESC_MODULOS, id_conversacion, InterfazPNJHablante.DIR_DESC_ESPECIALES) |
|
|
|
|
106 |
def __setattr__(self, name, value): |
def __setattr__(self, name, value): |
107 |
"""Realiza ajustes autom�ticos al modificar cualquier par�metro""" |
"""Realiza ajustes autom�ticos al modificar cualquier par�metro""" |
108 |
|
|
109 |
if name == "personaje": |
if name == "personaje": |
110 |
# Si es personaje, liberar al anterior y controlar al nuevo. |
# Si es personaje, liberar al anterior y controlar al nuevo. |
111 |
if (self.personaje != None): self.personaje.controlador = None |
if (self.personaje != None): self.personaje.controlador = None |
117 |
# En otro caso, asignar y ya esta |
# En otro caso, asignar y ya esta |
118 |
self.__dict__[name] = value |
self.__dict__[name] = value |
119 |
|
|
120 |
# Mé�todos privados |
# Métodos privados |
121 |
|
|
122 |
|
|
123 |
|
# Localizar y cargar temas en la caché |
124 |
|
|
125 |
|
def __ruta_conversacion(self, id_conversacion=None): |
126 |
|
if not id_conversacion: id_conversacion = self.id_conversacion |
127 |
|
return os.path.join(Modulo.DIR_DESC_MODULOS, id_conversacion, InterfazPNJHablante.DIR_DESC_CONVERSACIONES) |
128 |
|
|
129 |
|
def __ruta_especial(self, id_conversacion=None): |
130 |
|
if not id_conversacion: id_conversacion = self.id_conversacion |
131 |
|
return os.path.join(Modulo.DIR_DESC_MODULOS, id_conversacion, InterfazPNJHablante.DIR_DESC_ESPECIALES) |
132 |
|
|
133 |
|
|
134 |
def __encuentra_tema(self, nombretema, ruta = None): |
def __encuentra_tema(self, nombretema, ruta = None): |
135 |
"""Localiza un tema por su nombre. Si es necesario lo carga desde disco a la cache de temas.""" |
"""Localiza un tema por su nombre. Si es necesario lo carga desde disco a la cache de temas.""" |
136 |
|
|
137 |
tema = self.__temas.get(nombretema, None) |
tema = self.__temas.get(nombretema, None) |
138 |
if tema: return tema |
if tema: return tema |
139 |
|
|
140 |
if not ruta: ruta = self.__ruta_conversacion() |
if not ruta: ruta = self.__ruta_conversacion() |
141 |
try: |
try: |
142 |
self.__temas[nombretema] = self.__cargar_modulo(nombretema) |
self.__temas[nombretema] = self.__cargar_modulo(nombretema) |
151 |
tema = self.__temas_especiales.get(nombretema, None) |
tema = self.__temas_especiales.get(nombretema, None) |
152 |
if tema: |
if tema: |
153 |
return tema |
return tema |
154 |
|
|
155 |
if not ruta: ruta = self.__ruta_especial() |
if not ruta: ruta = self.__ruta_especial() |
156 |
try: |
try: |
157 |
self.__temas_especiales[nombretema] = self.__cargar_modulo(nombretema, ruta) |
self.__temas_especiales[nombretema] = self.__cargar_modulo(nombretema, ruta) |
185 |
macros = [ ("$interlocutor", "Personaje"), |
macros = [ ("$interlocutor", "Personaje"), |
186 |
("$pnj", "Personaje"), |
("$pnj", "Personaje"), |
187 |
("$sala", "Sala")]#, |
("$sala", "Sala")]#, |
188 |
# ("$tema", "Modulo") ] |
# ("$tema", "Modulo") ] |
189 |
|
|
190 |
|
|
191 |
macros_extra = {} |
macros_extra = {} |
192 |
return Modulo(elem_desc_modulo, macros, macros_extra) |
return Modulo(elem_desc_modulo, macros, macros_extra) |
196 |
else: |
else: |
197 |
self.__temas_especiales[id_modulo] = Modulo(elem_desc_modulo, macros, macros_extra) |
self.__temas_especiales[id_modulo] = Modulo(elem_desc_modulo, macros, macros_extra) |
198 |
return |
return |
199 |
|
|
200 |
|
# Controlar la iniciativa del PNJ |
201 |
def __fallar(self, nombretema, macros): |
|
|
tema = self.__temas_especiales[nombretema] |
|
|
guion_equivocado = tema.mejor_guion() |
|
|
if not guion_equivocado: |
|
|
tema = self.__encuentra_tema_generico(nombretema) |
|
|
guion_equivocado = tema.mejor_guion() |
|
|
if not guion_equivocado: |
|
|
raise Exception("Error en modulo de conversacion: no quedan guiones activos en el tema por defecto.") |
|
|
guion_equivocado.ejecutar(macros) |
|
|
self.__reanudar_iniciativa() |
|
|
|
|
|
|
|
202 |
def __temporizar_nueva_iniciativa(self): |
def __temporizar_nueva_iniciativa(self): |
203 |
"""Establece el tiempo necesario para activar la iniciativa""" |
"""Establece el tiempo necesario para activar la iniciativa""" |
204 |
from mundo import Mundo |
from mundo import Mundo |
228 |
self.__temporizador.reanudar() |
self.__temporizador.reanudar() |
229 |
except: |
except: |
230 |
escribir("Error inesperado al reanudar la iniciativa") |
escribir("Error inesperado al reanudar la iniciativa") |
231 |
|
|
232 |
def __disparar_iniciativa(self): |
def __disparar_iniciativa(self): |
233 |
"""Activa el modulo de iniciativa propia de la conversacion""" |
"""Activa el modulo de iniciativa propia de la conversacion""" |
234 |
trazar("Activando iniciativa del pnj", "pnj") |
trazar("Activando iniciativa del pnj", "pnj") |
237 |
macros = { "$interlocutor": self.__interlocutor, |
macros = { "$interlocutor": self.__interlocutor, |
238 |
"$pnj": self.__personaje, |
"$pnj": self.__personaje, |
239 |
"$sala": self.__personaje.sala } |
"$sala": self.__personaje.sala } |
240 |
iniciativa.ejecutar(macros) |
guion = iniciativa.mejor_guion(self.__contextos) |
241 |
|
if guion: |
242 |
|
self.__interlocutor = self.__personaje |
243 |
|
self.__activar_guion(guion, macros) |
244 |
self.__temporizador = None |
self.__temporizador = None |
245 |
self.__temporizar_nueva_iniciativa() |
self.__temporizar_nueva_iniciativa() |
246 |
else: |
else: |
247 |
self.__temporizador = None |
self.__temporizador = None |
248 |
|
|
249 |
|
# Controlar la temporización del diálogo activo |
250 |
|
|
251 |
|
def __fallar(self, nombretema, macros): |
252 |
|
"""Encuentra una respuesta correcta cuando el PJ ha preguntado por un tema no válido.""" |
253 |
|
guion_equivocado = None |
254 |
|
if self.__temas_especiales.has_key(nombretema): |
255 |
|
tema = self.__temas_especiales[nombretema] |
256 |
|
guion_equivocado = tema.mejor_guion(self.__contextos) |
257 |
|
if not guion_equivocado: |
258 |
|
tema = self.__encuentra_tema_generico(nombretema) |
259 |
|
guion_equivocado = tema.mejor_guion(self.__contextos) |
260 |
|
if not guion_equivocado: |
261 |
|
raise Exception("Error en modulo de conversacion: no quedan guiones activos en el tema por defecto.") |
262 |
|
self.__activar_guion(guion_equivocado, macros) |
263 |
|
self.__reanudar_iniciativa() |
264 |
|
|
265 |
|
def __activar_guion(self, guion, macros): |
266 |
|
"""Comienza la temporización del guion encontrado""" |
267 |
|
self.__guion_activo = guion # Recordar: es un wrapper, "not the Real Thing" |
268 |
|
self.__contextos = guion.contextos |
269 |
|
for contexto in guion.contextos: |
270 |
|
if not self.__historia.has_key(contexto): |
271 |
|
self.__historia[contexto] = 0 |
272 |
|
else: |
273 |
|
self.__historia[contexto] += 1 |
274 |
|
trazar(str(contexto) + str(self.__historia[contexto]) + " veces", "pnj", 3) |
275 |
|
guion.ejecutar(macros, finalizar=(lambda guion, cancelado: self.__finalizar_guion(cancelado))) |
276 |
|
|
277 |
|
def __finalizar_guion(self, cancelado=0): |
278 |
|
"""Callback que usa el guion activo cuando termina o es cancelado""" |
279 |
|
if self.esta_hablando(): |
280 |
|
trazar("Finalizando la conversacion", "pnj") |
281 |
|
self.__guion_activo.cancelar_secuencia() |
282 |
|
self.__guion_activo = None |
283 |
|
|
284 |
# ------------------------------------------------- |
# ------------------------------------------------- |
285 |
# Metodos publicos |
# Metodos publicos |
286 |
# ------------------------------------------------- |
# ------------------------------------------------- |
287 |
|
|
288 |
|
def esta_hablando(self): |
289 |
|
return self.__guion_activo != None |
290 |
|
|
291 |
def conversa_personaje(self, personaje, comando, nombretema): |
def conversa_personaje(self, personaje, comando, nombretema): |
292 |
"""Metodo llamado cuando otro personaje habla con este PNJ""" |
"""Metodo llamado cuando otro personaje habla con este PNJ |
293 |
|
|
294 |
|
Devuelve false cuando el PNJ está hablando con otro personaje y no se les puede interrumpir |
295 |
|
""" |
296 |
|
|
297 |
|
if self.esta_hablando() and self.__interlocutor: |
298 |
|
#Interrumpiendo una conversacion en curso |
299 |
|
trazar("Esta hablando con " + repr(self.__interlocutor.nombre), "pnj", 2) |
300 |
|
if self.__interlocutor == personaje: |
301 |
|
self.__finalizar_guion() |
302 |
|
elif personaje.competicion(self.__interlocutor, "carisma", "carisma", malus=6): |
303 |
|
#Si el nuevo interlocutor gana la competicion de carisma, distrae al PNJ y cancela la conversacion anterior |
304 |
|
trazar("Cancelando una conversacion por una nueva pregunta que interrumpe la anterior", "pnj") |
305 |
|
self.__finalizar_guion() |
306 |
|
else: |
307 |
|
return 0 |
308 |
|
|
309 |
# LOGICA PARA CANCELAR +HABLAR CUANDO HABLA CON OTRO PNJ DE MAYOR CARISMA |
# LOGICA PARA CANCELAR +HABLAR CUANDO HABLA CON OTRO PNJ DE MAYOR CARISMA |
310 |
# if visita.activo: |
# if visita.activo: |
326 |
self.__fallar("tema_no_encontrado", macros) |
self.__fallar("tema_no_encontrado", macros) |
327 |
else: |
else: |
328 |
tema = self.__temas[nombretema] |
tema = self.__temas[nombretema] |
329 |
guion = tema.mejor_guion() |
guion = tema.mejor_guion(self.__contextos) |
330 |
if guion: |
if guion: |
331 |
# Nueva conversacion con exito |
# Nueva conversacion con exito |
332 |
guion.ejecutar(macros) |
self.__activar_guion(guion, macros) |
333 |
self.__reanudar_iniciativa() |
self.__reanudar_iniciativa() |
334 |
self.__temporizar_nueva_iniciativa() |
self.__temporizar_nueva_iniciativa() |
335 |
else: |
else: |
339 |
trazar("Ejecutado el tema " + nombretema, "pnj") |
trazar("Ejecutado el tema " + nombretema, "pnj") |
340 |
#self._modulo.ejecutar(None) |
#self._modulo.ejecutar(None) |
341 |
|
|
342 |
return |
return 1 |
|
|
|
|
visita = self._visitas[0] |
|
|
if visita.activo: |
|
|
if visita.pj_activo.atrib["carisma"] > personaje.atrib["carisma"] : #visita.pj_activo.aura > personaje.controlador._DialogoNormal__personaje.aura: |
|
|
personaje.controlador.conn().send(visita.pnj.nombre +" est� hablando con " + visita.pj_activo.nombre) |
|
|
return |
|
|
else: |
|
|
self.anular_conversacion() |
|
|
respuesta = visita.responder(personaje, comando, nombretema.lower()) |
|
|
escribir("Tema " + visita.estado()) |
|
|
#print respuesta[len(respuesta)-1] |
|
|
if visita.estado() == Visita.ENCONTRADO or visita.estado() == Visita.OFFTOPIC: |
|
|
visita.activar(respuesta) |
|
|
|
|
|
nombre = visita.pnj.nombre[0].lower() + visita.pnj.nombre[1:] |
|
|
if comando == "decir": |
|
|
visita.pj_activo.actuar("dice a " + nombre + " algo acerca de '" + nombretema + "'") |
|
|
personaje.controlador.conn().send("Dices a " + nombre + " algo sobre '" + nombretema + "'") |
|
|
else: |
|
|
visita.pj_activo.actuar("pregunta a " + nombre + " acerca de '" + nombretema + "'") |
|
|
personaje.controlador.conn().send("Preguntas a " + nombre + " sobre '" + nombretema + "'") |
|
343 |
|
|
344 |
|
|
345 |
|
def anular_conversacion(self): |
346 |
|
self._fragmentos = [] |
347 |
|
self._visitas[0].activo = None |
348 |
|
for crono in self.cronos: |
349 |
|
# Anula los cronos que no se han ejecutado a�n, asignandoles la funci�n nula |
350 |
|
crono.vencimiento_func = lambda x: None |
351 |
|
|
352 |
|
|
353 |
def controlar_personaje(self, personaje): |
def controlar_personaje(self, personaje): |
354 |
"""Pasar a estar controlando un personaje. |
"""Pasar a estar controlando un personaje. |
355 |
|
|
358 |
personaje != None |
personaje != None |
359 |
""" |
""" |
360 |
self.__personaje = personaje |
self.__personaje = personaje |
|
|
|
361 |
|
|
|
|
|
|
def anular_conversacion(self): |
|
|
self._fragmentos = [] |
|
|
self._visitas[0].activo = None |
|
|
for crono in self.cronos: |
|
|
# Anula los cronos que no se han ejecutado a�n, asignandoles la funci�n nula |
|
|
crono.vencimiento_func = lambda x: None |
|
|
|
|
|
|
|
362 |
# ------------------------------------------------- |
# ------------------------------------------------- |
363 |
# Metodos de manejo de eventos (ver controlador.py) |
# Metodos de manejo de eventos (ver controlador.py) |
364 |
# ------------------------------------------------- |
# ------------------------------------------------- |
369 |
if not self.__temporizador: |
if not self.__temporizador: |
370 |
self.__temporizar_nueva_iniciativa() |
self.__temporizar_nueva_iniciativa() |
371 |
|
|
372 |
|
|
373 |
def sale_personaje(self, personaje, nombre_sala_destino): |
def sale_personaje(self, personaje, nombre_sala_destino): |
374 |
"""Si no quedan personajes, desactiva el temporizador de iniciativa""" |
"""Si no quedan personajes, desactiva el temporizador de iniciativa""" |
375 |
|
|
376 |
if personaje == self.__interlocutor: |
if personaje == self.__interlocutor: |
377 |
|
trazar("Cancelando la conversacion por abandono del interlocutor", "pnj") |
378 |
self.__interlocutor = None |
self.__interlocutor = None |
379 |
if self.__guion_activo: |
self.__finalizar_guion() |
|
trazar("Cancelando la conversacion por abandono del interlocutor", "pnj") |
|
|
self.__guion_activo.cancelar() |
|
380 |
|
|
381 |
pjs = [ personaje for personaje in self.__personaje.sala.personajes.values() \ |
pjs = [ personaje for personaje in self.__personaje.sala.personajes.values() \ |
382 |
if isinstance(personaje.controlador, InterfazPNJHablante) ] |
if not isinstance(personaje.controlador, InterfazPNJHablante) ] |
383 |
trazar("Quedan %s personajes jugadores en la sala" % len(pjs), "pnj", 2) |
trazar("Quedan %s personajes jugadores en la sala" % len(pjs), "pnj", 2) |
384 |
if len(pjs) == 1: |
if len(pjs) == 0: |
385 |
trazar("Desactivando la iniciativa del pnj", "pnj", 2) |
trazar("Desactivando la iniciativa del pnj", "pnj", 2) |
386 |
if self.__temporizador: |
if self.__temporizador: |
387 |
try: |
try: |
394 |
def habla_personaje(self, personaje, mensaje): |
def habla_personaje(self, personaje, mensaje): |
395 |
""" El PNJ intenta entender las conversaciones entre PJs """ |
""" El PNJ intenta entender las conversaciones entre PJs """ |
396 |
pass |
pass |
397 |
# TO DO: añadir una lista de keywords que el PNJ "entiende" en el diálogo entre PJs |
# To Do: añadir una lista de keywords que el PNJ "entiende" en el diálogo entre PJs |
398 |
for palabra in mensaje.split(): |
for palabra in mensaje.split(): |
399 |
if palabra in self.__keywords: |
if palabra in self.__keywords: |
400 |
self.__reaccionar(palabra) |
self.__reaccionar(palabra) # To Do: implementar el metodo __reaccionar |
401 |
|
|
402 |
|
|
403 |
|
|