![]() | ![]() | ![]() | Definiendo nuevos verbos y extendiendo los existentes |
Hemos mencionado en la sección anterior unos cuantos verbos que se le pueden ocurrir al jugador. Uno de ellos es, por ejemplo DESATAR FUNDA. La respuesta por defecto de Inform ante ese comando es "No conozco ese verbo." La librería Inform proporciona una buena cantidad de verbos ya programados, pero tarde o temprano llegarás a uno que no estaba programado. Aquí mostraremos cómo añadir un verbo nuevo al lenguaje manejado por Inform.
La primera cuestión que debes plantearte es: además de un nuevo verbo, ¿necesito una nueva acción? Hay
casos en los que no es así, sino que un verbo nuevo da lugar a una de las acciones previamente existentes. Este
es el caso más fácil de manejar. Por ejemplo, el verbo MORDER no está previsto en
Inform. Si el jugador intenta MORDER
BARROTES, le dirá que no comprende ese verbo. Podemos añadir ese verbo para que genere la acción
Eat (Comer)
, que ya existe en Inform, asociada al verbo COMER. Para lograr esto, la cosa es tan simple como
poner lo siguiente después de Include "Gramatica"
Verb 'muerde' = 'come'; |
![]() | Un detalle importantísimo: siempre que añadas un nuevo verbo, debes hacerlo en IMPERATIVO, y no en infinitivo. No debes poner 'morder' sino 'muerde'. Y la hora de hacerlo sinónimo de un verbo preexistente, el verbo original también estará en imperativo, es decir 'come' en lugar de 'comer'. |
Ahora el juego ya comprenderá MUERDE BARROTES, generará la acción Take barrotes
, y producirá
la respuesta por defecto "Eso es simplemente incomestible". Si quieres otra respuesta basta ponerla en la
rutina antes de los barrotes, para la acción Eat (Comer)
.
Sin embargo el juego no comprende MORDER BARROTES. El verbo que hemos añadido está en forma imperativa. Inform es capaz de deducir por sí solo la forma infinitiva si el verbo es regular, ya que en este caso basta añadir una R. Por ejemplo, "come" se convierte en "comer". En cambio "muerde" se convertiría en "muerder", por lo que Inform no sabe hacerlo sin tu ayuda. Esto no sólo afecta a los comandos que escribe el jugador, sino a algunas frases que Inform genera por sí solo a veces. Por ejemplo, si el jugador pone MUERDE sin más, Inform detectará que la frase está incompleta, y le preguntará "¿Qué quieres muerder?", de nuevo en este caso genera la forma infinitiva símplemente añadiendo una R a la forma imperativa, que es la que el programador ha definido.
Para evitar ambos errores, el programador debe decirle a Inform que morder es un verbo irregular. Para esto, basta añadir la siguiente línea a continuación:
VerboIrregular 'morder' with imperativo 'muerde'; |
![]() |
Recuerda siempre que definas un nuevo verbo, comprobar si es regular, es decir, si
al añadirle una R sale el infinitivo. Si no lo es, debes definir su forma infinitiva
mediante una línea como la anterior. Si no lo haces el juego no comprenderá el
comando en inifinitivo.
De este error nadie está libre. El propio autor de este cursillo olvidó declarar como irregular el verbo regar en la Aventura original, y como consecuencia el juego sólo admitía la forma imperativa "riega". (Bueno, y también admitía la forma aberrante "riegar", pues sale añadiendo una R a lo que Inform conocía) |
Un caso más complejo aparece con DESATAR. Este es un verbo nuevo, pero la acción que debe generar es
también nueva. No hay acción Desatar (o UnTie)
en Inform, y no parece apropiado convertir la orden DESATA
FUNDA en otra cosa rara como "mover funda". Lo propio será definir la nueva acción y asociar el nuevo verbo con ella.
Definir una acción nueva consiste en programar una rutina cuyo nombre ha de ser el mismo que la acción,
más la palabra Sub
. En nuestro ejemplo el nombre de la rutina sería DesatarSub
. Esta rutina será ejecutada
como "acción por defecto". Es decir, si el jugador pone DESATAR FUNDA, el parser generará la acción
Desatar
con noun=funda
y como ya sabemos esto da lugar a una serie de "avisos" que el parser envía a los
objetos cercanos a través de la rutina reaccionar_antes de estos objetos. Si ninguno de los objetos "dice
nada", entonces finalmente se llamará a la rutina antes del propio objeto que el jugador ha nombrado (la
funda). Si esta rutina tampoco "dice nada" (es decir, si no retorna true), será entonces cuando Inform ejecutará la rutina
DesatarSub
y dará el asunto por terminado.
Por tanto, la rutina DesatarSub
hay que programarla bajo este punto de vista: se ejecutará cuando el
jugador intente "desatar algo", y ese "algo" no maneja por sí mismo la situación. Hay dos alternativas a la hora
de programar una rutina de acción:
Desatar
desde la
rutina antes del objeto en cuestión.Burn (Encender)
sí que hace algo (mira si el objeto en cuestión es conmutable, si está ya encendido,
mostrando mensajes apropiados en cada caso, o bien cambiando su atributo on (encendido)
si procede).
En Inform, aunque la acción Tie (Atar)
existe, en realidad la libería no prevé que haya objetos que puedan ser
atados, en la misma forma que prevé que hay objetos que pueden ser abiertos/cerrados o
encendidos/apagados. La rutina TieSub
que ya viene con Inform es "de las que no hacen nada",
simplemente emite el mensaje "No lograrás nada así" cada vez que el jugador intente atar algo.
Si nuestro juego va a contener gran cantidad de cuerdas, que pueden ser atadas y desatadas a diferentes
objetos del juego, habría que plantearse añadir este nuevo tipo de objeto al modelo del mundo. Habría que
"inventar" el tipo de objetos "atables", y entonces cuando el jugador ponga ATAR noun
A second
, la rutina
TieSub
debería comprobar si noun
es del tipo atable, y si lo es, modificar de alguna forma su estado para
señalar que ha quedado atado a second
. Esto puede tener implicaciones profundas, por ejemplo ¿qué pasaría
cuando el jugador intenta moverse de una localidad, llevando en su mano una cuerda que está atada a un
objeto fijo de esa localidad? ¿Se le debe permitir? ¿Habrá que tener en cuenta la longitud de la cuerda? ¿Y si
una cuerda se ata a otra cuerda?
El manejo de cuerdas u objetos atables de forma totalmente genérica, es un asunto bastante difícil que se sale
de los objetivos de este cursillo. Por suerte, en nuestro juego el único objeto que puede ser atado es la funda, y
además no necesitamos que pueda atarse a cualquier cosa, sino solo que pueda atarse al barrote. Así que ese
caso particular podemos manejarlo, como hemos hecho en el capítulo anterior, desde la rutina before
de la
propia funda.
Por tanto, nuestra rutina DesatarSub
será también de las que "no hacen nada" (o sea, simplemente emiten
un mensaje). Y dejaremos que la propia funda maneje la situación desde su rutina before
. Así que lo único a
decidir es qué mensaje poner cuando el jugador intente DESATAR ALGO que no sea la funda. Digamos que
vamos a poner "Eso no puede desatarse". Entonces la rutina será la siguiente:
[ DesatarSub ; print_ret "Eso no puede desatarse."; ]; |
Por el mero hecho de haber programado una rutina llamada DesatarSub
, tenemos ya una nueva acción
llamada Desatar
(la trabajaremos en español). Esa acción puede manejarse desde la rutina before
de cualquier objeto,
y en particular, desde la funda. Para poner algo como esto:
Desatar: if (funda hasnt general) "La funda no está atada a ningún sitio."; give funda ~general; "Desatas la funda de los barrotes."; |
Observa que este código está escrito específicamente para este juego, ya que si la funda no tiene el atributo
general
, es que no ha sido atada a nada, y si lo tiene es que está atada a los barrotes, ya que en este juego
no dejamos que el jugador ate la funda a ningún otro lugar. Si quisiéramos hacer un juego más flexible,
deberíamos dejar que el jugador pudiera atar la funda a donde quisiera, aunque no sirviera para nada. Por
ejemplo, puede ocurrirsele atar la funda al esqueleto. Esto complicaría las cosas, ya que no basta con tener un
atributo como general para saber si la funda está atada o no, sino que además habría que tener una nueva
propiedad que almacene a qué objeto está atada. Cuanta más flexibilidad se le quiera dar al jugador, más se
complica la programación.
Ya solo queda una cosa por hacer. La más importante de todas. Se trata de asociar un verbo con esta nueva acción. Si olvidamos hacerla, el jugador no podrá nunca DESATAR LA FUNDA, ya que, aunque el juego prevé la acción desatar, esa acción no se genera nunca, ya que no hay ningún verbo que la genere. Nada de lo que pueda escribir el jugador daría lugar a esa acción.
Definir una asociación entre verbo y acción es lo que en Inform se conoce como "definir una gramática".
En realidad, la gramática no asocia un verbo con una acción, sino un "tipo de frase" con una acción. Por
ejemplo, en nuestro caso queremos que entienda frases como DESATA FUNDA, y DESATA FUNDA DE
BARROTE (tal como hemos programado la acción Desatar
en la funda, el jugador no necesita especificar
el barrote, pues lo damos por supuesto, pero si de todas formas decide ponerlo, el juego debería aceptar la
orden).
Por tanto tenemos dos "tipos de frase" que el juego debe comprender. Ambos tipos comienzan por el verbo
"desata", y después:
Inform permite asociar a cada tipo de frase una acción diferente, pero en este caso particular no nos
interesa. Ambos tipos de frase generan la misma acción Desatar
. Así que la "gramática" que tenemos que
escribir en nuestro juego (después del Include "SpanishG"
) será:
Verb 'desata' * noun −> Desatar * noun 'de' noun −> Desatar; |
Analicemos qué significa esto:
Verb
que indica que vamos a definir un nuevo verbo.
'desata'
entre comillas simples, es el verbo a definir. Recuerda que debes ponerlo
siempre en forma imperativa (esto es, no 'desatar'). Esta es la palabra que tendrá que teclear el
jugador. Podemos poner varias, separadas por espacios, como en la propiedad nombre de un objeto.
En ese caso se entiende que todas esas palabras son el mismo verbo.
noun
, una flecha, y Desatar
. Lo que significa es que
noun
puede ser el nombre de cualquier objeto que el jugador tenga a su alrededor. Es decir, podría
ser algo tan simple como "funda", o tan complejo como "espada magica con incrustaciones de rubi".
El parser decidirá que una serie de palabras más o menos larga es un nombre de objeto, si todas esas
palabras aparecen en la propiedad nombre de un objeto.Si el parser llega al final de la frase y todas las palabras tecleadas
por el jugador le "encajan" como
nombre de objeto, el parsing ha tenido éxito, y la acción generada será Desatar
(la que se indique
después de la flecha), y la variable uno apuntará al objeto cuyo nombre ha encajado con todo eso.
noun
aparece dos veces, con la palabra 'de' en medio (ojo,
esta palabra debe ir con comillas simples). Así que el parser espera ahora una secuencia de palabras
que se refieran a un objeto, seguida de la palabra 'de', seguida de otra secuencia de palabras que se
refieran a otro objeto (o tal vez al mismo). Por ejemplo: DESATA FUNDA SUCIA DE BARROTE
FIJO. En este caso, si el objeto funda tiene en su nombre las palabras 'funda' y 'sucia', las palabras
"funda sucia" serán comprendidas como referidas a ese objeto, y la variable noun
apuntará a él.
Después aparece la preposición 'de', tal como el parser espera. Finalmente aparecen las palabras
"barrote fijo", que el parser intentará encajar con el nombre de algún objeto cercano. Si el barrote
está cerca, finalmente se generará la acción Desatar
con noun==funda
,
second==barrote_fijo
.
![]() | En Inform, las acciones sólo se generan si el parser tiene éxito al tratar de comprender la frase. Y el parser sólo tendrá éxito si todos los objetos que se mencionan en la frase están disponibles para el jugador. Esto ahorra al programador el tener que comprobar si el jugador está en la misma localidad que la funda, y cosas así que eran típicas en PAWS. |
![]() |
El parser de Inform+INFSP es inteligente con respecto a la preposición "de". Imagina que en el
juego tienes una "funda de plástico". ¿Cómo sabe el parser si DESATA FUNDA DE
PLASTICO pertenece a una frase del tipo 1 (desata cosa) o del tipo 2 (desata cosa de cosa)?
¿Y si ponemos DESATA FUNDA DE PLASTICO DE BARROTE DE HIERRO? ¡Qué
locura! ¡Cuántas veces aparece la preposición "de"! No te preocupes, el parser no se
confundirá, e interpretará "funda de plastico" como el primer nombre y "barrote de hierro"
como el segundo, con tal de que el objeto El truco que usa Inform+INFSP para no liarse con los "de", es que simplemente los ignora, si las palabras que tiene alrededor del "de" son nombres de un mismo objeto. Así, en DESATA FUNDA DE PLASTICO DE BARROTE DE HIERRO, el primer DE es ignorado, pues las palabras FUNDA y PLASTICO son del mismo objeto. El segundo DE no es ignorado, pues las palabras PLASTICO y BARROTE no son del mismo objeto. Finalmente, el tercer DE sí es ignorado, porque BARROTE y HIERRO son del mismo objeto. Así que la frase final queda como DESATA FUNDA PLASTICO DE BARROTE HIERRO. |
¿Y si tanto el verbo como la acción ya estaban definidos en Inform, pero no hacen lo que yo quiero que hagan, o les falta algún "tipo de frase"?
Por ejemplo, el verbo 'ata' ya está definido, y la acción Tie (Atar)
también. Sin embargo el juego no entiende ATA
FUNDA EN BARROTE. ¿Por qué? ¿Qué hacer?
La razón por la que no lo entiende, es porque Inform sólo define algunos "tipos de frase" para el verbo
'ata', que son los siguientes (el siguiente fragmento está sacado del fichero SpanishG.h
):
Verb 'ata' 'enlaza' 'enchufa' 'une' * noun -> Tie * 'a//' creature -> Tie * 'a//' creature 'a//' noun -> Tie * noun 'a//' noun -> Tie; |
Vemos aquí que Inform define como sinónimo de 'ata', el verbo 'enchufa' y 'une'. Esto significa que, curiosamente, en nuestro juego de La Torre, el jugador puede escribir ENCHUFA FUNDA A BARROTE, ¡y funcionará!
Pero olvidémonos de momento de eso, y veamos el resto de la gramática del verbo 'ata'. Observa que se
definen para este verbo cuatro "tipos de frase", y todas ellas generan la misma acción Tie
. La primera es un
simple noun
, para admitir cosas como ATA FUNDA.
La segunda tiene un 'a//'. La doble barra es una rareza de Inform. Necesitas ponerla si la preposición que
quieres especificar consta de una sola letra. Si te olvidas de esa doble barra, en el fondo esta línea significa
que tras 'ata', debe venir la palabra 'a', y despues una criatura. creature
es como noun
, solo que además el
parser sólo aceptará la orden si el objeto que le sale tiene el atributo animate
(no hemos visto nada de este
atributo, pero es básico para introducir otros personajes en la aventura). Por tanto esta línea está pensada para
admitir comandos como ATA A THORIN. En cambio rechazará la orden ATA A FUNDA, ya que FUNDA
no es un ser animado (el parser dirá "Sólo puedes hacer eso con seres animados").
La tercera es similar a la anterior, sólo que permite especificar un segundo objeto. Por ejemplo ATA A THORIN A LA ESTACA.
![]() |
Por cierto, no debes preocuparte si el jugador usa 'AL' en lugar de 'A', en una frase como ATA AL ORCO AL PALO. Inform convierte esos 'AL' en 'A' antes de ponerse a interpretar la frase. Lo mismo cabe decir para los 'DEL', que son convertidos en 'DE'. |
Finalmente, la cuarta es para frases del tipo ATA FUNDA A BARROTE.
Como vemos, no se especifica ninguna frase del tipo ATA FUNDA EN BARROTE, y por eso esta orden fracasa.
Por suerte es fácil "extender" un verbo para añadirle más "tipos de frase". Una forma sería abrir el fichero
SpanishG.h
, buscar las líneas donde está definido el verbo 'ata'
, y añadirle tú mismo nuevas líneas. Esta
forma se desaconseja, no conviene tocar los ficheros que forman parte de Inform, porque si quieres
compartir tu código fuente con otras personas y has modificado la librería, deberías indicar a esas otras
personas qué modificaciones has hecho para que ellos las hagan también. A la larga sería un caos.
Inform proporciona un método para extender gramáticas sin necesidad de retocar el fichero
SpanishG.h
. Consiste en hacer uso de la palabra especial Extend
, y seguidamente definir las nuevas
gramáticas usando la misma sintaxis que para definir un verbo nuevo.
Así que en nuestro caso, bastaría añadir lo siguiente tras el Include "SpanishG.h"
:
Extend 'ata' * noun 'en' noun −> Tie; |
Esto añade un quinto "tipo de frase" a los ya definidos. Observa que si quisiéramos ser coherentes con lo que ya está definido en Inform, no estaría de más añadir un sexto tipo, para seres animados, que admita el comando ATA A THORIN EN LA ESTACA. ¿Cómo se haría?
El usar la palabra especial Extend
, tiene el mismo efecto que añadir la línea en cuestión en el fichero
SpanishG.h
, al final de las que ya había. Si en lugar de añadirla al final, quisiéramos añadirla al principio,
de modo que nuestra línea sea el "primer tipo de frase" para ese verbo, habría que poner:
Extend 'ata' first * noun 'en' noun −> Tie; |
La diferencia entre ponerla la primera o la última no es importante en este caso, pero en otros sí tiene su importancia. Debes saber que el parser de Inform va probando los diferentes "tipos de frase" en el orden en que los encuentra, y tan pronto como uno de ellos "encaje perfectamente", ya no seguirá mirando los demás.
En la gramática que hemos visto, la palabra especial noun
representa al nombre de cualquier objeto al que el
jugador pueda referirse. Inform sólo aceptará la orden del jugador si es capaz de encontrar un objeto "en
las inmediaciones" cuyo nombre coincida con lo tecleado por el jugador, que puede ser una sola palabra como
"COFRE" o varias como "COFRE DE MADERA DE ROBLE".
En el párrafo anterior hay un concepto por explicar, y es ¿Qué son exactamente "las inmediaciones" del jugador? Dicho de otro modo, si el jugador lleva una bolsa y dentro de ella hay un trozo de carbón, ¿entenderá el parser "EXAMINA TROZO DE CARBON"? ¿Puede considerarse que el trozo de carbón aún está "en las inmediaciones del jugador" incluso si está encerrado en una bolsa donde no puede verse?
La respuesta a la pregunta anterior es complicada. Inform considera por defecto que "las inmediaciones del jugador" se componen de todos los objetos que hay en la misma localidad que el jugador, más todos los objetos que hay en el inventario del jugador, más todos los objetos que pueda haber dentro de cualquiera de estos objetos, a menos que se hallen en un recipiente cerrado que no sea transparente. Así que la orden EXAMINA TROZO DE CARBON será comprendida por el parser sólo si el trozo de carbón está en la misma localidad, o si el jugador lo lleva consigo, o si está dentro de un recipiente abierto o dentro de un recipiente cerrado transparente. Si no se cumple lo anterior, es decir, si el carbón por ejemplo está en una bolsa cerrada opaca, el parser directamente ¡no comprenderá la frase!
![]() |
Si el objeto al que se refiere el jugador no está en las inmediaciones, no es que la acción fracase. ¡Es que ni siquiera se produce ninguna acción, ya que el juego simplemente no entiende lo que el jugador quiere decirle! |
Por otro lado, quizás en algún juego necesites acciones "raras" que puedan operar sobre objetos que no están en las inmediaciones. Por ejemplo, imagina un juego que tenga el verbo "TELEPORTA", que pueda operar sobre un objeto que está en otra localidad, de modo que si el jugador pone TELEPORTA CARBON, el carbón aparezca en su mano. Un verbo así es mucho más difícil de programar. Podemos intentar el enfoque que hemos aprendido antes y definir el verbo y su acción asociada de esta forma:
Verb 'teleporta' * noun −> Teleportar; [ TeleportarSub; move noun to jugador; "Aplicando tu poder de teleportación, te concentras y a los pocos segundos ", (el) noun, " se materializa en tu mano."; ]; |
Pero sin embargo no funcionará como queremos. Si el jugador pone TELEPORTA CARBON cuando el
carbón no está "en las inmediaciones", el parser directamente no entenderá la frase, y la acción no se
producirá. Por tanto no llegará a ejecutarse nunca la rutina TeleportarSub
. El jugador sólo podrá usar este
verbo sobre objetos que estén en sus inmediaciones ¡pero eso le quita toda la gracia!
Inform tiene previsto que algunos juegos puedan necesitar cosas así, de modo que permite al
programador que defina como quiera "qué son las inmediaciones". Así, para un verbo concreto (por ejemplo
para el verbo "teleporta"), las inmediaciones puedan ser el mundo entero y todos los objetos que contiene,
mientras que para otros verbos las "inmediaciones" conservan su significado original. La forma exacta de
definir qué son las inmediaciones para un verbo concreto es complicada, y se sale de los objetivos de este
cursillo. Pero que sepas que se puede hacer, y puedes averiguar cómo leyendo el manual DocumentATE, en la
sección sobre "el alcance" (DocumentATE llama "alcance" (ingles scope
lo que aquí hemos llamado inmediaciones).
También se pueden definir verbos que sólo funcionen sobre un subconjunto de las inmediaciones. Por
ejemplo, verbos que sólo funcionan sobre objetos que el jugador tenga en su poder. El verbo EXAMINAR
funciona sobre cualquier objeto, pero el verbo DEJAR, sólo debería funcionar sobre las cosas que el jugador
lleva consigo. Imagina que quieres en tu juego el verbo EXPRIMIR. Evidentemente, el jugador debe coger un
objeto antes de poder exprimirlo. Para forzar a que esto sea así, cuando defines la gramática de ese verbo
debes poner la palabra held
en lugar de la palabra noun
. La cosa quedará así:
Verb 'exprime' * held −> Exprimir; VerboIrregular "exprimir" with imperativo 'exprime'; [ ExprimirSub; "Aprietas ", (el) noun, " pero no sale nada."; ]; |
Ahora, si el jugador lleva consigo el carbón y pone EXPRIMIR CARBON, obtendrá el mensaje "Aprietas el
carbón, pero no sale nada." Si el carbón está en otra localidad, obtendrá el mensaje "No veo eso por aquí" (y la
acción no se genera). Y el caso interesante es cuando el carbón está en la localidad, pero no en el inventario
del jugador. En este caso, el parser se da cuenta de que la acción no puede realizarse, hasta que el jugador
tenga el carbón consigo. De modo que emite el mensaje "(primero coges el carbón)" y genera la acción
Take carbon
. Si esta acción tiene éxito, a continuación genera la acción Exprimir Carbon
. Fijate
que la acción TAKE CARBON podría fracasar. Por ejemplo, el carbón podría tener en la rutina before
un
caso así:
before [; Take: if (carbon hasnt general) "El carbón está todavía demasiado caliente para cogerlo."; Blow: if (carbon has general) "Ya lo has soplado bastante."; give carbon general; "Soplas el carbón, enfriándolo."; ], |
Y en este caso, esto sería lo que vería el jugador si pone EXPRIMIR CARBON:
> EXPRIME CARBON (primero coges el carbón) El carbón está todavía demasiado caliente para cogerlo. > SOPLA CARBON Soplas el carbón, enfriándolo. > EXPRIME CARBON (primero coges el carbón) Aprietas el carbón, pero no sale nada. |
Es decir, el primer intento de exprimir no llega a producirse, ya que la acción TAKE implícita fracasó. En cambio el segundo sí.
![]() |
Al poner held en lugar de noun al definir un verbo, dices al parser que
el objeto debe estar
en posesión del jugador. Si no lo está, el parser genera una acción TAKE automáticamente
para que lo esté. |
Además de noun
y held
hay otras palabras especiales que puedes usar al definir una gramática, pero ¡ya
está bien para un cursillo! Si quieres saber más, lee el manual DocumentATE
(preparado para informATE!, pero valido para inform+INFSP) y su version en
inglés, el Inform
Designer's Manual.
Por supuesto, pasate por los foros del CAAD. ¡Esperamos tus inquietudes!
FIN
![]() | ![]() | ![]() | Definiendo nuevos verbos y extendiendo los existentes |