La guida a Glulx Inform per autori di avventure testuali
Inform versione 6.21(G0.35)
http://www.eblong.com/zarf/glulxe/
traduzione di riktik
http://ifitalia.oldgamesitalia.net
http://www.riktik.com
Nota di traduzione: se ravvisi inesattezze nella traduzione di questo documento
contattami all'indirizzo
[email protected]
*** A cosa serve questa guida?
Se siete autori di avventure scritte con Inform, o possedete un poco di
familiarita con questo linguaggio, e volete addentrarvi nel mondo di Glulx,
allora il vstro primo passo dovrebbe essere leggere questo documento.
Probabilmente non e il *solo* documento che dovreste leggere. Essendo questo,
infatti, solo un tentativo di spiegare in poche parole la grafica, i suoni,
o qualsiasi altro trucchetto reso possibile dalle Glk.
Questa guida, in particolare, prova a rispondere alla prima domanda che potreste
farvi: "Ho un gioco in Inform, e voglio compilarlo in Glulx invece che per la
Z-machine. Cosa devo fare?"
E, come bonus, toccheremo anche la risposta alla vostra seconda domanda
"Quali trucchetti in piu sono disponibili usando Glulx Inform al posto della Z-machine?"
Ma sara solo una toccata e fuga, uomo avvisato...
Alcuni autori troveranno in questo documento tutto cio di cui hanno bisogno.
Se il vostro unico desiderio e scrivere un gioco in inform troppo corposo per una Z-machine,
vi bastera leggere questa guida, compilare il codice in Glulx, e distribuire il gioco.
* Cosa e questa roba chiamata Glulx?
La spiegazione lunga, con ragionamenti, storia, aneddoti e giustificazioni
filosofiche e disponibile sulla pagina web di Glulx (si veda URL in alto).
Per dirla in poche parole, e una virtual machine. Esattamente allo stesso
modo in cui la Z-machine e una virtual-machine. Potete scrivere un gioco in
Inform, compilarlo per la Z-machine, e lanciarlo su un interprete per
Z-machine; oppure compilarlo per Glulx e lanciarlo su un interprete Glulx.
La differenza e che la Z-machine ha molte limitazioni nella sua progettazione
che Glulx non ha, o meglio, ha in misura inferiore.
* Allora e proprio semplice, vero?
Il compilatore Glulx Inform e progettato per compilare lo stesso linguaggio
standard di Inform. Se il vstro codice sorgente e compilato correttamente sotto
Inform 6.21, allora dovrebbe essere tranquillamente compilabile in Glulx Inform,
senza quasi nessun cambiamento.
La parte restante di questo documento e dedicata proprio alle differnze tra
"quasi nessun cambianto" e "nessun cambiamento".
*** Cio che Qualsiasi Autore di Giochi Dovrebbe Sapere
* Introduzione
Per cominciare a scrivere un gioco in Glulx Inform, si ha bisogno di:
Il compilatore Glulx Inform (o bipiattaforma)
La libreria bipiattaforma Inform
La libreria italiana adattata a glulx
Un interprete Glulx per il vostro computer
Ossia la stessa quantita di cose che servirebbero per scrivere un gioco
in Z-code -- compilatore, librerie, interprete Z-machine.
Il compilatore e scaricabile dalla pagina web di Glulx. Funziona esattamente
allo stesso modo del compilatore originale di Inform (Inform Z-Code). L'unica
differenza e il tipo di file che genera.
(Al momento, ho disponibili due compilatori. Uno genera solo file Glulx;
l'altro puo generare sia file Glulx che Z-code, a seconda che si usi il
parametro -G o meno. E' pertanto preferibile il compilatore bipiattaforma.)
La libreria bipiattaforma e anch'essa disponibile sulla mia pagina web. E'
una versione bipiattaforma della versione 6/10 della libreria di Graham
-- cosi, puo essere usata per compilare sia in Z-code, sia in Glulx.
In Z-code essa funziona esattamente come la libreria 6/10. In Glulx, essa compie
lo stesso lavoro, ma diversi pezzi di codice sono implementati differentemente, tenendo conto
delle differenza tra le due VM.
Anche l'interprete lo trovate sempre li. Potete scaricarvi il codice sorgente,
o anche gli eseguibili per Mac, DOS e Windows. (Se scaricate il sorgente,
avrete bisogno per compilarlo delle librerie Glk. Se invece optate per gli eseguibili,
le librerie Glk sono gia inserite al loro interno.)
Al momento (agosto 2003) non sono state distribuite librerie italiane ufficiali per
Glulx. Tommaso Caldarola
http://digilander.libero.it/tommyzombie/atschizo/schizo.html
ha pero adattato le librerie di Giovanni Riccardi
http://www.composizioni.net/inform/
alla nuova virtual machine.
Scrivere un gioco con questi strumenti e quasi lo stessa cosa che scriverne uno
con gli strumenti di Inform in Z-code. Naturalmente bisogna fare attenzione
ad alcuni dettagli:
Se si vuole che il gioco rimanga (ancora) compatible con il compilatore
di Inform per Z-code di Graham, si dovra inserire un piccolo blocco di
codice di compatibilita all'inizio del programma.
Se si prende la lunghezza di una proprieta, usando l'espressione
(obj.#prop)/2, si dovrebbe cambiarla con (obj.#prop)/WORDSIZE.
Se si scrive una propria procedura DrawStatusLine(), usando l'assembly
Z-code, si dovra riscriverla usando le opportune chiamate alla libreria Glk.
(Vedi la libreria bipiattaforma per il codice di esempio.)
Se si usano le istruzioni "style", si dovra riscriverle usando una chiamata Glk.
Alcune funzioni nuove della libreria sono disponibili, ma solo
quando si compila per Glulx.
La seguente sezione le spiega piu dettagliatamente. Siete liberi di saltare
qualsiasi cosa di cui non abbiate bisogno. (il file sorgente Advent.inf
e compilabile in Glulx senza alcun cambiamento.)
* Scrivere codice bipiattaforma
L'ideale, naturalmente, e scrivere un codice sorgente di Inform che possa
essere compilato su entrambe le piattaforme. Non tutti potrebbero volerlo
fare -- se il vostro gioco e troppo grande per girare su una Z-machine, non vi
e ragione di rendere il codice sorgente bipiattaforma. Ma, in nome della chiarezza,
questo documento assumera che si intende realizzare un gioco per entrambe le VM.
Il compilatore predefinisce due costanti che sono piuttosto utili.
Quando si compila per la Z-machine, esso (effettivamente) definisce:
Constant TARGET_ZCODE;
Constant WORDSIZE 2;
Quando si compila per Glulx, invece definisce:
Constant TARGET_GLULX;
Constant WORDSIZE 4;
Cio significa che si puo usare #ifdef TARGET_ZCODE o #ifdef TARGET_GLULX
per definire pezzi di codice che sono appropriati solo per una singola piattaforma.
(Le librerie adattate di Inform usano ripetutamente questa strategia).
(La costante WORDSIZE definisce il numero di byte in una parola. Vedi
la prossima sezione.)
C'e un inconveniente temporaneo: il compilatore per Inform 6.21 realizzato da Graham
(per Z-code) al momento non definisce ancora queste costanti. Future versioni probabilmente lo
faranno. Nel frattempo, per far uso delle librerie 6/10 adattate con il compilatore di Graham,
si deve inserire il seguente codice all'inizio del sorgente (prima di qualsiasi istruzione Include):
! Istruzioni necessarie per compilare con l'attuale compilatore Inform 6.21 di Graham.
#ifndef WORDSIZE
Constant TARGET_ZCODE;
Constant WORDSIZE 2;
#endif;
Questo codice *non* e necessario quando si compila un gioco Z-code con il
mio compilatore bipiattaforma. Il compilatore bipiattaforma definisce WORDSIZE
e l'appropriata costante TARGET_, in entrambe le piattaforme.
* Grandezza delle parole
La principale differenza tra Glulx e la Z-machine e che le parole sono
lunghe quattro byte invece che due. Tutte le variabili sono valori a 32-bit,
lo stack contiene valori a 32-bit, le proprieta sono valori a 32-bit. E cosi via.
Nella maggior parte della programmazione in Inform, non ci si deve preoccupare
di questo cambiamento. Per esempio, se si ha una array
Array list --> 10;
---lo Z-code Inform alloca 10 parole --ossia, venti bytes -- e si puo
accedere a questi valori come list-->0 fino a list-->9. Se si compila
lo stesso codice in Glulx Inform, il compilatore allochera ancora una volta
10 parole -- quaranta byte -- e si potra ancora accedere ai valori come
list-->0 fino a list-->9. Ogni cosa funzionera alla stessa maniera, con
l'eccezione che i variabili potranno contenere valori piu grandi di 65535.
La tabella degli array riporta anche un valore di quattro-byte, e la prima
parola di quattro-byte e la lunghezza dell'array. Stringhe e -> array, e la -> notation,
si rifersiscono ancora a singoli byte. Non vi dovrebbe essere alcuna necessita di modificare
il proprio codice qui.
Vi e un caso importante in cui *dovrete* modificare il vostro codice:
l'operatore .#. L'espressione obj.#prop restituisce la lunghezza della proprieta in byte.
Dal momento che la maggior parte delle proprieta contiene parole, piuttosto che byte,
e spesso comune avere un codice di questo tipo:
len = (obj.#prop) / 2;
for (i=0 : i<len : i++)
print (string) (obj.&prop)-->i;
Nei programmi Inform Glulx, e necessario dividere per 4 invece che per 2.
Per rendere le cose piu leggibili, si puo usare la costante WORDSIZE.
Cosi potreste rimpiazzare il codice precedente con questo:
len = (obj.#prop) / WORDSIZE;
for (i=0 : i<len : i++)
print (string) (obj.&prop)-->i;
Che sara compilabile correttamente per entrambe le VM.
In generale, dovreste cercare nel vostro codice l'operatore
# ; ogni volta che lo incontrate, trovate il "2" e sostituitelo con "WORDSIZE".
* Istruzioni mancanti
Alcune istruzioni di Inform sono specifiche per la Z-machine. Nel codice Glulx,
esse dovrebbero essere sostituite con assebly Glulx o funzioni della libreria Glk. Queste
istruzioni nella maggior parte dei giochi non sono usate; esse vengono utilizzate per lo piu
nelle librerie, la libreria 6/10 adattata (bipiattaforma) le modifica correttamente.
Se provate ad usare una qualsiasi di queste istruzioni in Glulx, otterrete
un errore di compilazione.
Style: questa istruzione dovrebbe essere sostituita con la funzione delle Glk
per cambiare l'aspetto dello stile. Vedi la sezione "Stili di testo".
E' possibile che in future versioni del compilatore Glulx queste istruzioni
siano implementate con "best-guess Glk style calls". Comunque,
Glk fornisce piu possibilita rispetto ai soliti grassetto/corsivo/altezza-fissa
bold/italic/fixed-width imposte dalla Z-machine. Vale quindi la pena inserire esplicite
chiamate Glk.
save, restore: Sono precedure piu complicate in Glulx rispetto allo Z-code.
Non possono essere implementate senza utilizzare variabili e funzioni della
libreria. Se volete qualcosa del genere, modificate o copiate le routine della
libreria SaveSub e RestoreSub.
read: Allo stesso modo, leggere una linea di testo in Glulx coinvolge
la libreria; il compilatore non puo generare da solo codice indipendente
per farlo. Vedi la routine KeyboardPrimitive della libreria.
* Thingie References
Nella Z-machine, le strighe e le funzioni sono indirizzi "concentrati";
le parole del dizionario sono indirizzi normali; e gli oggetti del gioco
sono rappresentati come numeri in sequenza dall'1 fino al #ultimo_oggetto.
Questi range si sovrappongono. Pertanto una stringa, una parola del dizionario ed
un oggetto potrebbero essere rappresentati dallo stesso valore.
In Glulx, invece, sono tutti rappresentati da indirizzi normali.
Pertanto due cose differenti avranno sempre valori differenti. In piu,
il primo byte trovato nell'indirizzo e un valore identificativo, che
dice quale tipo di contenuto e rappresentato dall'indirizzo:
E0...FF: Stringhe.
E0: Non codificato, null-terminated strings.
E1: Stringhe compresse.
E2...FF: Riservato per future espansioni dei modelli di stringhe.
C0...DF: Funzioni.
C0: Stack-argument functions.
C1: Local-argument functions.
C2...DF: Riservato per future espansioni del modello delle funzioni.
80...BF: Riservato per usi futuri della VM.
40...7F: Riservato per l'uso della libreria di Inform.
70: Oggetti (e classi).
60: Parole del dizionario.
01...3F: Disponibile per l'uso dal codice del gioco.
00: Riservato per "nessun oggetto".
Ora che avete visto questo sistema, potete dimenticarlo. Le funzioni metaclass e
ZRegion prevedono il tipo di oggetto, e restituiscono gli stessi valori
come sotto lo Z-code Inform.
Un piccolo dettaglio: nello Z-code Inform, si puo stampare una stringa compressa
da quasiasi parte della RAM con la linea:
print (address) word;
Diciannove o venti timeout, cio e usato per stampare le parole del
dizionario. (Il valore di una parola del dizionario e un indirizzo RAM,
e una parola del dizionario Z-Code inizia con una stringa compressa.)
Questo si puo ancora fare in Glulx Inform, ma *solo* per stampare
parole del dizionario. (Le parole del dizionario in Glulx Inform non
sono compresse, e iniziano con il byte 60 invece che con E0 o E1.)
Se si vuole stampare una stringa generica, si usi il metodo abituale,
ossia print (string).
Un altro dettaglio: dal momento che gli oggetti non sono piu sequenze di
integer, non si possono scrivere cicli come
for (obj=rock : obj<rock+16 : obj++)
move obj to Cavern;
Questo tipo di codice e comunque sconsigliato. Si consiglia di usare invece un objectloop.
* Stili di testo
La Z-machine ha cinque stili disponibili: normal, bold, italic/underlined,
reverse, e fixed-width. E le possibili combinazioni tra gli stessi (sebbene
non siano realmente degli standard).
I programmi Glulx, attraverso le Glk, possono usufruire di una lista piu
nutrita di stili: Normal, Emphasized, Preformatted, Header, Subheader, Alert,
Note, BlockQuote, Input, User1, User2.
Inoltre, dal momento che queste sono etichette semantiche piuttosto che
comandi di formattazione, il giocatore puo aggiustare la sua libreria per
visualizzarle come preferisce. (Okay, con l'eccezione del Preformatted. Che e
sempre usato per l'altezza fissa del testo.)
(Vedi la prossima sezione per informazioni su come eseguire le funzioni Glk
nel tuo gioco Inform.)
Le librerie bipiattaforma si avvantaggiano di queste possibilita. Il titolo
del gioco e visualizzato nello stile Header; i nomi delle locazioni sono
visualizzati con il Subheader; i box quotati usano il BlockQuote; i cambiamenti
del punteggio sono nello stile Note: ed il messaggio "Sei morto!" usa lo stile
Alert.
Quando eseguite cambiamenti di stile nel gioco, dovreste fare attenzione
ad usare un stile appropriato. Non presupponete che tutti apprezzino lo stesso stile.
il testo che si vuole enfatizzare dovrebbe usare lo stile Emphasized, ma gli utenti
potrebbero decidere di impostarlo per essere visualizzato in grassetto o in corsivo
o sottolineato (o forse di un colore rosso acceso.) Lo stile Alert puo essere grassetto
sul vostro interprete, ma cio non significa che dovreste usarlo per stampare testo
in grassetto in un messaggio; il mio interprete potrebbe visualizzarlo con una scritta
lampeggiante giallo fosforescente, per questo il sintentico all'osso "Sei morto" e da brivido.
Se volete provvedere voi all'apparenza degli stili, usate le funzioni degli
stylehint Glk. User1 e User2 sono disponibili per specifici stili non
coperti da un altro degli stili gia previsti.
Percio l'istruzione Inform "style roman/bold/underline/reverse"
non e disponibile in Glulx Inform.
L'istruzione "font off/on" funziona; "font off" imposta lo stile Preformatted,
e "font on" imposta lo stile Normal. (Ricordate che se siete in un altro stile,
"font off" seguito da "font on" vi riportera in Normal, non nello stile con cui
si e iniziato.)
* Linguaggio Assembly; Le funzioni Glk
L'assembly della Z-machne non funziona nel codice di Glulx. Bisogna
quindi usare l'assembly di Glulx. Cio dovrebbe essere ovvio. Per il codice
bipiattaforma, quindi, e necessario usare i soliti #ifdef per gestire
l'implementazione di entrambri gli assembly.
Molte delle cose che sono solitamente implementate con l'assembly nel
codice Z ora vengono gestite dalle funzioni della Glk. Eseguire una chiamata
alle funzioni della Glk da Inform e un po' stravagante, ma non difficile.
Ogni cosa nelle Glk e gestita dalla funzione, gia presente in Inform, glk(),
tale funzione puo contenere uno o piu argomenti. Il primo argomento e un integer;
che dice *quale* funzione Glk si sta richiamando. I rimanenti argomenti sono semplicemente,
nell'ordine, passati come argomenti alla fuzione Glk.
La tabella delle funzioni Glk, e gli integer che si riferiscono ad esse, sono nelle
specifiche Glk. Si veda la "Table of Selectors" nell'appendice "Dispatch Layer".
Ricordate che i valori li presenti sono esadecimali.
Le costanti Glk (come style_Normal, wintype_TextBuffer, winmethod_Proportional,
e tutte le altre dai nomi lunghissimi descritti nelle specifiche Glk),
sfortunatamente, non sono disponibili in inform. Dovrete quindi andare a vedere
i valori integer nelle intestazione del file glk.h.
Mettiamo, ad esempio, che vogliate impostare lo stile del testo
su "preformatted". Il codice Inform per realizzare tale impostazione e:
glk($0086, 2); ! set_style
Il valore esadecimale $0086 sta per glk_set_style; la costante style_Preformatted
e definita come 2. (E il commento rende l'istruzione un poco piu comprensibile.)
Come altro esempio, ecco qui come potreste implementare una funzione
"premi un tasto per continuare":
#ifdef TARGET_ZCODE;
@read 1 dummy;
#ifnot; ! TARGET_GLULX;
done = false;
glk($00D2, gg_mainwin); ! request_char_event
while (~~done) {
glk($00C0, gg_event); ! select
switch (gg_event-->0) {
5: ! evtype_Arrange
DrawStatusLine();
2: ! evtype_CharInput
if (gg_event-->1 == gg_mainwin)
done = true;
}
}
#endif; ! TARGET_
Notate che la gg_mainwin e una variabile della libreria che contiene il numero identificativo
della finestra, e gg_event e una array di quattro parole che viene usata dalla
libreria per catturare gli eventi Glk. Essa e usata in KeyboardPrimitive,
ma puo essere usata anche a parte.
Infatti, una versione piu simpatica di questo codice e presente nella libreria,
come KeyCharPrimitive(). Si veda dopo.
Come probabilmente avrete notato, non e estremamente facile da leggere.
Se pensate di fare molte chiamate alla Glk, potreste usare la libreria di
Katre "infglk.h". Che definisce le funzioni contenitore e le costanti.
Cosi ad esempio, potreste chiamare
glk_set_style(style_Preformatted);
ed ottenere lo stesso effetto che si ottiene con l'istruzione
glk($0086, 2); ! set_style
La libreria non usa questo trucco, per questione di semplicita, ma voi potete se lo preferite.
* Spieghiamo la DrawStatusLine()
L'ostacolo piu duro per gli autori che decidono di passare da Inform a Glulx
e scrivere una procedura DrawStatusLine().
Nello Z-code Inform, costituisce un gran pezzo di codice assembly, e tutti solitamente
si limitano a riarragiarne uno precedente che funziona -- al massimo dando qualche
sguardo alle specifiche della Z-machine. Sotto Glulx, e un gran pezzo di codice in chiamate
alle funzioni della Glk, ma potete riarrangiarla alla stessa maniera.
Ecco la procedura completa della DrawStatusLine() della libreria bipiattaforma.
(Naturalmente, la libreria contiene due versioni -- Glulx e Z-code -- con
#ifdefs per compilare quella giusta a seconda dei casi.)
[ DrawStatusLine width height posa posb;
! Se non abbiamo una finestra status, non dobbiamo provare a ridisegnarla.
if (gg_statuswin == 0)
return;
! Se il giocatore non e nella locazione, non dobbiamo continuare a provare.
if (location == nothing || parent(player) == nothing)
return;
glk($002F, gg_statuswin); ! set_window
StatusLineHeight(gg_statuswin_size);
glk($0025, gg_statuswin, gg_arguments, gg_arguments+4);
! window_get_size
width = gg_arguments-->0;
height = gg_arguments-->1;
posa = width-26; posb = width-13;
glk($002A, gg_statuswin); ! window_clear
glk($002B, gg_statuswin, 1, 0); ! window_move_cursor
if (location == thedark) {
print (name) location;
}
else {
FindVisibilityLevels();
if (visibility_ceiling == location)
print (name) location;
else
print (The) visibility_ceiling;
}
if (width > 66) {
glk($002B, gg_statuswin, posa-1, 0); ! window_move_cursor
print (string) SCORE__TX, sline1;
glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
print (string) MOVES__TX, sline2;
}
if (width > 53 && width <= 66) {
glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
print sline1, "/", sline2;
}
glk($002F, gg_mainwin); ! set_window
];
Andiamo a spiegare il codice riga per riga.
[ DrawStatusLine width height posa posb;
Una normale funzione in Inform, con quattro variabili locali.
! Se non abbiamo una finestra status, non dobbiamo provare a ridisegnarla.
if (gg_statuswin == 0)
return;
gg_statuswin e la variabile globale della libreria che contiene il riferimento
alle finestra status. Alcune librerie non supportano una finestra status; se non ve ne e una,
gg_statuswin contiene zero. E quindi, non c'e nulla da fare qui e si puo operare un return immediatamente.
! Se il giocatore non e nella locazione, non dobbiamo continuare a provare.
if (location == nothing || parent(player) == nothing)
return;
Questo caso non capitera in molti giochi, ma e meglio prevederlo. (un modo
in cui potrebbe verificarsi e se si pone una domanda YesOrNo() nella procedura
Initialise().)
glk($002F, gg_statuswin); ! set_window
Imposta la corrente finestra di output. Tutto cio che verra stampato
dopo questa istruzione andra nella finestra status.
Ancora una volta, notate la forma generale delle chiamate Glk. Il primo
argomento di glk() dice quale funzione Glk viene richiamata; $002F e glk_set_window().
Segue il resto degli argomenti.
StatusLineHeight(gg_statuswin_size);
StatusLineHeight() e una funzione della libreria che semplicemente imposta la
altezza della status line ad un determinato valore. E' meglio ricordare la grandezza
corrente, e lasciare che la finestra la decida da sola se non si necessita di cambiamenti.
(La variabile globale gg_statuswin_size di default e posta ad 1, e la libreria non la
cambia in nessun punto.)
glk($0025, gg_statuswin, gg_arguments, gg_arguments+4);
! window_get_size
width = gg_arguments-->0;
height = gg_arguments-->1;
Questo codice richiama la glk_window_get_size(), per misurare la grandezza
della finestra status. Gli ultimi due argomenti dovrebbero essere indirizzi nella memoria;
La larghezza e la altezza sono scritte li.
gg_arguments e una array della libreria disponibile solo a questo scopo.
E' lunga otto parole, ma in questo caso abbiamo bisogno solo delle due prime posizioni.
(La seconda posizione e data come gg_arguments+4, poiche una parola e lunga quattro
byte in Glulx Inform.)
Una volta che la chiamata e stata fatta, metteremo i due valori in due variabili locali.
posa = width-26; posb = width-13;
La posizione orrizzontale degli indicatori del punteggio e del conto dei turni di gioco.
Questa linea appare sia nello Z-code che nelle versioni Glulx della DrawStatusLine().
glk($002A, gg_statuswin); ! window_clear
Richiama la routine glk_window_clear() per pulire la finestra corrente.
glk($002B, gg_statuswin, 1, 0); ! window_move_cursor
Sposta il cursore nella posizione (1,0) -- che corrisponde al secondo
carattere della prima linea. La posizione in alto a sinistra nella finestra
Glk e infatti numerata come (0,0). Il che e differente dalle finestre in
Z-code, dove la posizione in alto a sinistra corrisponde a (1,1). Altra differenza,
solo come importanza, e che la glk_window_move_cursor() richiede due coordinate
nell'ordine (X, Y). Lo Z-code @set_cursor opcode usa invece (Y, X).
if (location == thedark) {
print (name) location;
}
else {
FindVisibilityLevels();
if (visibility_ceiling == location)
print (name) location;
else
print (The) visibility_ceiling;
}
Questo e identico al codice Z.
if (width > 66) {
glk($002B, gg_statuswin, posa-1, 0); ! window_move_cursor
print (string) SCORE__TX, sline1;
glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
print (string) MOVES__TX, sline2;
}
if (width > 53 && width <= 66) {
glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor
print sline1, "/", sline2;
}
Stampa il punteggio e muove. Questo codice funziona allo stesso modo della versione Z-code.
glk($002F, gg_mainwin); ! set_window
];
Imposta la finestra corrente alla precedente nella story window, e return.
La sola differenza che potreste notare e che la versione Z-code verifica
((0->1)&2 == 0). Questo e un piccolo header della Z-machine, che nei giochi
V3 e usato per dire all'interprete di disegnare una status line con il "Tempo"
invece che con il "Puteggio / Turni". La routine DrawStatusLine() Z-code emula
questo comportamento.
Naturalmente i giochi V3 sono rari, e Glulx non ha alcun piccolo header.
Cosi la versione Glulx delle routine tralascia completamente questa idea.
Se volete mostrare il tempo, rimpiazzate la DrawStatusLine e
scrivete il vostro codice.
Le spiegazioni qui esposte dovrebbero bastare a iniziare.
Ancora una volta, se non volete perder tempo il poco tempo che rimane della vostra
breve vita andando a cercare le costanti nelle tabelle, potete usare le librerie "infglk.h".
* Se si scrive una estensione alla Libreria di Inform
Non tutti gli autori di giochi vorranno scrivere codice sorgente in Inform
tenendo conto di entrambre le piattaforme. Molti saranno interessati a creare un
gioco in Glulx, e imposteranno il loro codice unicamente per esso.
In ogni caso, una libreria supplementare *dovrebbe* essere bipiattaforma, poiche
potrebbe essere usata in giochi che sfruttano sia lo Z-code che Glulx.
Potrebbe anche essere usata sia con il compilatore di Graham (solo per Z-code),
sia con i miei compilatori. Quindi, dovreste probabilmente includere una hack
di portabilita all'inizio del sorgente della libreria:
! Cio e necessario per compilare con l'attuale compilatore Inform 6.21 di Graham.
#ifndef WORDSIZE
Constant TARGET_ZCODE;
Constant WORDSIZE 2;
#endif;
Dopo di che si puo usare WORDSIZE nel proprio codice, ed inserire le codizioni #ifdef
TARGET_... , se necessario.
Nota: usate questo pezzo di codice esattamente come e dato. Non tralasciate la linea
TARGET_ZCODE , anche se la libreria supplementare non ne fa uso. La spiegazione e
lasciata come esercizio.
*** Cose disponibili solo in Glulx
* Nuove funzioni della Libreria
KeyCharPrimitive();
Questa routine aspetta la pressione di un singolo tasto, e restituisce
il carattere premuto (da 0 a 255, o uno dei codici chiave speciali delle Glk).
Potete anche chiamare questa routine usando uno o due argomenti, come in questo caso:
KeyCharPrimitive(win, nostat);
Se win e diverso da zero, il carattere immesso va alla finestra Glk specificata
(invece di gg_mainwin, finestra di default.) Se nostat e diverso da zero l'evento
riarrangiamento della finestra e restituito immediatamente come valore 80000000
(invece del comportamento di default, che consiste nel chiamare la DrawStatusLine()
ed aspettare.)
StatusLineHeight();
Questa routine cambia l'altezza della status line, come potreste aspettarvi.
La routine standard DrawStatusLine() la richiama ad ogni turno, il che non e un male,
dal momento che la StatusLineHeight() e piuttosto utile. Se si rimpiazza la DrawStatusLine(),
conviene mantenere questa convenzione. (Le routine del menu della libreria sono in sintonia con la
status line, e e dato alla DrawStatusLine() il compito di resettarle dopo che il menu a terminato
il suo lavoro.)
DecimalNumber(num);
Questa stampa num come numero decimale. E' di fatto identica a:
print num;
..ed e cosi che viene implementata. Ma potrebbe essere utile unitamente alle
seguenti funzioni:
PrintAnything(obj, ...);
Questa stampera ogni cosa conosciuta dalla libreria. Gestisce stringhe,
funzioni (con argomenti opzionali), oggetti, proprieta degli oggetti (con argomenti
opzionali), e parole del dizionario.
Chiamare: E' equivalente a:
------- ----------------
PrintAnything() <nothing printed>
PrintAnything(0) <nothing printed>
PrintAnything("string"); print (string) "string";
PrintAnything('word') print (address) 'word';
PrintAnything(obj) print (name) obj;
PrintAnything(obj, prop) obj.prop();
PrintAnything(obj, prop, args...) obj.prop(args...);
PrintAnything(func) func();
PrintAnything(func, args...) func(args...);
Argomenti extra dopo una stringa o una parola del dizionario sono
sicuramente ignorati.
Il (primo) argomento che si passa alla routine e sempre interpretato come a riferimento
ad una cosa, non come un integer. Cio spiega il perche nessuna dell forme precedentemente
mostrate stampa una integer. Naturalmente, si puo ottenere lo stesso effetto chiamando
PrintAnything(DecimalNumber, num);
..che e il momento in cui torna utile la funzione DecimalNumber(). Si puo
anche, naturalmente, usare altre funzioni della libreria, e fare trucchi tipo
PrintAnything(EnglishNumber, num);
PrintAnything(DefArt, obj);
Nessuno di questi sembra molto utile. Dopo tutto, ci sono gia dei modi per
stampare tutte queste cose. Ma PrintAnything() e fondamentale per implementare
la seguente funzione:
PrintAnyToArray(array, arraylen, obj, ...);
Questa funzione lavora allo stesso modo, con l'eccezione che invece di stampare a schermo,
l'output e trasferito all'array nominata.
I primi due argomenti devono essere l'indirizzo della array e la sua massima
lunghezza. Fino ad essa i caratteri verranno scritti nell'array; quasiasi
extra sara silenziosamente scartato. Cio significa che non dovrete preoccuparvi
di eventuali sovrascritture nell'array.
La funzione PrintAnyToArray() restituisce il numero di caratteri generati.
(Che potrebbero essere di piu della lunghezza dell'array. Rappresenta l'intero
testo che viene dato in output, non il numero limite scritto nell'array.)
E' piu sicuro annidare le fuzioni PrintAnyToArray(). Cioe, potete richiamare
PrintAnyToArray(func), dove func() e una funzione che richiama a sua volta
PrintAnyToArray(). (Naturalmente, se entrambe dovessero cercare di scrivere
la *stessa array*, il caos sarebbe assicurato.)
E' consentito che arraylen sia pari a zero (nel qual caso l'array e ignorata,
e puo essere zero tranquillamente.) Cio scarta *tutto* l'output, e semplicemente
restituisce il numero di caratteri generati. Potete usare questo artifizio
per trovare la lunghezza di qualsiasi cosa -- anche di una chiamata ad una funzione.
* Nuovi Entry Points
Un entry point e una funzione che si puo inserire o meno nel proprio codice;
la libreria la richiamera solo se e presente, e la ignorera nel caso
opposto. (Vedi l'inform DM)
La libreria bipiattaforma possiede alcuni entry point che aiutano l'autore
nella scrittura di interfacce piu complicate del solito -- giochi con suoni,
disegni, finestre supplementari, ed altri simpatici trucchetti offerti
dalla Glk. Se state scrivendo solo un gioco in stile Infocom standard,
potete ignorare questa sezione.
HandleGlkEvent(ev, context, abortres)
Questa entry point e chiamata ogni volta che accade un evento Glk.
L'evento potrebbe indicare qualunque cosa, una linea di input dal
giocatore, il ridimensionamento di una finestra o il ridisegno di una immagine,
un rintocco dell'orologio, un click del mouse e cosi via.
La libreria gestisce tutti questi eventi necessari per un normale gioco
in stile Infocom. E' necessario prevedere una funzione solo se si vuole
aggiungere qualche funzionalita extra.
L'argomento ev e una array di quattro parole che descrive l'evento.
ev-->0 e il tipo di evento; ev-->1 e la finestra coinvolta (se rilevante);
ed ev-->2 e ev-->3 sono informazioni extra. L'argomento context e 0 se
l'evento accade durante la linea di input (comandi normali, YesOrNo(),
o qualche altro uso della funzione della libreria KeyboardPrimitive();
1 indica che l'evento e accaduto durante la pressione di un carattere input
(quasiasi uso della funzione della libreria KeyCharPrimitive().
L'argomento abortres e usato solo se si vuole cancellare l'input del
giocatore e forzare un particolare risultato; si veda sotto.
Se restituite 2 dal HandleGlkEvent(), l'input del giocatore sara immediatamente
abortito. E' richiesto un po' di codice in piu:
* Se l'evento e un input carattere (context == 1), dovete richiamare la
funzione Glk cancel_char_event function, e quindi impostare abortres-->0
al carattere che volete sia restituito. Quindi restituite 2; KeyCharPrimitive()
finira e restituira il carattere, come se il giocatore lo avesse premuto.
* Se l'evento e una linea di input (context == 0), dovete richiamare la funzione Glk
cancel_line_event. (Potete passare un argomento all'array per controllare quando il
giocatore ha premuto il tasto.) Quindi, inserire la lunghezza dell'input perche restituisca
abortres-->0. Se e diversa da zero, scrivere il carattere input in sequenza nell'array di partenza
abortres->WORDSIZE, sino a (ma non includendolo) abortres->(WORDSIZE+len).
Non eccedete i 256 caratteri. Quindi restituite 2; KeyboardPrimitive() terminera e restituira la linea.
Se restituite -1 dalla funzione HandleGlkEvent(), l'input del giocatore continuera anche dopo
aver premuto un tasto (per l'input carattere) o dopo il tasto invio (per l'input linea).
(Non so se e utile, ma potrebbe esserlo.) You
must re-request input by calling request_char_input or request_line_input.
Ogni altro valore restituito dalla funzione HandleGlkEvent()
(una normale restituzione, rfalse, o rtrue) sicuramente non condizionera l'input del giocatore.
InitGlkWindow(winrock)
Questa entry point e richiamta dalla libreria quando imposta la finestra standard:
la finestra story, la finestra status, e (se usate caselle in evidenza) altre finestre
in evidenza. Le prime due finestre sono create alla partenza del gioco
(prima della funzione Initialise()). Il terzo tipo di finestre sono create e distrutte
quando e necessario.
E' chiamata in cinque phases:
* La libreria richiama InitGlkWindow(0). Che sopravviene proprio
all'inizio dell'esecuzione, anche prima della routine Initialise().
Potete impostare qualsiasi situazione che vogliate. (naturalmente, ricordate
che le finestre story e status potrebbero gia esistere -- ad esempio, se
il giocatore ha semplicemente dato il comando "ricomincia".) Questo e un buon momento
per impostare gg_statuswin_size ad un valore diverso da 1. Restituire 0 per procedere
con l'impostazione standard delle finestre della libreria, o 1 se avete creato
per conto vostro tutte le finestre.
* La libreria richiama InitGlkWindow(GG_MAINWIN_ROCK), prima di creare la finestra story.
Questo e un buon momento per impostare gli stili di testo per la finestra story.
Restituite 0 per lasciare che la libreria crei la finestra; restituite 1 se
avete creato per conto vostro una finestra e l'avete posta in gg_mainwin.
* La libreria richiama InitGlkWindow(GG_STATUSWIN_ROCK), prima di creare la finestra
status. Ancora una volta, restituite 0 per lasciare che la libreria lo faccia per voi;
restituite 1 se avete creato una vostra finestra e l'avete posta in gg_statuswin.
* La libreria richiama InitGlkWindow(1). Che e la fine dell'impostazione delle finestre;
Potete cogliere questa opportunita per aprire altre finestre. (Altrimenti potete farlo
nella vostra routine initialise(). Non cambia molto.)
* La libreria richiama InitGlkWindow(GG_QUOTEWIN_ROCK), prima di creare la finestra
quote box. Cio non accade durante l'inizializzazione del gioco; la finestra quote box
e creata durante il gioco, quando stampate un quote, e distrutta al turno successivo.
Come al solito, restituite 1 per indicare che avete creato una finestra in gg_quotewin.
(Il numero desiderato di linee per la finestra puo essere trovato in gg_arguments-->0.)
In ogni modo gestiate l'inizializzazione delle finestre, ricordate che la libreria
richiede una gg_mainwin. Se non ne create una, e non lasciate che la libreria
lo faccia per voi, il gioco verra terminato. Viceversa, la finestra status e quote
sono opzionali; la libreria puo tranquillamente funzionare senza di esse.
IdentifyGlkObject(phase, type, ref, rock)
Questa entry point e richiamate dalla libreria per lasciare che sappiate quale
oggetto Glk esiste. Dovrete inserire questa funzione nel caso in cui decidiate
di creare una qualsiasi finestra, filerefs, file stream, o canale sonoro oltre quelli
standard della libreria.
(Cio si rende necessario dal momento che dopo un comando di caricamento, di reinizio o di undo
le vostre variabili globali che contengono gli oggetti Glk potrebbero essere inesatte.)
Questa viene richiamata in tre phase:
* La libreria richiama la funzione IdentifyGlkObject() con phase==0. Qui dovreste
impostare tutti i riferimenti agli oggetti Glk a zero.
* La libreria richiama la funzione IdentifyGlkObject() con phase==1. Sopravviene
una volta per ogni finestra, stream, e fileref che la libreria non riconosce.
(La libreria gestisce le due finestre standard, e i file e gli stream che hanno a che
fare con i comandi di salvataggio, copia e registrazione. Dovrete quindi unicamente
combattere con gli oggetti che create.) Dovreste impostare qualsiasi riferimento
in modo appropriato all'oggetto. Per ogni oggetto: il tipo sara 0, 1, 2 per finestre, stream,
fileref rispettivamente; ref sara il riferimento all'oggetto; e rock
sara la pietra dell'oggetto, dalla quale potrete riconoscierlo.
* La libreria richiama la funzione IdentifyGlkObject() con phase==2. Che sopravviene una volta,
dopo tutte le altre funzioni, e che vi da la possibilita di riconoscere
gli oggetti che non sono ne finestre, ne stream ne filerefs. Se non create nessun
altro oggetto, potreste ignorarla. Dovreste pero cogliere l'occasione
di aggiornare tutti gli oggetti Glk allo stato del gioco che fosse cominciato o caricato.
(Per esempio, ridisegnare le finestre, o impostare la giusta musica di sottofondo.)
* Stack-Argument Functions
Di default, gli argomenti delle funzioni operano in Glulx allo stesso modo
che in Z-code. Quando chiamate una funzione, gli argomenti che passate sono
scritti nelle variabili locali della funzione stessa, in ordine. Se passate
troppi argomenti, quelli in piu saranno scartati; se ne passate pochi,
le variabili locali non usate assumeranno valore zero.
In ogni caso, la VM Glulx supporta un secondo tipo di funzioni. Potete definire
una funzione di questo tipo dando "_vararg_count" come nome del primo argomento
della funzione. Ad esempio:
[ StackFunc _vararg_count ix pos len;
! ...code...
];
Se fate questo, gli argomenti della funzione *non* verranno scritti nelle
variabili locali. Essi saranno inseriti nello stack; dovrete usare l'assembly di
Glulx per tirarli fuori. Tutte le variabili locali saranno inizializzate a zero,
con l'eccezione di _vararg_count, che (come potreste immaginare) contiene il numero
di argomenti che vengono passati.
Notate che _vararg_count e una variabile locale normale, all'infuori del suo valore iniziale.
Potrete assegnargli un valore, incrementarla o decrementarla, usarla come espressione, e cosi via.
Le funzioni stack-argument sono molto utili se si vuole una funzione
con argomenti variabili, o se volete scrivere una funzione contenitore che
passa i suoi argomenti ad una ulteriore funzione.
(I lettori piu attenti potrebbero notare che questa non e esattamente
la struttura che le specifiche della Glulx VM descrive.
Una funzione C0-type comincia con un valore extra sullo stack, che specifica
il numero di argomenti che lo seguono sullo stack. Naturalmente, cio e nascosto
ai vostri occhi dal compilatore. Quando scrivete una funzione _vararg_count,
il compilatore genera automaticamente il codice per far uscire questo valore dallo
stack e inserirlo nella variabile locale _vararg_count.)
* Lunghezza limitata print_to_array
La metaclass String di Inform ha un metodo interno print_to_array.
str = "Questa e una stringa.";
len = str.print_to_array(arr);
..scrivera il contenuto della stringa nell'array, a cominciare dal byte 2.
La prima parola (arr-->0) conterra il numero di caratteri scritti, come fara len.
Cio funziona alla stessa maniera in Glulx Inform, con la solita eccezione che la
stringa comincia a 4 byte, cosi che c'e una locazione per i quattro-byte arr-->0.
Comunque, esiste anche una forma estesa in Glulx.
len = str.print_to_array(arr, 80);
..scrivera non piu di 76 caratteri nell'array. Se arr e una array di
80-byte, potrete star sicuri che essa non sara sovrascritta. (Non provatela
con il secondo argomento inferiore a 4.)
Il valore scritto in arr-->0, e il valore restituito, *non* sono
limitati al numero di caratteri scritti. essi rappresentano il numero
di caratteri nella stringa completa. Cio significa che
len = str.print_to_array(arr, 4);
..e un lungo ma perfettamente valido modo di trovare la lunghezza di una stringa.
(e in questo caso, arr ha bisogno di essere lungo 4 byte.)
* Migliori variabili di stampa
Z-code Inform supporta 32 variabili di stampa, @00 to @31, che si possono includere
in una stringa e quindi impostare con una istruzione.
string num "value";
In Glulx, questo limite e stato elevato a 64. (Eventualmente, il limite potrebbe essere oggetto
essere una opzione di linea di comando di inform. Potrebbe, infatti, essere facilmente elevato
a 100, o anche di piu se la sintassi delle stringhe di inform venisse cambiata
in modo da permettere tre digitazioni dopo il segno @.)
Inoltre, in Glulx potete impostare queste variabili per ogni stringa o
valore di funzione. Se usate una funzione, la funzione dovra essere chiamata
senza argomenti e il risultato sara scartato; dovreste percio stampare l'output desiderato
all'interno della funzione. Un valore stringa sara semplicemente stampato.
In Glulx, diversamente dallo Z-code, una variabile stringa stampabile
puo contenere al suo interno codici @.., permettendo la ricorsione. Potete
annidarli tanto profondamente quanto volete. Naturalmente e una cattiva idea
causare una ricorsione infinita. Ad esempio,
string 3 "This is a @03!";
print "What is @03?";
..provochera certamente un crash dell'interprete.
*** Trucchi di cui probabilmente non avrete bisogno
Se volete definire una vostra classe di oggetti, potete farlo. (Solitamente
non e necessario - e piu facile rappresentare le proprie carabattole come oggetti
di inform -- ma e possibile.) Definite un qualsiasi blocco di memoria il cui
primo byte e in un range 01 to 3F. Poi passate il suo indirizzo
come ogni altro riferimento. Le funzioni della libreria come metaclass
e PrintAnything non lo capiranno, ma se prendete un riferimento,
potete con una certa attendibilita dire la differenza tra le tue cose e
le cose della libreria esaminando il primo byte.