Capitolo 7: Guglielmo Tell, i primi anni ======================================== M was a miser, and hoarded up gold N was a nobleman, gallant and bold Muovendoci con una certa celerità, definiremo le prime due stanze e le popoleremo con vari paesani e mobilio da strada, equipaggeremo Wilhelm con il suo arco e la sua faretra piena di frecce, e introdurremo Helga, l'amichevole verduraia. Definiamo la strada ******************* Questa è la strada, la locazione dove il gioco inizia: Room strada "Una strada di Altdorf" with description [; print "La piccola strada conduce verso nord alla piazza principale. La gente del luogo sta sciamando nella città attraverso la porta a sud, salutando a voce alta, offrendo prodotti in vendita, scambiando notizie, informandosi con incredulità esagerata sui prezzi delle merci esposte dai mercanti, i cui banchi rendono ancora più difficile l'avanzare in mezzo alla folla.^"; if (self hasnt visited) print "^~Stammi vicino, figliolo,~ dici, ~altrimenti potresti perderti fra tutta questa gente.~^"; ], n_to vicino_piazza, s_to "La folla, muovendosi verso la piazza a nord, ti impedisce di tornare indietro."; Stiamo usando la nostra nuova classe Room, così non c'è bisogno dell'attributo light. Le proprietà n_to e s_to, i cui valori sono rispettivamente un identificatore interno e una stringa, sono tecniche che abbiamo usato già in precedenza. L'unica innovazione è che la proprietà description una routine incapsulata nel suo valore. La ragione è la seguente: Le prima cosa che incontriamo nella routine è l'istruzione print, che visualizza i dettagli della strada e dell'ambiente circostante. Se questo fosse tutto stato ciò che volevamo fare, avremmo potuto fornire questi particolari usando una stringa come valore della proprietà description; cioè, questi due esempi si comportano in modo identico: description [; print "La piccola strada conduce verso nord alla piazza principale. La gente del luogo sta sciamando nella città attraverso la porta a sud, salutando a voce alta, offrendo prodotti in vendita, scambiando notizie, informandosi con incredulità esagerata sui prezzi delle merci esposte dai mercanti, i cui banchi rendono ancora più difficile l'avanzare in mezzo alla folla.^"; ], description "La piccola strada conduce verso nord alla piazza principale. La gente del luogo sta sciamando nella città attraverso la porta a sud, salutando a voce alta, offrendo prodotti in vendita, scambiando notizie, informandosi con incredulità esagerata sui prezzi delle merci esposte dai mercanti, i cui banchi rendono ancora più difficile l'avanzare in mezzo alla folla.", Comunque, questo non è tutto quello che vogliamo fare. Dopo aver presentato la descrizione di base, vogliamo visualizzare quel piccolo frammento di dialogo in cui Wilhelm dice a suo figlio di essere prudente. E vogliamo che ciò accada una volta sola, la prima volta che la descrizione della strada viene visualizzata. Se il giocatore scrive GUARDA più volte, o va a nord e poi torna verso sud alla strada, noi siamo ben felici di vedere l'ambiente nuovamente descritto, ma non vogliamo che il dialogo si svolga ancora. Questo paio di istruzioni realizza ciò che vogliamo: if (self hasnt visited) print "^~Stammi vicino, figliolo,~ dici, ~altrimenti potresti perderti fra tutta questa gente.~^"; Il frammento di dialogo è realizzato dall'istruzione print, che è controllata dall'istruzione if, che a sua volta esegue il misterioso controllo self hasnt visited. Guardiamo più da vicino: * visited è un attributo, ma non uno che normalmente assegnereste voi stessi ad un oggetto. E' assegnato automaticamente agli oggetti stanza dall'interprete, ma solo dopo che la stanza è stata visitata per la prima volta dal giocatore. * hasnt (e has) sono disponibili per verificare se un certo attributo è stato assegnato o no ad un certo oggetto. X has Y è vera se e solo se l'oggetto X possiede attualmente l'attributo Y, e falsa altrimenti. Per invertire il controllo, X hasnt Y è vera se e solo se l'oggetto X attualmente non possiede l'attributo Y, e falsa se invece lo possiede. * self, che abbiamo già incontrato nel capitolo scorso, è una utilissima variabile che, all'interno di un oggetto, si riferisce sempre a tale oggetto. Siccome la stiamo usando all'interno dell'oggetto strada, si riferirà proprio alla strada. Così, riassumendo il tutto, self hasnt visited risulta vera (e quindi l'istruzione print viene eseguita) solo quando l'oggetto strada non ha l'attributo visited. Siccome l'interprete assegna ad una stanza l'attributo visited appena il giocatore c'è stato almeno una volta, questa condizione sarà vera solo per un turno. Quindi, il frammento di dialogo sarà visualizzato solo durante un singolo turno: la prima volta che il giocatore si trova in strada, all'inizio del gioco. Anche se la variabile self risulta di primaria importanza nella definizione delle classi, può essere conveniente usarla semplicemente all'interno di un oggetto. Perchè? potrete chiedere. Perchè non scrivere l'istruzione in questo modo: if (strada hasnt visited) print "^~Stammi vicino, figliolo,~ dici, ~altrimenti potresti perderti fra tutta questa gente.~^"; E' vero che l'effetto è identico, ma c'è un paio di buoni motivi per usare self. Primo: è un aiuto per comprendere il codice anche giorni o settimane dopo che l'avete scritto. Se leggete la riga if (street hasnt visited), dovete pensare per un momento a quale è l'oggetto riferito dalla condizione; oh, è proprio questo. Se leggete if (self hasnt visited), saprete immediatamente di che oggetto stiamo parlando. Un'altra ragione è l'auto-plagio. Molte volte vi accorgerete che un frammento di codice è utile in differenti situazioni (ad esempio, se volete ripetere il meccanismo della descrizione della strada in un'altra stanza). Piuttosto che riscrivere tutto da capo, tipicamente userete copia-incolla per ripetere la routine, e allora tutto quello che dovete fare è scrivere le appropriate stringhe descrittive per la nuova stanza. Se avete usato self, la riga if (self hasnt visited) è sempre buona; se invece avete scritto if (strada hasnt visited), dovete cambiare anche quella. (Peggio ancora, se vi dimenticate di cambiarla il gioco funzionerà ancora, ma non nel modo che voi intendevate, e il bug risultante risulterebbe parecchio difficile da individuare.) Aggiungiamo un po' di scenografia ********************************* La descrizione della strada menziona diversi oggetti - la porta, la gente, etc. etc. - che merita di esistere all'interno del gioco (almeno in una minima forma) per sostenere l'illusione della marea di folla in movimento. La nostra classe Prop è l'ideale per questo: Prop "porta meridionale" with name 'cancello' 'porta' 'meridionale', description "Nelle mura della città si apre una larga porta. Il pesante cancello di legno ora è aperto.", found_in strada, has female; Prop "banchi" with name 'banchi', description "Cibo, abiti, attrezzature da montagna; la solita roba.", found_in strada vicino_piazza, has pluralname; Prop "mercanti" with name 'mercante' 'mercanti', description "Qualche truffatore, ma per il resto gente a posto che si guardagna da vivere.", found_in strada vicino_piazza, has animate pluralname; Prop "gente" with name 'persone' 'gente', description "Gente di montagna, come te.", found_in [; return true; ], has animate female; Potete vedere un paio di nuovi attributi: animate individua un oggetto come "essere vivente", mentre pluralname specifica che il nome di un oggetto è plurale, piuttosto che singolare. L'interprete usa questi attributi per assicurarsi che i messaggi relativi a tali oggetti siano grammaticalmente appropriati (per esempio, si riferà da ora in poi a "dei mercanti" piuttosto che a "un mercanti"). Siccome la libreria gestisce molte situazioni automaticamente, è difficile stabilire esattamente quale messaggio verrà generato in risposta alle azioni del giocatore; il miglior approccio è quello di restare sul sicuro e assegnare sempre ad un oggetto un pertinente insieme di attributi anche quando, come in questo caso, probabilmente non serviranno. Vedete anche la nuova proprietà found_in, che specifica la stanza o le stanze dove questo oggetto può essere trovato. Per la porta della città, che appare solo in una stanza, avremmo potuto facilmente includere l'identificatore dell'oggetto padre nella riga di intestazione: Prop "porta meridionale" strada with name 'cancello' 'porta' 'meridionale', description "Nelle mura della città si apre una larga porta. Il pesante cancello di legno ora è aperto.", has female; ma abbiamo scelto di usare found_in per uniformarci agli altri oggetti scenici, che sono più interessanti. I mercanti e i loro banchi possono essere ESAMINATI sia nella strada che vicino alla piazza - e sono resi come un unico oggetto che l'interprete sposta in una di queste stanze quando il giocatore si trova lì. E la popolazione locale è ancora più onnipresente. La proprietà found_in di un oggetto è una routine incapsulata, e quello che dovrebbe fare generalmente è controllare la locazione corrente e restiture true se vuole che l'oggetto in questione sia presente in quella locazione, e false altrimenti. Qui noi vogliamo che la gente sia sempre presente, in ogni stanza, così restituiamo true senza nemmeno preoccuparci di esaminare la locazione. E' come se avessimno scritto una di queste altre possibilità, ma in modo più semplice e con meno possibilità d'errore: Prop "gente" with name 'persone' 'gente', description "Gente di montagna, come te.", found_in strada vicino_piazza piazza_sud centro_piazza piazza_nord mercato, has animate female; Prop "gente" with name 'persone' 'gente', description "Gente di montagna, come te.", found_in [; if (location == strada || location == vicino_piazza || location == piazza_sud || location == centro_piazza || location == piazza_nord || location == mercato) return true; return false; ], has animate female; Prop "gente" with name 'persone' 'gente', description "Gente di montagna, come te.", found_in [; if (location == strada or vicino_piazza or piazza_sud or centro_piazza or piazza_nord or mercato ) return true; return false; ], has animate female; Nel secondo esempio vedete l'operatore ||, che deve essere letto come "o", e che abbiamo menzionato verso la fine di "Heidi"; esso combina i vari confronti location == qualche_stanza in modo che tutta l'istruzione if risulta vera se anche uno solo di questi confronti risulta vero. E nel terzo esempio introduciamo la parola or, che è un modo più succinto per ottenere esattamente lo stesso risultato. Gli oggetti del giocatore ************************* Visto che la nostra routine Initialise li ha già menzionati, vorremmo definire l'arco e le frecce di Wilhelm: Object arco "arco" with name 'arco', description "Your trusty yew bow, strung with flax.", before [; Drop,Give: print_ret "Non ti separi mai dal tuo fidato arco."; ] has clothing; Object faretra "faretra" with name 'faretra', description "Fatta in pelle di capra, di solito pende dalla tua spalla sinistra.", before [; Drop,Give: print_ret "E' un regalo di tua moglie Hedwig."; ], has container open clothing female; Entrambi sono oggetti semplici, che intercettano le azioni Drop e Give per essere sicuri che Wilhelm non ne è mai privo. L'attributo clothing appare per la prima volta, indicando che sia la faretra sia (meno probabilmente) l'arco possono essere indossati; ricorderete che la nostra routine Initialise assegna l'attributo worn alla faretra. Una faretra vuota è abbastanza inutile, così questa è la classe usata per definire la riserva di frecce di Wilhelm. Questa classe ha qualche caratteristica inusuale: Class Arrow with name 'freccia' 'frecce//p', article "una", plural "frecce", description "Come tutte le altre tue frecce, dritta e acuminata.", before [; Drop: print_ret "Troppo pericolose, meglio non lasciarle in giro."; ], has female; Le classi che abbiamo creato fino ad ora - Room, Prop e Furniture - erano pensate per oggetti che si comportano nella stessa maniera ma sono altrimenti chiaramente separati. Per esempio, un tavolo, un letto e un armadio generalmente avrebbero le loro proprie caratteristiche individuali - un nome, una descrizione, forse qualche proprietà particolare - e allo stesso tempo erediterebbero il comportamento generale degli oggetti Furniture. Le frecce non sono così: non soltanto si comportano nello stesso modo, ma sono altresì indistinguibili l'una dall'altra. (Beh, potremmo aspettarci che un esperto arciere come Wilhelm possa riconoscerle, ma certamente noi non possiamo.) Cerchiamo di ottenere questo effetto: >INVENTARIO Stai portando: una faretra (indossata) tre frecce un arco dove l'interprete accorpa insieme la nostra scorta di tre frecce, piuttosto che elencarle individualmente in questa goffa maniera: >INVENTARIO Stai portando: una faretra (indossata) una freccia una freccia una freccia un arco L'interprete farà questo per bnoi se i nostri oggetti sono "indistinguibili", effetto che viene meglio ottenuto rendendoli membri dei una classe che contiene sia la proprietà "name" che la proprietà "plural". Definiamo le frecce reali molto semplicemente, così: Arrow "freccia" faretra; Arrow "freccia" faretra; Arrow "freccia" faretra; e potete vedere che forniamo solo due informazioni per ogni oggetto Arrow: un nome esterno tra doppi apici ("freccia" in questo caso) che l'interprete usa per riferisi all'oggetto, e la locazione iniziale (nella faretra). Questo è tutto: niente blocco delle proprietà, niente insieme di attributi, nessun identificatore interno, perchè non avremo mai bisogno di riferirci ad un individuale oggetto Arrow all'interno del gioco. La proprietà name ha un aspetto bizzarro: name 'freccia' 'frecce//p' La parola 'freccia' si riferisce ad una singola freccia. La stessa cosa succederebbe alla parola 'frecce', a meno che non specifichiamo all'interprete che è un riferimento plurale. Il suffisso //p individua la parola 'frecce' come potenziale riferimento a più di un oggetto alla volta, permettendo quindi al giocatore di scrivere PRENDI FRECCE e quindi raccogliere tutte le frecce disponibili (senza di esso PRENDI FRECCE avrebbe raccolto solo una freccia a caso). Ci sono altre due proprietà che ancora non abbiamo visto: article "una" plural "arrows" La proprietà article vi permette di definire l'articolo indefinito dell'oggetto - usualmente qualcosa come "un", o "alcune" - invece di lasciare che la libreria ne assegni uno automaticamente. E' spesso una precauzione di sicurezza. La maggior parte degli interpreti assegna i giusti articoli, ma se vogliamo restare sul sicuro, possiamo definire esplicitamente la parola appropriata. [NdT: Nella versione inglese la parola "arrow", iniziando per vocale, avrebbe bisogno dell'articolo "an" piuttosto che "a". Per questo motivo la proprietà article viene introdotta in questo punto. La corrispondente parola italiana "freccia" non ha la stessa particolarità, ma abbiamo lasciato l'introduzione della proprietà article in questo punto] E la proprietà plural definisce la parola che deve essere usata quando mettiamo insieme diversi di questo oggetti, come in "tre frecce" nell'inventario. L'interprete non può determinare automaticamente il plurale di ogni parola; il plurale di freccia è frecce e non freccie, e il plurale di "fetta di torta" non è usualmente "fetta di torte", per esempio. Andiamo avanti lungo la strada ****************************** Come Wilhelm si muove verso nord, verso la piazza, arriva in questa stanza: Room vicino_piazza "Lungo la strada" with description "La gente continua a premere e a farsi strada dalla porta sud alla piazza principale, che si trova appena più a nord. Riconosci la proprietaria di un banco di frutta e verdura.", n_to piazza_sud, s_to strada; Nessuna sorpresa qui, e nemmeno nella maggior parte degli oggetti scenici. Furniture banco "banco di frutta e verdura" vicino_piazza with name 'frutta' 'verdura' 'banco' 'tavolo', description "Davvero un piccolo banco, con un grosso mucchio di patate, qualche carota, qualche rapa, un po' di mele.", before [; Search: <>; ], has scenery; Prop "patate" vicino_piazza with name 'patata' 'patate' 'mucchio', description "Devono essere state raccolte un po' di tempo fa... almeno 300 anni!", has pluralname female; Prop "frutta e verdura" vicino_piazza with name 'carota' 'carote' 'rapa' 'rape' 'mele' 'vegetali', description "Prodotti locali.", has female; L'unica novità qui è la proprietà before nel banco di frutta e verdura. La descrizione del banco - un mucchio di oggetti sul tavolo - può suggerire al giocatore che egli può CERCARE fra i vari prodotto, mageri trovando fortunosamente una barbabietola o qualcos'altro di interessante. Il giocatore non è così fortunato, ma noi vogliamo lo stesso permettere questo tentativo. Avendo intercettato l'azione Search, il nostro piano è quello di rispondere con la descrizione del banco, come se il giocatore avesse scritto ESAMINA IL BANCO. Non c'è per uno un modo semplice per infilare letteralmente quelle parole all'interno dell'interprete (in modo che CERCA generi un'azione Examine e non un'azione Search), ma possiamo simulare l'effetto che esse creano: un'azione di Examine applicata all'oggetto banco. Questa istruzione abbastanza criptica esegue il giusto compito: ; Avendo deviato l'azione Search in un'azione Examine, dobbiamo dire all'interprete che non ha bisogno di fare altro, siccome abbiamo già gestito completamente l'azione noi. Abbiamo già fatto questo in precedenza - usando return true - e così un primo tentativo per la nostra before avrà questo aspetto before [; Search: ; return true; ], La sequenza di due istruzioni <...>; return true; è così comune che c'è una singola istruzione che le abbrevia: <<...>>. Inoltre, per esattamente le stesse motivazioni cha abbiamo mostrato prima, il nostro codice è più chiaro se usiamo self invece di banco. Così questa è finalmente la nostra proprietà before: before [; Search: <>; ], Un paio di osservazioni finali prima di lasciare quest'argomento. L'esempio qui è di un'azione applicata ad un oggetto (self, anche se banco o non avrebbero funzionato lo stesso). Potete usare le istruzioni <...> e <<...>> per azioni che non influiscono su oggetti: <>; (che rappresenta il comando LOOK), o che ne infuliscono su due oggetti. Per esempio, il comando METTI L'UCCELLINO NEL NIDO può essere simulato da questa istruzione: <>; Introduciamo Helga ****************** Uno degli aspetti più complicati del realizzare un buon gioco è quello di fornire una soddisfacente interazione con altri personaggi. E' già abbastanza difficile codificare un oggetto inanimato che genera risposte appropriate a qualunque azione il Personaggio Giocatore (PG, o PC - Player Character) possa provare. Tutto questo diventa molto molto peggio quanto l'"altro oggetto" è una creatura vivente - un Personaggio Non Giocatore (PNG, o NPC - Non-Player Character) - con, si suppone, una mente propria. Un buon PNG dovrebbe muoversi indipendentemente, eseguire azioni con uno scopo, iniziare conversazioni, rispondere a ciò che dici o fai (e anche a ciò che non dici e non fai): può essere un vero incubo. Ma non qui: terremo i nostri tre PNG - Helga, Walter e il balivo - più semplici possibile. Nonostante ciò, possiamo stabilire alcuni principi fondamentali; ecco la classe su cui baseremo i nostri PNG: Class NPC with life [; Answer,Ask,Order,Tell: print_ret "Usa soltanto PARLA [", (arta) self, "]."; ], has animate; La cosa più importante qui è l'attributo aniumate - ciò che definisce un oggetto com PNG e fa sì che l'interprete lo tratti in modo lievemente differente - ad esempio, PRENDI HELGA ha come risposta "Non credo che Helga voglia farsi prendere in braccio". L'attributo animate inoltre porta in gioco nove azioni extra che possono essere applicate solo a oggetti animati: Answer (rispondi), Ask (chiedi), Order (ordina) e Tell (parla) sono tutteassociate con la comunicazione vocale, e Attack (attacca), Kiss (bacia), Show (mostra), ThrowAt (lancia a) e WakeOther (sveglia) sono associati con l'interazione non verbale. In più la nuova proprietà life - molto simile a before - può essere definita per intercettare queste azioni. Qui noi la usiamo per intercettare i comandi verbali come CHIEDI A HELGA DELLE MELE o PARLA A WALTER DEI BAMBINI, rispondendo al giocatore che in questo gioco abbiamo implementato soltanto un più semplice comando PARLA - vedi "Verbi, verbi, verbi", nel cap. 9. Basata su questa classe, ecco Helga: NPC proprietaria "Helga" vicino_piazza with name 'proprietaria' 'verduraia' 'venditrice' 'negoziante' 'mercante' 'helga' 'vestito' 'sciarpa', description "Helga è una donna grassoccia e affabile, infagottata in un abito informe e una sciarpa a pois.", initial [; print "Helga smette di sistemare le patate e ti saluta calorosamente.^"; if (location hasnt visited) { move mela to player; print "^~Salve, Wilhelm, è una buona giornata per il commercio! Questo è il giovane Walter? Come è cresciuto... Ecco, questa è una mela per lui... Se toglie la parte ammaccata, il resto è buono. Come sta la signora Tell? Salutamela davvero...~^"; } ], frasi_dette 0, ! per contare gli argomenti di conversazione life [; Kiss: print_ret "~Ooh, che sfacciato!~"; Talk: self.frasi_dette = self.frasi_dette + 1; switch (self.frasi_dette) { 1: score = score + 1; print_ret "Ringrazi calorosamente Helga per la mela."; 2: score = score + 1; print_ret "~Ci vediamo presto.~"; default: return false; } ], has female proper; Gli attributi sono female - in modo che che l'interprete si riferisca ad Helga con i pronomi appropriati - e proper. Quest'ultimo significa che il nome esterno di questo oggetto è un nome proprio, e così non deve essere preceduto da "una" o "la": non vorrete mica visualizzare "Puoi vedere una Helga qui" o "Non penso che la Helga voglia essere presa in braccio". Ci sono anche le proprietà life e frasi_dette (di cui parleremo in "Guglielmo Tell: la fine è prossima, nel capitolo 9) e una proprietà initial. La proprietà initial è usata quando l'interprete sta descrivendo la stanza e elencando gli oggetti che potete vedere. Se non l'avessimo definita, otterremmo questo: Lungo la strada La gente continua a premere e a farsi strada dalla porta sud alla piazza principale, che si trova appena più a nord. Riconosci la proprietaria di un banco di frutta e verdura. Puoi vedere Helga qui. > ma noi vogliamo introdurre Helga in modo più interattivo, ed è per questo motivo che esiste la proprietà initial: sostituisce il messaggio standard "Puoi vedere oggetto qui" con un messaggio personalizzato di vostra ideazione. Il valore della proprietà initial può essere una stringa da visualizzare o, come in questo caso, una routine incapsulata. Questa è abbastanza simile alla proprietà description che abbiamo definito per la strada: qualcosa che viene visualizzato sempre (Helga smette di...) e qualcoso che viene stampato solo alla prima occasione ("Salve, Wilhelm, è una buona giornata..."): Lungo la strada La gente continua a premere e a farsi strada dalla porta sud alla piazza principale, che si trova appena più a nord. Riconosci la proprietaria di un banco di frutta e verdura. Helga smette di sistemare le patate e ti saluta calorosamente. "Salve, Wilhelm, è una buona giornata per il commercio! Questo è il giovane Walter? Come è cresciuto... Ecco, questa è una mela per lui... Se toglie la parte ammaccata, il resto è buono. Come sta la signora Tell? Salutamela davvero..." > Ma non è esattamente identica alla routine description della strada. Anzitutto, abbiamo bisogno di un controllo if lievemente diverso: self hasnt visited funziona bene per un oggetto stanza, ma questa routine fa parte di un oggetto che si trova all'interno di una stanza; invece potremmo usare o vicino_piassa hasnt visited o (meglio) location hasnt visited - siccome location è la variabile di libreri che si riferisce alla stanza in cui il giocatore si trova al momento. E secondariamente, sono apparde alcune parentesi graffe {...}: perchè? Alla prima visita di Wilhelm in questa stanza, dobbiamo fare due cose: * assicuriamoci che Wilhelm entri in possesso della mela, poichè questo fatto è menzionato mentre... * visualizziamo il festoso saluto di Helga. L'istruzione move esegue il primo compito, mentre l'istruzione print esegue il secondo. Ed entrambe le istruzioni sono controllate dall'istruzione if. Fino ad ora, abbiamo usato l'istruione if due volte, entrambi i casi per controllare solo una singola istruzione. if (nido in ramo) deadflag = 2; if (self hasnt visited) print "^~Stammi vicino, figliolo,~ dici, ~altrimenti potresti perderti fra tutta questa gente.~^"; Questo è ciò che fa l'istruzione if - controlla se l'istruzione che segue debba essere eseguita oppure no. Così come possiamo controllare contemporaneamente due istruzioni? Bene, potremmo scrivere due istruzioni if: if (location hasnt visited) move mela to player; if (location hasnt visited) print "^~Salve, Wilhelm, è una buona giornata per il commercio! Questo è il giovane Walter? Come è cresciuto... Ecco, questa è una mela per lui... Se toglie la parte ammaccata, il resto è buono. Come sta la signora Tell? Salutamela davvero...~^"; Ma ciò è incredibilmente goffo; invece, useremo le graffe per raggruppare le istruzioni move e print in un unico blocco di istruzioni (chiamato talvolta blocco di codice, oppure soltanto blocco) che conta come un'unica istruzione per quanto riguarda il controllo datogli dall'istruzione if. if (location hasnt visited) { move mela to player; print "^~Salve, Wilhelm, è una buona giornata per il commercio! Questo è il giovane Walter? Come è cresciuto... Ecco, questa è una mela per lui... Se toglie la parte ammaccata, il resto è buono. Come sta la signora Tell? Salutamela davvero...~^"; } Un blocco può contenere una, due, dieci, cento istruzioni; non fà differenza - saranno tutti trattati come un'unica entità dall'istruzione if (e dall'istruzione objectloop che incontreremo più avanti, e dalle istruzioni do, for e while, che non incontreremo affatto in questa guida. NOTA: l'esatta posizione delle graffe è questione di scelta personale. Noi usiamo questo stile: if (condizione) { istruzione; istruzione; ... } ma altri autori hanno le loro preferenze, tra cui: if (condizione) { istruzione; istruzione; ... } if (condizione) { istruzione; istruzione; ... } if (condizione) { istruzione; istruzione; .... } Anche se fino ad ora non ne abbiamo avuto bisogno, ora probabilmente è il momento opportuno per introdurre l'estensione else all'istruzione if. Qualche volta potremmo voler eseguire un blocco di istruzione se una certa condizione è vera, e un altro blocco di istruzioni se invece non è vera. Ancora una volta potremmo scrivere due istruzioni if: if (location has visited) { istruzione; istruzione; ... } if (location hasnt visited) { istruzione; istruzione; ... } Ma questo ben difficilmente può essere reputato un approccio elegante; una clausola else svolge il lavoro in modo molto più pulito: if (location has visited) { istruzione; istruzione; ... } else { istruzione; istruzione; ... } Abbiamo creato un bel po' di ambienti e scenografia, ma l'azione vera e propria deve ancora arrivare. Ora arriverà il momento di definire la piazza della città e creare il confronto tra Wilhelm e i soldati del balivo.