Capitolo 4: Riepiloghiamo le basi ================================= G was a gamester, who had but ill-luck; H was a hunter, and hunted a buck. La progettazione del nostro primo gioco nel precedente capitolo ha introdotto moltissimi concetti di inform, spesso senza fornirti pero' molti dettagli di cio' che stava accadendo. Andiamo quindi a rivedere alcune delle cose che abbiamo imparato, cercando di presentarle in maniera piu' elegante. Parleremo a pagina .. di "Costanti e Variabili", a pagina .. di "Definizione degli Oggetti", a pagina .. di "Relazioni tra Oggetti - lÂ’albero degli oggetti", a pagina .. del "Testo quotato" e a pagina .. parleremo di "Routine e Istruzioni". Constanti e variabili ********************* Superficialmente possono apparire simili, ma in realta' sono due argomenti assai differenti. *** Costanti Una constante e' un nome al quale viene assegnato un valore una ed una volta soltanto, non si puo' quindi piu' tardi usare lo stesso nome per un valore differente. Pensa ad essa come ad una lastra di pietra sulla quale incidi un numero: una incisione non puo' essere cancellata, quindi ogni volta che guarderai la pietra leggerai sempre lo stesso numero. Nel nostro esempio abbiamo visto sia una Constant a cui e' stato associate per valore una stringa di caratteri: Constant Story "Heidi"; Sia una costante a cui e' stato associate un numero: Constant MAX_CARRIED 1; Questi due esempi rappresentano i modi piu' comuni in inform per usare le costanti. *** Variabili Una variabile e' un nome a cui viene assegnato un valore, ma tale valore puo' essere cambiato in qualsiasi momento. Pensate ad essa come ad una lavagna sulla quale segnare un numero con il gesso: quando se ne ha bisogno si puo' sempre cancellarlo e riscriverne uno nuovo. Non abbiamo impostato nessuna variabile nel nostro primo gioco, ma ne abbiamo utilizzate un paio create dalla libreria: Global location; Global deadflag; Il valore di una variabile globale creata in questa maniera e' inizialmente pari a 0, ma si puo' cambiare in qualsiasi momento. Per esempio, abbiamo usato lÂ’istruzione: location = davanti_baita; per reimpostare il valore della variabile location nellÂ’oggetto davanti_baita, e abbiamo scritto: if (nido in ramo) deadflag = 2; per reimpostare il valore della variabile deadflag a 2. Piu' tardi parleremo delle variabili locale (vai a "Routine" a pagina 157) e dellÂ’uso delle proprieta' degli oggetto come variabili (vedi "Oggetti" a pagina 155). Definizione degli oggetti ************************* Il concetto piu' importante che dovresti avere appreso dal precedente capitolo e' che tutti i giochi sono interamente costruiti su di una serie di oggetti. Ogni locazione e' un oggetto, ogni cosa che il giocatore puo' vedere e toccare e' un oggetto; anche il personaggio giocatore stesso e' un oggetto (uno di quelli definiti automaticamente dalla libreria). Il modello generale per definire un oggetto e': Object id_interno "nome_esterno" oggetto_padre with property valore, property valore, ... property valore, has attributo attributo ... attributo ; La definizione inizia con la parola Object e finisce con un punto e virgola; tra i due vi sono tre blocchi principali di informazioni: * immediatamente dopo la parola Object vi sono le informazioni * dell’intestazione dell’oggetto; * la parola with introduce le proprieta' dellÂ’oggetto; * la parola has introduce gli attributi dellÂ’oggetto. *** Intestazione dellÂ’Oggetto Essa contiene tre cose, tutte facoltative: * Un identificatore interno id_interno usato dagli altri oggetti per riferirsi a questo oggetto. EÂ’ una singola parola (anche se puo' contenere trattini e underscore), puo' arrivare fino a 32 caratteri e deve essere unica allÂ’interno del gioco. Si puo' omettere l’identificatore interno se nessun altro oggetto deve relazionarsi con esso. Esempio: uccello, albero, sull_albero. * Un nome_esterno, tra virgolette, usato dallÂ’interprete quando si riferisce allÂ’oggetto. Puo' essere composto da una o piu' parole e non deve necessariamente essere unico (si potrebbero quindi avere anche piu' locazioni chiamate "Da qualche parte nel deserto"). Sebbene non sia obbligatorio e' meglio dare un nome_esterno a tutti gli oggetti. Esempio: "uccellino", "albero di sicomoro", "in cima all’albero". * L'identificatore interno di un altro oggetto che e' la locazione iniziale dove il nostro oggetto e' presente allÂ’inizio del gioco (il suo "genitore" o "padre" - vedi la prossima sezione.). Puo' essere omesso per gli oggetti che non hanno un genitore allÂ’inizio; viene sempre omesso nelle locazioni. Esempio: la definizione dell’ uccello comincia specificando che allÂ’inizio esso puo' essere trovato nella locazione foresta (sebbene piu' tardi il personaggio giocatore lo raccogliera' e potra' portarlo altrove): Object uccello "uccellino" foresta ... L’ albero comincia allo stesso modo; l’unica differenza reale e' che, poiche' il giocatore non puo' muovere un oggetto con l’attributo scenery, esso rimarra' sempre nella radura: Object albero " albero di sicomoro" radura ... NOTA: C’e' un metodo alternativo per definire la locazione iniziale di un oggetto: usando le "frecce" invece che il nome interno del genitore dellÂ’oggetto (obj_id). Ad esempio la definizione dellÂ’uccello poteva cominciare in questa maniera: Object -> uccello "uccellino" ... Non useremo il metodo delle frecce in questa guida, anche se descriveremo come funziona nel capitolo "Impostare lÂ’albero degli oggetti" nel capitolo 14. *** Le proprieta' dellÂ’Oggetto La definizione delle proprieta' di un oggetto sono introdotte dalla parola chiave with. Un oggetto puo' possedere un qualsiasi numero di proprieta', e non cÂ’e' bisogno di seguire un ordine particolare. Ogni definizione e' composta di due parti: un nome e un valore divisi da uno spazio e con una virgola alla fine. Pensa alle proprieta' come a delle variabili specificatamente associate ad un oggetto. Il valore iniziale della variabile e' quello fornito nella definizione della proprieta', ma se necessario esso puo' essere modificato durante la partita (anche se normalmente la maggior parte delle proprieta' non cambia valore). Ecco alcuni esempi di proprieta' che abbiamo incontrato: description "Il nido e' fatto di rametti e sterpi intrecciati.", e_to foresta, name 'uccello' 'uccellino' 'passero' 'passerotto' 'volatile', each_turn [; if (nido in ramo) deadflag = 2; ], Per una fortunata coincidenza questi esempi mostrano anche diversi tipi di valori che puo' assumere una proprieta'. Il valore associata alla proprieta' description in questo esempio particolare e' una stringa di caratteri tra virgolette; il valore associato alla proprieta' e_to e' il nome identificativo interno di un oggetto; la proprieta' name e' un pochino insolita - il suo valore e' una serie di parole del dizionario, ognuna posta tra apici; la proprieta' each_turn ha come valore una embedded routine (vedi "Routine incapsulate" a pagina 50). L’unico altro tipo di valore che si puo' trovare comunemente associato ad una proprieta' e' un semplice numero; per esempio: capacity 10. In totale la libreria definisce circa 48 proprieta' standard - come name e each_turn - che puoi associare ai tuoi oggetti; CÂ’e' una lista completa delle "Proprieta' degli Oggetti" nell'appendice F. E in "Guglielmo Tell", nel capitolo 8, mostriamo come inventarsi una proprieta' personalizzata. *** Attributi degli Oggetti Una serie di attributi per un oggetto e' introdotta dalla parola chiave has. Un oggetto puo' avere un numero qualsiasi di attributi, che possono essere scritti in qualsiasi ordine divisi da uno spazio tra ognuno di loro. Come per le proprieta', puoi pensare ad ogni attributo come a una variabile che e' specificatamente associata ad un oggetto. Naturalmente un attributo e' molto piu' limitato di una proprieta' dal momento che puo' assumere solo due valori: presente o assente (o anche acceso/spento pieno/vuoto vero/falso, una variabile che puo' assumere solo due valori viene spesso chiamata flag (bandiera)). Inizialmente un attributo o e' presente (se menzioni il suo nome nella lista di attributi dellÂ’oggetto) o e' assente (nel caso inverso); se necessario il suo valore puo' cambiare durante la partita (la cosa di solito accade frequentemente). Spesso diremo che un certo oggetto al momento ha un certo attributo o che altrimenti non lo ha. Gli attributi che abbiamo incontrato fino adesso sono: container light open scenery static supporter Ognuno di essi risponde ad una domanda: Questo oggetto puo' contenerne altri? E’ una fonte di luce? E cosi' via. Se l’attributo e' presente allora la risposta e' SI; se invece non e' presente la risposta e' NO. In totale la libreria definisce circa 30 attributi standard che puoi associare ai tuoi oggetto; nell'appendice F puoi trovare una lista completa degli attributi degli oggetti. Rapporti tra oggetti - l’albero degli oggetti ********************************************* Non solo il tuo gioco e' composto interamente di oggetti, ma e' fatto anche di relazioni tra essi, inform fa molta attenzione a tali rapporti e ne monitora costantemente l’evoluzione. Con "rapporti" naturalmente non intendiamo certo dire solo che Walter e' il figlio di Guglielmo, mentre Helga e Guglielmo sono semplici amici, ma stiamo parlando di un operazione costante di registrazione di dove si trova esattamente ogni oggetto rispetto agli altri nel gioco. Inform gestisce le relazioni tra oggetti in termini di oggetti genitori (parent) e oggetti figli (child), anche se non nel senso esatto dei termini. Quando il personaggio giocatore e' in una particolare locazione - ad esempio nella foresta - possiamo dire che: * l’oggetto foresta e' il genitore (parent) dell’oggetto giocatore, o viceversa che * l’oggetto giocatore e' un figlio (child) dell’oggetto foresta. Inoltre, se il giocatore sta trasportando un oggetto – per esempio il nido – possiamo dire che: * l’oggetto giocatore e' il genitore (parent) dell’oggetto nido, o che * l’oggetto nido e' un figlio (child) dell’oggetto giocatore. Nota che un oggetto ha solo un genitore (parent) o nessun genitore (parent), ma puo' avere un qualsiasi numero di oggetti figli (child) o nessuno. Per fare un esempio di un oggetto che ha piu' oggetti figli (child), pensa a come abbiamo definito gli oggetti nido e albero: Object nido "nido di uccelli " radura ... Object albero "albero di sicomoro" radura ... Ricorderai poi che abbiamo usato la terza voce nellÂ’intestazione dell’oggetto per affermare che la radura e' il genitore (parent) dell’oggetto nido, e anche che la radura e' il genitore dellÂ’oggetto albero, quindi sia il nido che l’albero sono oggetti figli (child) dell’oggetto radura. NOTA: una "stanza" percio' non ha niente di magico rispetto agli altri oggetti, e' semplicemente un oggetto che non ha mai un genitore (parent), e che di tanto in tanto puo' avere come oggetto figlio (child) il giocatore. Quando abbiamo definito lÂ’uccello, lo abbiamo posto nella foresta in questa maniera: Object uccello "uccellino" foresta ... Non abbiamo posto nessun altro oggetto in quella locazione, cosi' all’inizio del gioco la foresta e' il genitore (parent) dell’uccello e quest’ultimo e' l’unico oggetto figlio (child) della foresta. Ma cosa accade quando il personaggio giocatore, che inizialmente si trovava nella locazione davanti_baita, va verso EST ed entra nella foresta? Risposta: il genitore dellÂ’oggetto giocatore diventa la foresta, cosi' quest’ultima si ritrova con due oggetti figli (child) - l’uccello e il giocatore. Questo e' il principio chiave del sistema con cui inform gestisce i suoi oggetti: i rapporti tra oggetti genitori - figli (parent - child) cambia continuamente, spesso drammaticamente, nello svolgersi del gioco. Facciamo un altro esempio: supponi che il giocatore prenda l’uccello. Cio' causa unÂ’ulteriore cambio di rapporti tra gli oggetti. L’uccello e' ora un figlio (child) del giocatore (e non della foresta), e il giocatore e' sia genitore (parent) dell’oggetto uccello sia figlio (child) dell’oggetto foresta . In questo diagramma, mostriamo come i rapporti tra gli oggetti cambino durante il corso di una partita. Le linee verticali rappresentano le relazioni tra genitori - figli (parent - child) con in alto lÂ’oggetto genitore (parent) ed in basso lÂ’oggetto figlio (child); player e' l'oggetto giocatore. davanti sull' _baita foresta radura albero 1. ALL'INIZIO DEL GIOCO | | / \ | | | / \ | player uccello nido albero ramo davanti sull' _baita foresta radura albero 2. il giocatore scrive / \ / \ | VAI A EST / \ / \ | player uccello nido albero ramo davanti sull' _baita foresta radura albero 3. il giocatore scrive | / \ | PRENDI L'UCCELLO | / \ | player nido albero ramo | | uccello davanti sull' _baita foresta radura albero 4. il giocatore scrive / | \ | VAI A NORDEST / | \ | player nido albero ramo | | uccello davanti sull' _baita foresta radura albero 5. il giocatore scrive / | \ | METTI L'UCCELLO NEL / | \ | NIDO player nido albero ramo | | uccello davanti sull' _baita foresta radura albero 6. il giocatore scrive / \ | PRENDI IL NIDO / \ | player albero ramo | | nido | | uccello davanti sull' _baita foresta radura albero 7. il giocatore scrive | / \ SU | / \ albero player ramo | | nido | | uccello davanti sull' _baita foresta radura albero 8. il giocatore scrive | / \ METTI IL NIDO SUL | / \ RAMO albero player ramo | | nido | | uccello In questo breve esempio, ci siamo presi molto tempo e spazio per spiegare esattamente come funziona il modello di rapporti tra gli oggetti - generalmente conosciuto anche con il nome di albero degli oggetti (object tree) - e a come appare e cambia dopo ogni passo avanti del gioco. Normalmente non dovrai preoccuparti di tutti questi dettagli (a) perche' lÂ’interprete fa la maggior parte del lavoro al posto tuo e (b) perche' nelle avventure testuali ci sono di solito troppi oggetti per poter tracciarne tutti i movimenti. La cosa importante e' che tu capisca i principi di base: in qualsiasi momento un oggetto o non ha un genitore (il che probabilmente vuol dire che e' un locazione, o che sta fluttuando nel vuoto e non e' al momento parte del gioco) o ha un solo ed unico genitore (parent) - ossia lÂ’oggetto "nel quale" o "sul quale" si trova o "di cui fa parte". Naturalmente non c’e' limite al numero di figli (children) che un oggetto puo' avere. L’uso pratico dei rapporti tra oggetti sara' spiegato dettagliatamente piu' avanti. Programmando la tua avventure potrai riferirti al corrente genitore (parent) o ai figli (children) di ogni oggetto dato con le routine parent, child e children, e questa sara' una delle caratteristiche che userai piu' frequentemente. Ci sono anche altre routine associate allÂ’albero degli oggetti (object tree), per aiutarti a tenere traccia degli oggetti e dei loro movimenti. Le vedremo una per una nei prossimi capitoli. Per un piccolo sommario, guarda "Oggetti" nel capitolo 15. Il testo tra virgolette e apici ******************************* Inform fa una netta distinzione tra il testo quotato tra virgolette ("...") e quello messo tra apici ('...'). *** Testo quotato tra virgolette. Le virgolette vengono usate per contenere una stringa - una lettera, una parola, un paragrafo o un qualsiasi numero di caratteri alpha numerici - che si vuole far visualizzare sullo schermo dall’interprete durante la partita. Puoi usare la tilde ~ per rappresentare le virgolette all’interno della stringa, e il carattere ^ per creare una nuova linea (ovvero andare a capo). Lettere maiuscole e minuscole vengono riconosciute come differenti caratteri. Una stringa puo' essere suddivisa su piu' righe; inform trasforma ogni ritorno a capo nel codice (e ogni spazio intorno al ritorno a capo) in un singolo spazio bianco (ulteriori spazi pero' verranno preservati). Pertanto queste due stringhe sono equivalenti: "Questa e' una stringa di caratteri." "Questa e' una stringa di caratteri." Quando un interprete visualizza una lunga stringa di caratteri - per esempio, mentre descrive una lunga e dettagliata descrizione di una locazione - impiega un automatico sistema di ritorno a capo per visualizzare correttamente il testo sullo schermo del giocatore. Per forzare i ritorni a capo puoi utilizzare il carattere ^ cio' fara' si che il testo sia visualizzato in una serie di paragrafi. Poco fa abbiamo visto stringhe utilizzate come valore di una Constante: Constant Headline "^Un semplice esempio in inform ^di Roger Firth e Sonja Kesserich.^"; che potrebbe allo stesso modo essere definita cosi': Constant Headline "^ Un semplice esempio in inform^di Roger Firth e Sonja Kesserich.^"; e come valore della proprieta' description di un oggetto: description " Troppo giovane per saper volare, il passerotto pigola inerme.", Piu' in la troverai che il loro uso e' molto comune nelle istruzioni print di stampa. *** Testo tra apici Le apici sono usate attorno a una parola del dizionario. Questa deve essere una singola parola - senza spazi - che generalmente contiene unicamente lettere (e solo occasionalmente numeri e trattini), Tuttavia e' possibile usare il carattere ^ all’interno degli apici per rappresentare un apostrofo. Lettere maiuscole o minuscole vengono considerate identiche; inoltre, l’interprete di solito guarda unicamente ai primi nove caratteri di ogni parola che digita il giocatore. Quando il giocatore digita un comando, l’interprete divide il comando in singole parole, che compara con il dizionario. Se trova tutte le parole, ed esse sembrano rappresentare unÂ’azione valida, esso la riconosce e esegue le opportune istruzioni. Poco fa abbiamo visto le parole del dizionario usate come valore della proprieta' name di un oggetto: name 'uccello' 'uccellino' 'passero' 'passerotto' 'volatile', e in effetti questo e' praticamente lÂ’unico posto dove il testo tra apici e' necessario. Eviterai ogni confusione al riguardo ricordandoti semplicemente questa distinzione: Output tra Virgolette, Input tra Apici (OVIA). Routine e istruzioni ******************** Una routine e' una serie di istruzioni, che sono studiate (o come diremo spesso, eseguite) durante lÂ’esecuzione del programma dallÂ’interprete. Ci sono due tipi di routine, e circa due dozzine di istruzioni, puoi trovare la lista completa delle istruzioni nel capitolo 14, o anche andare a leggere "II linguaggio INFORM" nell'appendice E. *** Istruzioni Un'istruzione e' un commando che dice allÂ’interprete di eseguire una particolare operazione mentre si gioca la partita. Un gioco reale normalmente contiene una moltitudine di istruzioni, ma per adesso noi ne abbiamo incontrate solo alcune, in particolare: location = davanti_baita; che e' un esempio di assegnazione, poiche' attraverso l’operatore = assegna un nuovo valore (il nome interno della nostra locazione davanti_baita) alla variabile globale location che e' parte della libreria. Poi abbiamo incontrato la linea: if (nido in ramo) deadflag = 2; che contiene due istruzioni: una assegnazione, preceduta da una condizione if : if (nido in ramo) ... L’istruzione if controlla una particolare condizione; se la condizione e' vera, l’interprete esegue qualunque istruzione che la segue; se invece non e' vera allora lÂ’interprete ignora l’istruzione successiva. In questo esempio l’interprete sta controllando se l’oggetto nido e' "dentro" o "sopra" (che noi sappiamo significare "e' figlio (child) de") l’oggetto ramo. Per la maggior parte della durata del gioco questa condizione non e' vera, quindi l’interprete ignora lÂ’istruzione successiva. Quando poi, eventualmente, la condizione diventa vera l’interprete esegue la successiva istruzione, che prevede una assegnazione: deadflag = 2; che cambia il valore della variabile della libreria deadflag dal valore corrente a 2. A proposito dellÂ’istruzione if essa e' spesso scritta su due linee, con l'istruzione "controllata" evidenziata da un rientro del margine. Cio' rende il codice piu' semplice da leggere, e non cambia in alcun modo il suo funzionamento: if (nest in branch) deadflag = 2; La condizione che viene controllata dall’istruzione if non deve essere una assegnazione ma puo' essere un qualsiasi tipo di istruzione. Infatti puo' accadere di avere molte istruzioni, e non solo una, controllate da una istruzione if. Parleremo di queste altre possibilita' piu' avanti nella guida. Per adesso ti basti ricordare che l’unico posto dove potrai trovare delle istruzioni e' all’interno di routine indipendenti o incapsulate. *** Routine indipendenti Una routine indipendente e' una serie di istruzioni messe assieme e con un nome. Quando la routine viene "chiamata" - dal nome dato - vengono eseguite le istruzioni che contiene. Vediamone una che abbiamo definito nel nostro esempio: [ Initialise; location = davanti_baita; ]; Visto che e' una routine davvero minuscola, l’avevamo posta su di una singola riga. Andiamola pero' a riscrivere usando piu' linee (come con lÂ’istruzione if, cio' migliora la leggibilita' e non cambia il funzionamento del codice): [ Initialise; location = davanti_baita; ]; Il primo pezzo [ Initialise; e' l’inizio della routine, e definisce il nome con cui essa puo' essere chiamata. La ]; e' la fine della routine. Tra i due vi sono le istruzioni - talvolta esse vengono chiamate il corpo della routine - che sono eseguite quando la routine viene chiamata. Potresti chiederti: come si fa a chiamare una determinata routine? Presto detto, con una istruzione di questo genere: Initialise(); Il nome della routine seguita dall’apertura e chiusura di parentesi tonde e' tutto cio' che serve per richiamare una routine. Quando incontra una linea come questa, l’interprete esegue le istruzioni - che in questo esempio si limitano ad una unica assegnazione, ma che potrebbero essere dieci venti o centinaia - contenute nel corpo della routine. Una volta che le ha eseguite, lÂ’interprete ricomincia il suo lavoro dalla linea successive alla chiamata della routine Initialise();. NOTA: potresti aver notato che, sebbene abbiamo definito la routine chiamata Initialise, nel nostro esempio non la abbiamo mai "chiamata". Non preoccuparti - la routine e' chiamata dalla libreria di inform, proprio all’inizio di ogni partita. *** Routine incapsulate Una routine incapsulata assomiglia molto a una routine indipendente, sebbene non abbia un nome e non finisca con un punto e virgola. Eccone una di quelle che abbiamo definito: [; if (nido in ramo) deadflag = 2; ] con l’eccezione del fatto che noi non la abbiamo scritta cosi' ma come un valore di una proprieta' dell’oggetto: each_turn [; if (nido in ramo) deadflag = 2; ], che funziona ugualmente anche se scritta in questo modo: each_turn [; if (nest in branch) deadflag = 2; ], Tutte le routine incapsulate sono definite cosi', ossia come valore di una proprieta' di un oggetto. ovvero il luogo dove esse sono racchiuse nell’oggetto. I caratteri introduttivi [; potrebbero sembrare un poco strani, ma in realta' e' la medesima sintassi delle standalone routine, solo senza nome tra la [ e il ;. Per chiamare una embedded routine, in modo da eseguire le istruzioni che contiene, il metodo che abbiamo descritto per le standalone routine non funziona. Infatti una embedded routine non ha un nome, e non ne ha bisogno; essa viene automaticamente chiamata dalla libreria nel momento opportuno, che viene determinato dal ruolo della proprieta' di cui e' il valore. Nel nostro esempio, cio' avviene alla fine di ogni turno nel quale il personaggio protagonista e' nella stessa locazione del ramo. Piu' avanti, vedremo altri esempi di embedded routine, ognuna progettata per realizzare un obiettivo appropriato alla proprieta' con cui e' associata; vedremo inoltre che e' possibile chiamare anche una embedded routine, usando una sintassi del tipo id_oggetto.propieta'() - in questo esempio avremmo potuto chiamare la routine scrivendo ramo.each_turn(). Troverai piu' informazioni su questo argomento in "Routine e argomenti" alla fine del capitolo 5, in "Routine" nel capitolo 14, e in "La Piazza del Mercato", nel capitolo 9. Siamo arrivati alla fine della nostra revisione degli argomenti coperti nel nostro primo gioco. Avremmo avuto molte piu' cose da dire, ma stiamo cercando di non sovraccaricarti troppo in questa fase iniziale e le recupereremo piu' avanti. Cio' che ci piacerebbe che tu facessi ora e' tornare indietro a dare unÂ’occhiata al codice del gioco in modo da assicurarti di aver ben compreso ogni elemento descritto in questo paragrafo. Fatto? Bene, allora andiamo avanti a sistemare qualche importante difetto che ancora affligge il nostro gioco.