lunes, 30 de noviembre de 2009

Orcoscomp






OrcosComp
Fecha límite de recepción de obras 31 de Diciembre de 2009.

miércoles, 24 de junio de 2009

jueves, 21 de mayo de 2009

La Venganza de Yan, release 0


Acabo de subir al CAAD La Venganza de Yan.

Yan, ausente durante años labrándose un futuro en el ejército, regresa unos días a su isla natal.
Y en ella descubre que de la aldea sólo quedan ruinas y brasas aún calientes y humeantes.
¿Quién ha podido hacer esto?
¡Quien haya sido lo pagará!

Ocupa unos 10 megas, de los cuales la mitad son gráficos, luego sonido, y por último el código en sí, que son 500 Kb.

Para más información podéis leer el comentario de Urbatain en SPAC, tras testear la aventura, o entradas anteriores de este mismo blog para detalles técnicos.

miércoles, 20 de mayo de 2009

me paso a Damusix

El sistema de sonido que venía usando hasta ahora, basado en Efectos.h, tenía un fallito relacionado con el volumen de los canales de sonido, que a veces sonaban más bajo como si arrastrasen una configuración anterior sin actualizarse correctamente.
Pero ayer Urba me reportó un nuevo bug gordísimo: al guardar una posición con el sonido desactivado, y volver a cargarla, el sonido reaparecía y no había forma ya de quitarlo.

Como en el tema de la restauración GLK mis conocimientos se reducen al COPYPASTE y se me estaba hinchando la cabeza, opté por la mejor solución. Damusix funciona bien... pues me paso a DAMUSIX.

El manual parece un tocho, pero está escrito con letras grandes y tampoco es para tanto. Es una librería bastante sencilla de usar, con funciones minimalistas -como las llama el autor- que me han venido de miedo para realizar la sustitución a nivel de funciones de sonido sin tener que reescribir un código que ya estaba hecho. A parte que me servirá para parchear otras aventuras antiguas.
Por ejemplo, la instrucción musica (sonido, volumen); la mantengo en el código, pero el contenido de la función en lugar de llamar a instrucciones de Efectos.h, ahora llama a instrucciones de DAMUSIX.

[musica que_sonido quevolumen;
Damusix.AsignarCanal(que_sonido,0,quevolumen,-1);
Damusix.Tocar(que_sonido);
cancionquesuena=que_sonido;
bajandovolumen=0;
cancionquesonara=0;
rtrue;
];

Y así con todas. Algunas variables son ya innecesarias, pero las mantengo porque se usan en los códigos, y se trata de que estos tengan que modificarse lo mínimo.

Es una librería muy fácil de utilizar. No hay que quebrarse la cabeza ni planificar nada. Sólo mandarle instrucciones al kernel de la librería, y éste ya se ocupa de lanzar un sonido, apagar otro anterior que ocupara el mismo canal; o de cosas más peliagudas como las restauraciones y los UNDO.

El único tropezón lo he tenido por las interferencias de los HandleGlkEvent e IdentifyGlkObject de la librería DAMUSIX con los míos propios. Culpa mía por lanzarme a compilar sin terminar de leer el manual, donde se explicaba la resolución de estos puntos.

sábado, 2 de mayo de 2009

interferencias de la librería Inform

Pues resulta que el código para analizar las cadenas de texto introducidas en el prompt (en el BeforeParsing) falla con "dame", no lo detecta, y sólo me queda pensar que la librería transforma los "dame" en otra cosa distinta antes si quiera de que el programador pueda intervenir.

En efecto, he probado a detectar "da" (es decir "d"+"a"+longitud de la cadena=2), y cuando escribo "dame", me lo da por bueno. Comprobado entonces que cada vez que escribes "dame", se descompone en "da" + personaje_jugador.

Un pelín fastidio, ya que te imposibilita la intervención desde el código cuando, como en este caso, pretendes hacer un parseado alternativo.

Ahora me toca revisar todas las formas reflexivas y eliminarlas.

miércoles, 29 de abril de 2009

Gráficos con Inkscape

Llevo cosa de un mes usando Inkscape, a la par que Urbatain (lo cual habrán notado por su serie de pruebas de concepto de El Anillo).

