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: pnj.py |
#Fichero: pnj.py |
23 |
|
|
24 |
"""Clase pnj.""" |
"""Clase pnj.""" |
25 |
|
|
26 |
|
|
27 |
from xml.dom import minidom |
from xml.dom import minidom |
28 |
import random |
import random |
29 |
import sys |
import sys |
30 |
import string |
import string |
31 |
import os |
import os |
32 |
import cPickle |
import cPickle |
33 |
|
|
34 |
from personaje import * |
from personaje import * |
35 |
from personajejugador import * |
from personajejugador import * |
36 |
from personajenojugador import * |
from personajenojugador import * |
37 |
from interfazpnj import * |
from interfazpnj import * |
38 |
|
|
39 |
|
|
40 |
|
|
41 |
class Pnj: |
class Pnj: |
42 |
"""Un personaje no jugador. |
"""Un personaje no jugador. |
43 |
FIXME! |
FIXME! |
44 |
Contiene los siguientes atributos: |
Contiene los siguientes atributos: |
45 |
- nombre (string): identifica univocamente al pnj |
- nombre (string): identifica univocamente al pnj |
46 |
- descripcion (string): texto largo que explica todo lo que cualquier jugador ve al entrar |
- descripcion (string): texto largo que explica todo lo que cualquier jugador ve al entrar |
47 |
- descripcion_especial (string): texto describiendo mas cosas que solo se ven superando |
- descripcion_especial (string): texto describiendo mas cosas que solo se ven superando |
48 |
una tirada de percepcion |
una tirada de percepcion |
49 |
- direcciones (string[]): lista con los nombres de las salas accesibles desde esta |
- direcciones (string[]): lista con los nombres de las salas accesibles desde esta |
50 |
- encuentro (PersonajeNoJugador): el PersonajeNoJugador que hay en la sala o None si no hay ninguno |
- encuentro (PersonajeNoJugador): el PersonajeNoJugador que hay en la sala o None si no hay ninguno |
51 |
- tesoro (string): nombre del objeto que hay en la sala, o "" si no hay ninguno |
- tesoro (string): nombre del objeto que hay en la sala, o "" si no hay ninguno |
52 |
- personajes (Personaje{}): diccionario con los personajes que se encuentran en la sala, |
- personajes (Personaje{}): diccionario con los personajes que se encuentran en la sala, |
53 |
indexado por nombre. NO MODIFICAR DIRECTAMENTE, SINO CON |
indexado por nombre. NO MODIFICAR DIRECTAMENTE, SINO CON |
54 |
entrar_personaje() Y salir_personaje() |
entrar_personaje() Y salir_personaje() |
55 |
- n_jugadores (int): numero de personajes de tipo PersonajeJugador dentro de la sala |
- n_jugadores (int): numero de personajes de tipo PersonajeJugador dentro de la sala |
56 |
|
|
57 |
Al instanciar una sala, se crea nueva y se lee desde el fichero de descripcion de sala, |
Al instanciar una sala, se crea nueva y se lee desde el fichero de descripcion de sala, |
58 |
poniendola en su estado inicial (se lee el tesoro y se crea un PersonajeNoJugador partiendo |
poniendola en su estado inicial (se lee el tesoro y se crea un PersonajeNoJugador partiendo |
59 |
del valor del atributo "encuentro"). Mas tarde, se puede salvar en un fichero de sala, |
del valor del atributo "encuentro"). Mas tarde, se puede salvar en un fichero de sala, |
60 |
borrarla y cargarla despues, manteniendo su estado (situacion del tesoro y de los personajes |
borrarla y cargarla despues, manteniendo su estado (situacion del tesoro y de los personajes |
61 |
contenidos). |
contenidos). |
62 |
|
|
63 |
Importante: al salvar una sala se salvan también todos los objetos y todos los personajes |
Importante: al salvar una sala se salvan también todos los objetos y todos los personajes |
64 |
contenidas en ella. El módulo "Pickle" se encarga de todo, incluyendo la resolución del bucle |
contenidas en ella. El módulo "Pickle" se encarga de todo, incluyendo la resolución del bucle |
65 |
formado por el atributo "sala" de la clase Personaje. |
formado por el atributo "sala" de la clase Personaje. |
66 |
""" |
""" |
67 |
|
|
68 |
DIR_DESC_PNJS = 'desc_pnjs' |
DIR_DESC_PNJS = 'desc_pnjs' |
69 |
DIR_PNJS = 'pnjs' |
DIR_PNJS = 'pnjs' |
70 |
|
|
71 |
|
|
72 |
|
|
73 |
def __init__(self, archivo): |
def __init__(self, archivo): |
74 |
|
|
75 |
"""Método inicial para leer los datos de un fichero de pnj""" |
"""Método inicial para leer los datos de un fichero de pnj""" |
76 |
|
|
77 |
self.id = archivo |
self.id = archivo |
78 |
self.archivo=archivo+".xml" |
self.archivo=archivo+".xml" |
79 |
self.archivo=os.path.join(Pnj.DIR_DESC_PNJS, self.archivo) |
self.archivo=os.path.join(Pnj.DIR_DESC_PNJS, self.archivo) |
80 |
print self.archivo |
print self.archivo |
81 |
fuente = openAnything(self.archivo) |
fuente = openAnything(self.archivo) |
82 |
self.source = minidom.parse(fuente).documentElement |
self.source = minidom.parse(fuente).documentElement |
83 |
fuente.close() |
fuente.close() |
84 |
|
|
85 |
self.iniciar_parseador(self.source) |
self.iniciar_parseador(self.source) |
86 |
|
|
87 |
self.parsear_pnj(self.source) |
self.parsear_pnj(self.source) |
88 |
self.parsear_id(self.source) |
self.parsear_id(self.source) |
89 |
self.parsear_nombre(self.source) |
self.parsear_nombre(self.source) |
90 |
self.parsear_descripcion(self.source) |
self.parsear_descripcion(self.source) |
91 |
self.parsear_propiedades(self.source) |
self.parsear_propiedades(self.source) |
92 |
self.parsear_atributos(self.source) |
self.parsear_atributos(self.source) |
93 |
self.parsear_idiomas(self.source) |
self.parsear_idiomas(self.source) |
94 |
self.parsear_controlador(self.source) |
self.parsear_controlador(self.source) |
95 |
self.parsear_objetos(self.source) |
self.parsear_objetos(self.source) |
96 |
|
|
97 |
del self.source |
del self.source |
98 |
del self.descripcion |
del self.descripcion |
99 |
|
|
100 |
|
|
101 |
|
|
102 |
|
|
103 |
def iniciar_parseador(self, fuente): |
def iniciar_parseador(self, fuente): |
104 |
"""Esta función borra todos los nodos de tipo Text de un |
"""Esta función borra todos los nodos de tipo Text de un |
105 |
elemento DOM""" |
elemento DOM""" |
106 |
|
|
107 |
for node in fuente.childNodes[:]: |
for node in fuente.childNodes[:]: |
108 |
if not 'Element:' in str(node).split(): fuente.childNodes.remove(node) |
if not 'Element:' in str(node).split(): fuente.childNodes.remove(node) |
109 |
|
|
110 |
def parsear_pnj(self, fuente): |
def parsear_pnj(self, fuente): |
111 |
"""Extrae la información del elemento pnj: |
"""Extrae la información del elemento pnj: |
112 |
autor: el autor de la sala |
autor: el autor de la sala |
113 |
version: la versión del tipo de sala""" |
version: la versión del tipo de sala""" |
114 |
|
|
115 |
self.autor = fuente.attributes["autor"].value |
self.autor = fuente.attributes["autor"].value |
116 |
self.version = fuente.attributes["version"].value |
self.version = fuente.attributes["version"].value |
117 |
try: |
try: |
118 |
self.area = fuente.attributes["area"].value |
self.area = fuente.attributes["area"].value |
119 |
except: self.area = 'sin area' |
except: self.area = 'sin area' |
120 |
|
|
121 |
|
|
122 |
def parsear_id(self, fuente): |
def parsear_id(self, fuente): |
123 |
"""Extrae el id del pnj""" |
"""Extrae el id del pnj""" |
124 |
|
|
125 |
self.id=fuente.childNodes[0].childNodes[0].data.encode('ISO-8859-1') |
self.id=fuente.childNodes[0].childNodes[0].data.encode('ISO-8859-1') |
126 |
|
|
127 |
def parsear_nombre(self, fuente): |
def parsear_nombre(self, fuente): |
128 |
"""Extrae el nombre del pnj""" |
"""Extrae el nombre del pnj""" |
129 |
|
|
130 |
self.nombre=fuente.childNodes[1].childNodes[0].data.encode('ISO-8859-1') |
self.nombre=fuente.childNodes[1].childNodes[0].data.encode('ISO-8859-1') |
131 |
|
|
132 |
|
|
133 |
def parsear_descripcion(self, fuente): |
def parsear_descripcion(self, fuente): |
134 |
"""Extrae el texto y la dificultad asociada de los items |
"""Extrae el texto y la dificultad asociada de los items |
135 |
de descripcion del pnj""" |
de descripcion del pnj""" |
136 |
|
|
137 |
for node in fuente.childNodes[2].childNodes[:]: |
for node in fuente.childNodes[2].childNodes[:]: |
138 |
if not 'Element:' in str(node).split(): fuente.childNodes[2].childNodes.remove(node) |
if not 'Element:' in str(node).split(): fuente.childNodes[2].childNodes.remove(node) |
139 |
|
|
140 |
|
|
141 |
self.descripcion=fuente.childNodes[2] |
self.descripcion=fuente.childNodes[2] |
142 |
self.items={} |
self.items={} |
143 |
|
|
144 |
for item in self.descripcion.childNodes[:]: |
for item in self.descripcion.childNodes[:]: |
145 |
texto_item="" |
texto_item="" |
146 |
for frase in item.childNodes[:]: |
for frase in item.childNodes[:]: |
147 |
|
|
148 |
frase.data=frase.data.encode('ISO-8859-1') |
frase.data=frase.data.encode('ISO-8859-1') |
149 |
|
|
150 |
texto_item=texto_item+frase.data |
texto_item=texto_item+frase.data |
151 |
|
|
152 |
if item._attrs == {}: dif_item = 0 |
if item._attrs == {}: dif_item = 0 |
153 |
else: |
else: |
154 |
dif_item=int(item._attrs.get('dificultad').value) |
dif_item=int(item._attrs.get('dificultad').value) |
155 |
|
|
156 |
self.items.update({dif_item:texto_item}) |
self.items.update({dif_item:texto_item}) |
157 |
|
|
158 |
def parsear_propiedades(self, fuente): |
def parsear_propiedades(self, fuente): |
159 |
|
|
160 |
self.aura=int(fuente.childNodes[3].attributes["aura"].value) |
self.aura=int(fuente.childNodes[3].attributes["aura"].value) |
161 |
self.volumen=fuente.childNodes[3].attributes["volumen"].value |
self.volumen=fuente.childNodes[3].attributes["volumen"].value |
162 |
self.experiencia=int(fuente.childNodes[3].attributes["experiencia"].value) |
self.experiencia=int(fuente.childNodes[3].attributes["experiencia"].value) |
163 |
self.nivel=int(fuente.childNodes[3].attributes["nivel"].value) |
self.nivel=int(fuente.childNodes[3].attributes["nivel"].value) |
164 |
self.vida=int(fuente.childNodes[3].attributes["vida"].value) |
self.vida=int(fuente.childNodes[3].attributes["vida"].value) |
165 |
try: |
try: |
166 |
self.intocable=fuente.childNodes[3].attributes["intocable"].value.encode('ISO-8859-1') |
self.intocable=fuente.childNodes[3].attributes["intocable"].value.encode('ISO-8859-1') |
167 |
except: |
except: |
168 |
self.intocable = 'no' |
self.intocable = 'no' |
169 |
|
|
170 |
def parsear_atributos(self, fuente): |
def parsear_atributos(self, fuente): |
171 |
|
|
172 |
self.fuerza=int(fuente.childNodes[4].attributes["fuerza"].value) |
self.fuerza=int(fuente.childNodes[4].attributes["fuerza"].value) |
173 |
self.destreza=fuente.childNodes[4].attributes["destreza"].value |
self.destreza=fuente.childNodes[4].attributes["destreza"].value |
174 |
self.constitucion=int(fuente.childNodes[4].attributes["constitucion"].value) |
self.constitucion=int(fuente.childNodes[4].attributes["constitucion"].value) |
175 |
self.inteligencia=int(fuente.childNodes[4].attributes["inteligencia"].value) |
self.inteligencia=int(fuente.childNodes[4].attributes["inteligencia"].value) |
176 |
self.sabiduria=int(fuente.childNodes[4].attributes["sabiduria"].value) |
self.sabiduria=int(fuente.childNodes[4].attributes["sabiduria"].value) |
177 |
self.carisma=int(fuente.childNodes[4].attributes["carisma"].value) |
self.carisma=int(fuente.childNodes[4].attributes["carisma"].value) |
178 |
|
|
179 |
def parsear_idiomas(self, fuente): |
def parsear_idiomas(self, fuente): |
180 |
|
|
181 |
self.idiomas = {} |
self.idiomas = {} |
182 |
|
|
183 |
self.oestron = int(fuente.childNodes[5].attributes["oestron"].value) |
self.oestron = int(fuente.childNodes[5].attributes["oestron"].value) |
184 |
self.idiomas.update({'oestron':self.oestron}) |
self.idiomas.update({'oestron':self.oestron}) |
185 |
try: |
try: |
186 |
self.quenya = fuente.childNodes[5].attributes["quenya"].value |
self.quenya = fuente.childNodes[5].attributes["quenya"].value |
187 |
except: |
except: |
188 |
self.quenya = 0 |
self.quenya = 0 |
189 |
self.idiomas.update({'quenya':self.quenya}) |
self.idiomas.update({'quenya':self.quenya}) |
190 |
try: |
try: |
191 |
self.orco=int(fuente.childNodes[5].attributes["orco"].value) |
self.orco=int(fuente.childNodes[5].attributes["orco"].value) |
192 |
except: |
except: |
193 |
self.orco = 0 |
self.orco = 0 |
194 |
self.idiomas.update({'orco':self.orco}) |
self.idiomas.update({'orco':self.orco}) |
195 |
try: |
try: |
196 |
self.enano=int(fuente.childNodes[5].attributes["enano"].value) |
self.enano=int(fuente.childNodes[5].attributes["enano"].value) |
197 |
except: |
except: |
198 |
self.enano = 0 |
self.enano = 0 |
199 |
self.idiomas.update({'enano':self.enano}) |
self.idiomas.update({'enano':self.enano}) |
200 |
|
|
201 |
|
|
202 |
|
|
203 |
def parsear_controlador(self, fuente): |
def parsear_controlador(self, fuente): |
204 |
|
|
205 |
self.controlador = fuente.childNodes[6].attributes["tipo"].value.encode('ISO-8859-1') |
self.controlador = fuente.childNodes[6].attributes["tipo"].value.encode('ISO-8859-1') |
206 |
|
|
207 |
def parsear_objetos(self, doc_xml): |
def parsear_objetos(self, doc_xml): |
208 |
"""Extrae toda la información de los objetos que hay en el pnj inicialmente.""" |
"""Extrae toda la información de los objetos que hay en el pnj inicialmente.""" |
209 |
|
|
210 |
nodo_objetos = doc_xml.childNodes[7] |
nodo_objetos = doc_xml.childNodes[7] |
211 |
|
|
212 |
# Limpiamos todos los subnodos de tipo Text, al igual |
# Limpiamos todos los subnodos de tipo Text, al igual |
213 |
# que hacíamos en la función iniciar_parseador() |
# que hacíamos en la función iniciar_parseador() |
214 |
for nodo in nodo_objetos.childNodes[:]: |
for nodo in nodo_objetos.childNodes[:]: |
215 |
if 'Element:' not in str(nodo).split(): |
if 'Element:' not in str(nodo).split(): |
216 |
nodo_objetos.childNodes.remove(nodo) |
nodo_objetos.childNodes.remove(nodo) |
217 |
|
|
218 |
self.objetos = [] |
self.objetos = [] |
219 |
|
|
220 |
for nodo_objeto in nodo_objetos.childNodes[:]: |
for nodo_objeto in nodo_objetos.childNodes[:]: |
221 |
|
|
222 |
id_objeto = nodo_objeto._attrs.get('id').value.encode('ISO-8859-1') |
id_objeto = nodo_objeto._attrs.get('id').value.encode('ISO-8859-1') |
223 |
try: |
try: |
224 |
cantidad_objeto = int(nodo_objeto._attrs.get('cantidad').value) |
cantidad_objeto = int(nodo_objeto._attrs.get('cantidad').value) |
225 |
except: |
except: |
226 |
cantidad_objeto = 1 |
cantidad_objeto = 1 |
227 |
try: |
try: |
228 |
prob_objeto = int(nodo_objeto._attrs.get('probabilidad').value) |
prob_objeto = int(nodo_objeto._attrs.get('probabilidad').value) |
229 |
except: |
except: |
230 |
prob_objeto = 0 |
prob_objeto = 0 |
231 |
desc_objeto = nodo_objeto.childNodes[0].data.encode('ISO-8859-1') |
desc_objeto = nodo_objeto.childNodes[0].data.encode('ISO-8859-1') |
232 |
|
|
233 |
self.objetos.append({"id": id_objeto, |
self.objetos.append({"id": id_objeto, |
234 |
"cantidad": cantidad_objeto, |
"cantidad": cantidad_objeto, |
235 |
"probabilidad": prob_objeto, |
"probabilidad": prob_objeto, |
236 |
"descripcion": desc_objeto}) |
"descripcion": desc_objeto}) |
237 |
|
|
238 |
|
|
239 |
|
|
240 |
def salvar(self): |
def salvar(self): |
241 |
"""Graba en un fichero de DIR_PNJS el pnj con todo su contenido.""" |
"""Graba en un fichero de DIR_PNJS el pnj con todo su contenido.""" |
242 |
|
|
243 |
fichero = open(os.path.join(Objeto.DIR_PNJS, self.id), 'w') |
fichero = open(os.path.join(Objeto.DIR_PNJS, self.id), 'w') |
244 |
cPickle.dump(self, fichero) |
cPickle.dump(self, fichero) |
245 |
fichero.close() |
fichero.close() |
246 |
|
|
247 |
print "TRAZA: hemos dumpeado con pickle el fichero",self.id |
print "TRAZA: hemos dumpeado con pickle el fichero",self.id |
248 |
|
|
249 |
|
|
250 |
# Métodos que se refieren a la clase pero tienen ámbito de clase, |
# Métodos que se refieren a la clase pero tienen ámbito de clase, |
251 |
# y no de instancia |
# y no de instancia |
252 |
|
|
253 |
def openAnything(source): |
def openAnything(source): |
254 |
"""URI, filename, or string --> stream |
"""URI, filename, or string --> stream |
255 |
|
|
256 |
This function lets you define parsers that take any input source |
This function lets you define parsers that take any input source |
257 |
(URL, pathname to local or network file, or actual data as a string) |
(URL, pathname to local or network file, or actual data as a string) |
258 |
and deal with it in a uniform manner. Returned object is guaranteed |
and deal with it in a uniform manner. Returned object is guaranteed |
259 |
to have all the basic stdio read methods (read, readline, readlines). |
to have all the basic stdio read methods (read, readline, readlines). |
260 |
Just .close() the object when you're done with it. |
Just .close() the object when you're done with it. |
261 |
|
|
262 |
Examples: |
Examples: |
263 |
>>> from xml.dom import minidom |
>>> from xml.dom import minidom |
264 |
>>> sock = openAnything("http://localhost/kant.xml") |
>>> sock = openAnything("http://localhost/kant.xml") |
265 |
>>> doc = minidom.parse(sock) |
>>> doc = minidom.parse(sock) |
266 |
>>> sock.close() |
>>> sock.close() |
267 |
>>> sock = openAnything("c:\\inetpub\\wwwroot\\kant.xml") |
>>> sock = openAnything("c:\\inetpub\\wwwroot\\kant.xml") |
268 |
>>> doc = minidom.parse(sock) |
>>> doc = minidom.parse(sock) |
269 |
>>> sock.close() |
>>> sock.close() |
270 |
>>> sock = openAnything("<ref id='conjunction'><text>and</text><text>or</text></ref>") |
>>> sock = openAnything("<ref id='conjunction'><text>and</text><text>or</text></ref>") |
271 |
>>> doc = minidom.parse(sock) |
>>> doc = minidom.parse(sock) |
272 |
>>> sock.close() |
>>> sock.close() |
273 |
""" |
""" |
274 |
|
|
275 |
# try to open with urllib (if source is http, ftp, or file URL) |
# try to open with urllib (if source is http, ftp, or file URL) |
276 |
import urllib |
import urllib |
277 |
try: |
try: |
278 |
return urllib.urlopen(source) |
return urllib.urlopen(source) |
279 |
except IOError: |
except IOError: |
280 |
pass |
pass |
281 |
|
|
282 |
# try to open with native open function (if source is pathname) |
# try to open with native open function (if source is pathname) |
283 |
try: |
try: |
284 |
return open(source) |
return open(source) |
285 |
except IOError: |
except IOError: |
286 |
pass |
pass |
287 |
|
|
288 |
# assume source is string, create stream |
# assume source is string, create stream |
289 |
import StringIO |
import StringIO |
290 |
return StringIO.StringIO(source) |
return StringIO.StringIO(source) |
291 |
|
|
292 |
|
|
293 |
|
|
294 |
def Objeto_cargar(archivo): |
def Objeto_cargar(archivo): |
295 |
fichero = open(os.path.join(Objeto.DIR_OBJETOS, archivo), "r") |
fichero = open(os.path.join(Objeto.DIR_OBJETOS, archivo), "r") |
296 |
objeto = cPickle.load(fichero) |
objeto = cPickle.load(fichero) |
297 |
fichero.close() |
fichero.close() |
298 |
|
|
299 |
return objeto |
return objeto |
300 |
|
|
301 |
|
|
302 |
# Código para pruebas unitarias del módulo. |
# Código para pruebas unitarias del módulo. |
303 |
|
|
304 |
if (__name__ == '__main__'): |
if (__name__ == '__main__'): |
305 |
print "No hay prueba unitaria de módulo. prueba con pnjtester.py" |
print "No hay prueba unitaria de módulo. prueba con pnjtester.py" |