Haciendo los gráficos para "La venganza de Yan", empecé con un estilo sencillo para poder ir rápido. El caso es que cuando vas cogiendo destreza, puedes hacerlo menos sencillo sin perder velocidad.
He usado mucho del reciclaje de siluetas para abarcar las diferentes localidades de una misma zona.
Por ejemplo, para dibujar un bosque pinto unos pocos modelos de follaje, ramas y troncos, y lo que sigue es un copy-paste a lo bestia. Una vez compuesta la escena, se copia y se reordena todo para hacer otra localidad.

Y no todo es Inkscape. Aunque es ideal para dibujar las siluetas y calcar de imágenes... para los retoques finales o para dibujar las sombras de los personajes es preferible usar un programa de retoque fotográfico, a base de máscaras.
Así lo estoy haciendo finalmente, tras una fase incial de intentar obtener el acabado definitivo con el propio Inkscape.

El nivel de calidad ha ido in-cresccendo, y eso se nota comparando los primeros dibujos con los últimos. Las primeras localidades voy a tener que redibujarlas porque se han quedado muy atrás.






El tamaño de los gráficos es de 720 x 195 px

lunes, 6 de abril de 2009

La Venganza de Yan

Estos días estoy programando una historia que se me ocurrió el jueves. La programación va a buen ritmo, sólo que me falta por planear las secuencias finales (que no el final). Realmente ha ido saliendo todo improvisado a partir de la idea general.
La ambientación está inspirada en un antiguo largometraje animado chino llamado "Taro el niño Dragón (Tatsunoko Taro)"... aunque de la inspiración a la expiración hay un trecho ;D

Por fín voy a implementar para algo más que para juegos de palabras lo que aprendí del código de "Ad Verbum": para conversaciones con PSIS y como apoyo a otros objetos del juego, para poder especificar detalles sin necesidad de crear nuevos objetos ligados.

Se trata de un parseado en paralelo, que no suplanta sino que complementa, un parseado de reconocimiento de cadenas o de trozos de texto.
Inform funciona igual que siempre, sólo que cuando quiero le consulto al otro parseado qué ha encontrado, y así puedo dar mejores respuestas a las cosas que me interesen.

He dispuesto 5 variables que recolectan los valores coincidentes del parseado en paralelo, y son estas variables las que se consultan luego desde el código de los objetos y las funciones.

Es un sistema a la vieja usanza. Te tienes que haces una tabla y apuntar qué número está representando a qué concepto (uso "concepto" para describir un grupo de palabras o cadenas que vienen a ser sinónimas en el contexto del juego). Y a la vez escoger bien cómo haces las listas, en qué orden pones las cadenas en el código y cuál de las 5 variables será la encargada de almacenar cada tipo de concepto. En principio una variable cazará conceptos referidos a pronombres, otra cazará partículas interrogativas, otra acciones, otra nombres y otra adjetivos, en principio, porque en la práctica se mezcla todo un poco para aprovechar donde no se necesita y no se prevee que haya conflicto.

La detección de cadenas se realiza por barrido secuencial, no por el orden en que ha sido escrito, sino por el orden en el que cada cadena está escrita en el código. Cada variable adquiere el valor del último concepto coincidente de su lista personal. De modo que si dos cadenas vitales están en la misma lista, la variable sólo almacenará la que esté más abajo en el código.

Así he empezado a programar y ya es tarde para cambiarlo, pero explicaré un sistema mejor:
En lugar de unas pocas variables para muchos conceptos...
Una variable para cada concepto, y claro, no usaremos variables sino Flags (banderas), que apenas usan memoria. Puedes crear 5000 flag y el parser ni pestañea.

De esta forma, antes de cada input ponemos todos los Flags encargados de pescar conceptos en false, y procedemos a hacer el barrido.

En nuestro código del PSI, tendremos cosas como éstas:
react_before[;
hablar:
print "Pepito dice:^";
if(FlagOn(100) && FlagOn(156)) "Me llamo Jhames.";
if(FlagOn(101) && FlagOn(157)) "El pescador se llama Josema.";
if(FlagOn(111) && FlagOn(157)) "¡¿Cómo que quieres matar al pescador?!.";
],


Como decía, hay que tener la chuleta al lado, y veremos las cadenas que activan cada flag:
Flag 100: llama*, nombre
Flag 111: mata*, asesina*, liquid*
Flag 156: tu, usted, te
Flag 157: pesc*d*r



(*) Como esto va por fragmentos, los asteriscos los he puesto a modo representativo para que se vean las partes que se pueden omitir sin problemas. Para palabras largas, para preveer varias flexiones... simplemente detectas las letras de la palabra que son fijas y que son imprescindibles para distinguirla de otra similar.
También se puede fijar la longitud.
Podemos detectar de un plumazo "amigo" y "amiga", programando la detección de "a"+"m"+"i"+"g+"+longitud=5.
Aunque para este ejemplo, lo dejaríamos en "a"+"m"+"i"+"g". Ya que la secuencia "amig" no presenta demasiado riesgo de confundirsem (o estar fundida dentro) con otra palabra. Sin determinar la longitud, el Flag saltaría a la que el jugador escribiera "amigo", "amiga", "amigos", "amigas", "amigable", "amigablemente"... etc

Y así funciona. Que el parser detecte que quieres que "Fulano te acompañe hasta el rio cantando una canción, con las manos en la cabeza y dando saltitos" es posible técnicamente, aunque aviso que no pienso implementar chorradas de ese estilo XD.

La máxima utilidad es distinguir intenciones y preguntas respecto a sujetos.
Puedes decir "Fulano"... ¿Fulano qué?
Dónde vive Fulano.
Quién es Fulano el de la derecha.
Quién es Fulano el de la izquierda.
...
La espada de Fulano... ¿qué pasa con la espada de fulano?
Dame la espada de Fulano
Encuentra la espada de Fulano
Fulano no tiene espada
...
Quiero matar a Fulano.

Hoygan nesesito un krak para Fulano es urjente.

Fuera de las conversaciones, esto sigue siendo muy útil. Por ejemplo, para un combate:
Tenemos un objeto "troll" y queremos poder elegir a dónde le atizamos con la espada.
Normalmente habría que crear un objeto "mano", un objeto "brazo", un objeto "pestaña"...

Con este sistema simplemente creamos el objeto "troll", así:
object Troll "Troll" limbo
with name 'troll' 'planseldon',
adjectives 'mano' 'manos' 'brazo' 'brazos' 'pierna' 'piernas' 'derecha' 'izquierda' 'pestaña',
before[;
Attack: print "Golpeas al troll en ";
if(FlagOn (50))"una mano.";
if(FlagOn (51))"un brazo.";
if(FlagOn (52))"una pierna";
!etc
"una parte al azar de su cuerpo.";

],
has animate scenery talkable;


Nuevamente tendríamos que mirar la lista para ver qué activa cada flag. Pero en este caso, del propio código se deduce:
Flag 50: mano*, dedo*
Flag 51: brazo*, codo*
Flag 52: pie/, pies/, pierna*
!etc

Resúmen de lo que hemos hecho:
  1. Un único objeto
  2. Introducimos los nombres de todos los blancos o subelementos en la propiedad 'adjectives' para que el parser trague con ellos (recordemos cómo funciona Inform, el parseado en paralelo "detectacascajos" actúa de apoyo, pero no sustituye) (*** ver NOTA1 al final)
  3. En el código del objeto consultamos las pesquisas que ha realizado el parseado en paralelo de detección de cadenas, a ver si "ha pescado algo". De ser así, podemos programar fácimente una respuesta personalizada, y si no, dejamos que salte la respuesta estandar.

*** NOTA1:
El código del Troll arriba expuesto sólo es viable-razonable en un modo de parseado tal que la propiedad adjectives no tenga la misma jerarquía que la propiedad 'name' sino que sea dependiente. ('adjectives' sólo se consulta si antes ha habido coincidencia en la propiedad 'name')
En caso contrario, el código expuesto es inviable, pues estaría abierto a disparates tales como escribir "ex pestaña" y recibir la descripción del troll".

***NOTA2:
El código para realizar este parseado "bruto" tipo PAWS en paralelo está expuesto AQUÍ, aunque en la versión "poco óptima", usando unas pocas variables en lugar de millones y millones de Flags.