Capitolo 11: Capitan Fato: seconda! =================================== U was a usurer, a miserable elf; V was a vintner, who drank all himself. Visto dall'interno, il bar di Benny e' caldo e accogliente, e pieno di clienti venuti a pranzare. Proveremo a mettere insieme qualche immagine appropriata, ma l'elemento fondamentale della stanza non e' il suo arredamento: e' la porta che porta al gabinetto - e, forse, alla privacy. Un'atmosfera casalinga ********************** Il bar di Benny e' popolato da clienti che si godono il loro pranzo, cosi' non e' un buon posto per cambiare identita'. Comunque, il gabinetto a nord sembra promettente, anche se Benny ha delle regole rigide sul suo uso, e la porta sembra essere chiusa. NOTA DEL TRADUTTORE: abbiamo usato il termine gabinetto per indicare il bagno, una parola che non si riferisce solo all'artifatto di porcellana bianca ma anche al locale che lo ospita. Questo doppio uso risultera' importante piu' avanti. Definiamo la stanza del bar in questa semplice forma: Room bar "Il bar di Benny" with description "Benny offre la MIGLIORE selezione di pezzi dolci e sandwich. I clienti riempiono il bancone dove Benny in persona riesce a servire, cucinare e riscuotere senza la minima esistazione. Sulla parete nord del bar vedi una porta rossa che conduce al gabinetto."; s_to strada, n_to porta_del_gabinetto; Vedremo meglio l'ultima riga (n_to porta_del_gabinetto) piu' tardi, quando definiremo l'oggetto porta che giace tra il bar e il gabinetto. Abbiamo menzionato un bancone. Appliance bancone "bancone" bar with name 'bancone' 'banco', article "il", description "Il bancone è fatto con una strabiliante LEGA di metalli, a PROVA di briciole e liquidi VERSATI e FACILE da pulire. I clienti si godono i loro spuntini con ESTREMA tranquillità, sicuri sapendo che il bancone può resistere a tutto.", has supporter; E dei clienti. Questi sono trattati come PNG, che reagiscono alla performance del nostro eroe. Object clienti "clienti" bar with name 'clienti' 'persone' 'cliene' 'gente' 'uomini' 'donne', description [; if (costume has worn) "La maggior parte sembra concentrarsi sul proprio cibo, ma alcuni ti osservano blaterando. Deve essere colpa dei colori IPNOTIZZANTI-INSTUPIDENTI del tuo costume."; else "Un groppo di INERMI e IGNARI mortali, gli stessi che Capitan FATO ha giurato di DIFENDERE il giorno in cui i suoi genitori si sono soffocati con una MALIGNA fetta di TORTA DI MIRTILLI."; ], life [; Ask,Tell,Answer: if (costume has worn) "La gente sembra NON FIDARSI dell'asspetto del tuo FAVOLOSO costume."; else "Come John Covarth, sei MENO interessante del cibo di Benny."; Kiss: "Non saprei dirti quali tipi di batteri MUTANTI questi STRANIERI stanno portando."; Attack: "L'insensato massacro di civili è una caratteristica dei CATTIVI. Si SUPPONE che tu protegga le persone come queste."; ], orders [; "Queste persone non sembrano essere cooperative."; ], numero_di_commenti 0, ! for counting the customer comments daemon [; if (location == bar && random(2) == 1) { self.numero_di_commenti = self.numero_di_commenti + 1; switch (self.numero_di_commenti) { 1: "^~Non sapevo ci fosse il circo in città,~ dice un clienti ad un altro. ~Sembra che i pagliacci abbiano il giorno libero.~"; 2: "^~Questi stilisti non sanno più che fare per farsi conoscere,~ sbuffa un signore corpulento guardando nella tua direzione. Quelli che hanno sentito cercano di nascondere il sogghigno."; 3: "^~Deve essere di nuovo carnevale,~ dice un uomo a sua moglie, che sogghigna dandoti un'occhiata di sfuggita. ~Come vola il tempo...~"; 4: "^~La cosa peggiore delle grandi città~, commenta qualcuno parlando con il suo compagno di tavolo, ~è che vedi gli insetti più schifosi uscire dai cessi.~"; 5: "^~VORREI davvero poter andare al lavoro in pigiama,~ dice una ragazza in tailleur ai suoi colleghi. ~E' COSI' comodo.~"; default: StopDaemon(self); } } ], has scenery animate pluralname; Andiamo passo passo. Il nostro eroe entra nel bar vestito come John Covarth, ma eventualmente riuscira' a cambiarsi nel gabinetto, e quindi dovra' attraversare nuovamente il bar pre raggiungere la strada e risolvere il gioco. La descrizione dei clienti prende in considerazione cosa sta indossando il giocatore. In "Guglielmo Tell" abbiamo visto una piccola manifestazione della proprieta' life, ma ora la estenderemo un po'. Come abbiamo gia' spiegato, life ti permette di intercettare quelle azioni particolarmente dirette a oggetti animati. Qui noi intercettiamo Attack e Kiss per offrire qualche messaggio personalizzato per queste azioni quando vengono rivolte verso i clienti. Ancora evitiamo ogni conversazione intercettando Ask, Tell e Answer, in modo da produrre un messaggio che dipende dall'abbigliamento del personaggio. Un'altra caratteristica degli oggetti animati e' la possibilita' di ricevere ordini: BILL, AGITA LA LANCIA o ANNIE, PRENDI LA PISTOLA. Queste azioni sono gestite con la proprieta' orders e, cosi' come avviene per la proprieta' life, la routine incapsulata puo' diventare parecchio complessa se vogliamo che i nostri PNG si comportino in modo interessante. In questo caso, non abbiamo bisogno che gli avventori svolgano alcun compito per noi, cosi' invece forniamo un semplice messaggio di diniego, giusto nel caso in cui il giocatori provi a dare ordini in giro. Questo ci lascia solanto la parte relativa al demone (daemon). Un demone e' una proprieta' usata normalmente per eseguire azioni temporizzate o ripetitive senza il bisogno della diretta interazione da parte del giocatore; ad esempio, macchine che funzionano da sole, animali che si muovono, o persone che vanno dietro ai propri affari. In modo piu' efficace, un demone puo' tener conto delle decisioni del giocatore in un cento momento, permettendo un comportamento interattivo; questa e', comunque, una caratteristica avanzata che non useremo in questo esempio. Un demone ha la sua opportunita' di fare qualcosa alla fine di ogni turno, e tipicamente opera sull'oggetto o con l'oggetto a cui e' associato. Nel nostro esempio, il demone produce commenti maligni e ironici da parte dei clienti una volta che il nostro eroe e' uscito dal gabinetto vestito con il costume da Capitan Fato. Per creare un demone, avete bisogno di tre cose: 1. Anzitutto definite una proprieta' daemon nel corpo dell'oggetto; il valore di questa proprieta' e' sempre una routine incapsulata. 2. Comunque i demoni non fanno nulla fino a che non li attivate. Questo e' facilmente svolto dall'istruzione StartDaemon(identificatore_oggetto), che puo' essere messa in qualsiasi punto (se voltete che il demone di un certo oggetto sia attivo dall'inizio del gioco, mettete l'istruzione nella routine Initialise). 3. Quando il demone ha svolto il suo compito (se cio' accade), potete fermarlo con l'istruzione StopDaemon(identificatore_oggetto). In particolare come funziona il nostro demone? Noi vogliamo che gli avventori facciano commenti ironici quando vedono il Capitano in costume, ma non in modo completamente prevedibile. if (random(2) == 1) ... Random e' una routine di inform usata per generare numeri casuali o per scegliere casualmente tra piu' possibilita'; nella forma random(espressione) restituisce un numero casuale tra 1 e il valore dell'espressione tra parentesi (inclusa). Cosi' la nostra condizione attualmente dice: se un valore casuale scelto tra 1 e 2 vale 1, allora esegui qualcosa. Ricordatevi che un demone viene eseguito una volta alla fine di ogni turno, cosi' la condizione prova a tirar fuori un commento da un'avventore approssimativamente un turno si' e uno no. Dopo procediamo come abbiamo gia' visto in "Guglielmo Tell", con un'istruzione switch per stampare in ordine i messaggi della sequenza usando acutamente una nostra proprieta' locale, numero_di_commenti. Abbimo scritto solo cinque messaggi (potevano essere cento o uno solo) e dopo raggiungiamo il caso generico default, che e' un buon posto per fermare il demone, visto che non abbiamo piu' commenti salaci dei clienti da visualizzare. Ah, ma quando inizia a funzionare il demone? Appena il nostro protagonista esce fuori dalla toilette vistito nel suo pigiama multicolore da supereroe. Siccome vogliamo minimizzare i possibili stati del gioco, adotteremo qualche regola generale per evitare problemi: (a) il giocatore puo' cambiarsi soltanto nel gabinetto, (b) non permetteremo al giocatore di riindossare i suoi abiti normali, e (c) una volta che il giocatore riesce a uscire in strada con indosso il costume, il gioco e' vinto. Cosi' possiamo sicuramente supporre che se il giocatore entra nel bar con il costume addosso, sicuramente proviene dal gabinetto. Come conseguenza di tutto cio', possiamo cambiare la descrizione del bar: Room bar "Il bar di Benny" with description [; print "Benny offre la MIGLIORE selezione di pezzi dolci e sandwich. I clienti riempiono il bancone dove Benny in persona riesce a servire, cucinare e riscuotere senza la minima esistazione. Sulla parete nord del bar vedi una porta rossa che conduce al gabinetto."; if (costume has worn && self.appenauscito == false) { self.appenauscito = true; StartDaemon(clienti); print "^^I clienti osservano il tuo costume con evidente curiostità."; } new_line; ], appenauscito false, ! Prima apparizione di Capitan Fato? ... Questa routine visualizza sempre la prima stringa "Benny offre la migliore...", e dopo controlla se il giocatore sta indossando il costume, nel qual caso abbia il demone dell'oggetto clienti e visualizza un'altro messaggio: "I clienti osservano...". L'uso della proprieta' locale appenauscito ci garantisce che la condizione risulti vera una sola volta, in modo che che il blocco di istruzioni ad essa associato venga eseguito una volta sola. Finalmente troviamo l'istruzione new_line. Questa effettua soltanto un ritorno a capo alla fine della routine - in effetti funziona esattamente come print "^". Siccome abbiamo due istruzioni print, e una di queste viene visualizzata una volta sola, il ritorno a capo sistema il testo in entrambe le situazioni: Il bar di Benny Benny offre la MIGLIORE selezione di pezzi dolci e sandwich. I clienti riempiono il bancone dove Benny in persona riesce a servire, cucinare e riscuotere senza la minima esistazione. Sulla parete nord del bar vedi una porta rossa che conduce al gabinetto. <- RITORNO A CAPO > e Il bar di Benny Benny offre la MIGLIORE selezione di pezzi dolci e sandwich. I clienti riempiono il bancone dove Benny in persona riesce a servire, cucinare e riscuotere senza la minima esistazione. Sulla parete nord del bar vedi una porta rossa che conduce al gabinetto. I clienti osservano il tuo costume con evidente curiostità. <- RITORNO A CAPO > Abbiamo finito con i clienti nel bar. Ora abbiamo il gabinetto a nord che, per ragioni di gioco e di decenza, e' protetto da una porta. Una porta da adorare ******************** Gli oggetti porta richiedono certe specifiche proprieta' e certi specifici attributi. Anzitutto creiamo una porta semplice: Object porta_del_gabinetto "porta del gabinetto" bar with name 'porta' 'rossa' 'del' 'gabinetto' 'bagno', description "Una porta rossa con le inequivocabili silhouette di un uomo e di una donna che segnano l'ingresso ai locali igienici. C'è una nota scarabocchiata attaccata alla superficie della porta.", door_dir n_to, door_to gabinetto, with_key chiave_del_gabinetto, has scenery door openable lockable locked female; Troviamo questa porta nel bar. Dobbiamo specificare in che direzione la porta conduce ovvero, come abbiamo specificato nella descrizione del bar, nord. La proprieta' door_dir serve proprio a questo, e in questo caso prende il valore della proprieta' di direzione nord, n_to. Dopo dobbiamo dire a Inform quale e' la stanza che si trova dietro la porta, grazie alla proprieta' door_to che assume il valore gabinetto, stanza che andra' definita in seguito. Ricordate la connessione del bar verso nord, n_to porta_del_gabinetto? Grazie ad essa, Inform sapra' che la porta blocca la strada in quella direzione e, grazie alla proprieta' door_to, anche cosa si trova oltre la porta. Le porte devono avere l'attributo door, ma oltre a quello abbiamo anche un pacco di opzioni che ci aiutano a definire esattamente con che tipo di porta abbiamo a che fare. Cosi' come i contenitore, le porte possono essere apribili (attributo openable, che attiva i verbi APRI e CHIDI in modo che possano essere applicato all'oggetto) e, siccome di default esse sono chiuse, se volete altrimenti potete dagli l'attributo open (aperto). Addizionalmente le porte possono essere bloccabili (cioe' chiudibili a chiave, grazie all'attributo lockable che attiva i verbi BLOCCA/SBLOCCA e le frasi APRI...CON/CHIUDI...CON) e possono essere rese bloccate (attributo locked) visto che di default non lo sono. I verbi BLOCCA, SBLOCCA, APRI...CON e CHIUDI...CON hanno bisogno di un oggetto chiave che funzioni con la porta. Questo viene definito con la proprieta' with_key, il cui valore deve essere l'identificatore interno della chiave; nel nostro esempio, e' l'oggetto chiave_del_gabinetto, che definiremo in seguito. Se non fornite questa proprieta' il giocatore non sara' in grado di bloccare e sbloccare la porta. Questa semplice definizione di porta ha un problema, ovvero, esiste soltanto nel bar. Se volete che la porta sia presente anche nel gabinetto potete: (a) definire un'altra porta nel gabinetto, o (b) rendere questa porta una porta a doppia faccia. La soluzione (a) sembra superficialmente semplice, ma poi avrete il problema di tenere lo stato delle due porta (aperta/chiusa, bloccata/sbloccata) in sincronia. In questo scenario, dove potete accedere alla toilet solo attraverso questa porta, non dovrebbe essere troppo complicato, visto che potete lasciare la porta che si trova nel bar sempre aperta, indipendentemente da quello che il giocatore fa con la porta che si trova nel gabinetto, e viceversa - le due porte non verranno mai viste allo stesso momento. In termini piu' generali pero' l'inconsistenza tra le due porte comporta solo problemi; nella maggior parte dei casi e' meglio ignorare la soluzione (a). La soluzione (b) e' migliore, visto che avete solo un oggetto porta da gestire, e il suo stato influenza entrambe le sue facce. Comunque il codice diventa un po' piu' complicato, e dovete definire delle routine per la maggior parte delle proprieta': Object porta_del_gabinetto "porta del gabinetto" with name 'porta' 'rossa' 'del' 'gabinetto' 'bagno', description [; if (location == bar) "Una porta rossa con le inequivocabili silhouette di un uomo e di una donna che segnano l'ingresso ai locali igienici. C'è una nota scarabocchiata attaccata alla superficie della porta."; else "Una porta rossa senza alcuna caratteristica NOTEVOLE."; ], found_in bar gabinetto, door_dir [; if (location=bar) return n_to; else return s_to; ], door_to [; if (location=bar) return gabinetto; else return bar; ], with_key chiave_del_gabinetto, has scenery door openable lockable locked female; Anzitutto, la porta ora ha bisogno di una proprieta' found_in, visto che dovra' trovarsi sia nel bar che nel gabinetto. La descrizione controlla da quale della porta stiamo guardanto - controllando il valore della variabile location, che contiene la stanza in cui si trova il giocatore - poiche' abbiamo una nota affissa su di un lato, ma non sull'altro. E le proprieta' door_dir e door_to devono usare lo stesso trucco, poiche' vogliamo muoversi verso nord dal bar fino al gabinetto, ma verso sud dal gabinetto al bar. Adesso il gioco visualizzera' "La porta del gabinetto" ogni volta che ha bisogno di riferirsi a tale oggetto. Sarebbe carino se potessimo far si' che il gioco distinguess tra "la porta che conduce al gabinetto" e "la porta che conduce al bar", a seconda del lato della porta da cui ci troviamo. Per questo abbiamo bisogno della proprieta' short_name. Abbiamo gia' parlato del nome esterno definito nella prima riga di un oggetto: Object porta_del_gabinetto "porta del gabinetto" Quel "porta del gabinetto" sara' il nome visualizzato durante il gioco per riferirsi alla porta. Con lo stesso identico effetto avremmo potuto scrivere: Object porta_del_gabinetto with short_name "porta del gabinetto", short_name e' una proprieta' che fornisce il nome esterno di un oggetto, o come stringa o come routine incapsulata. Normalmente, gli oggetti mantengono lo stesso nome esterno durante tutto il gioco - e il metodo di tenere l'informazione nella prima riga e' perfetto in questo caso - ma se e' necessario cambiarlo, e' facile scrivere una routine come valore di short_name: Object porta_del_gabinetto with name 'porta' 'rossa' 'del' 'gabinetto' 'bagno', short_name [; if (location == cafe) print "porta verso il gabinetto"; else print "porta verso il bar"; return true; ], description [; ... Notate il return true alla fine della routine. Vi ricorderete che la regola standard dice "restiture false per continuare, true per interferire e interrompere la normale esecuzione". Nel caso di short_name, "continuare" significa "visualizzare il nome esterno che si trova nella prima riga", cosa che puo' talvolta tornare utile; ad esempio, potreste scrivere una routine short_name per stampare qualcosa da una lista prima del nome esterno - come una splendente/debole/inutile lampada. NOTA: cosa viene visualizzato se non c'e' un nome esterno nella prima riga dell'oggetto? Se avete letto la sezione "Compilazione in corso d'opera" nell'appendice C, ricoredere che l'interprete usa semplicemente l'identificatore interno tra parentesi; ovvero, senza nome esterno e senza proprieta' short_name, potremmo vedere: Apri la (porta_del_gabinetto). E lo stesso principio si appica se dimentichiamo di restituire false dalla routine short_name: otterremmo prima il risultato della nostra istruzione print, e dopo la regola standard visualizzerebbe l'identificatore interno: Apri la porta del gabinetto(porta del gabinetto). Le porte possono diventare piu' complicate di cosi' (no, per favore, non scagliate questa guida fuori dalla finestra). Ecco un po' di codice opzionale deluxe per rendere la porta un po' piu' amichevole durante il gioco; potete saltarlo se prevedete mal di testa. La nostra porta si comporta bene durante il gioco. Puo' essere bloccata e sbloccata se il giocatore ha la chiave giusta; puo' essere aperta e chiusa. Una sequenza di comandi per entrare nel gabinetto e chiudersi la porta alle spalle sarebbe: APRI LA PORTA CON LA CHIAVE, APRI LA PORTA, VAI A NORD, CHIUDI LA PORTA, CHIUDI LA PORTA CON LA CHIAVE Quando abbiamo finito torniamo nel ba: APRI LA PORTA CON LA CHIAVE, APRI LA PORTA, SUD, CHIUDI LA PORTA, CHIUDI LA PORTA CON LA CHIAVE Questo gioco ha una sola porta, ma se ne avesse tre o quattro il giocatore si scoccerebbe (almeno) dovendo scrivere cosi' tanti comandi solo per attraversare una porta. Cio' e quello che viene normalmente detto "design scarso", perche' il gioco si rallenta solo per effettuare una semplice azione che non nasconde segreti o sorprese. Quanto puo' essere eccitante attraversare una normalissima porta, dopo tutto? Se qualche linea di codice puo' rendere la vita piu' facile per il giocatore, allora ne vale la pena. Aggiungiamo qualche miglioria alla nostra porta del gabinetto nelle proprieta' before e after: before [ ks; Open: if (self hasnt locked || chiave_del_gabinetto notin player) return false; ks = keep_silent; keep_silent = true; ; keep_silent = ks; return true; Lock: if (self hasnt open) return false; print "(prima chiudi ", (the) self, ")^"; ks = keep_silent; keep_silent = true; ; keep_silent = ks; return false; ], after [ ks; Unlock: if (self has locked) return false; print "Apri ", (the) self, ".^"; ks = keep_silent; keep_silent = true; ; keep_silent = ks; return true; ], L'idea di base qui e' quella di permettere al giocatore che ha in mano la chiave di eseguire una sola azione per sbloccare e aprire la porta (e quindi anche per chiuderla e bloccarla). Le azioni rilevanti sono Unlock e Open, e Lock (Close non e' necessario; se il giocatore chiude la porta non possiamoassumere che voglia chiuderla a chiave). - Open: se la porta non e' bloccata o il giocatore non e' in possesso della chiave, allora procediamo con l'azione Open standard definita dalla libreria. Ci rimane il caso della porta bloccata e il giocatore che possiede la chiave, cosi' rimandiamo l'elaborazione all'azione Unlock, dando come argomenti la porta (self) e la chiave del gabinetto. Siccome usiamo solo una parentesi angolata per lato <...>, l'azione riprende dopo che la porta e' stata sbloccata (notate che l'azione Unlock si prende cura anche di aprire la porta). Finalmente restituiamo true per far si' che la libreria non provi ad aprire la porta. - Lock: se la porta e' gia' chiusa, allora procediamo con l'azione Lock standard di libreria. Altrimenti diciamo al giocatore che stiamo chiudendo la porta per lui, rimandiamo brevemente l'esecuzione all'azione Close per chiuderla e poi restituiamo false in modo che l'azione Lock proceda normalmente. - Unlock: piazziamo questa azione della proprieta' after cosi' (speriamo) l'azione Unlock e' gia' accaduta. Se la porta e' sempre bloccata allora e' successo qualcosa di sbagliato, cosi' restituiamo falso per visualizzare il messaggio standard per lo sbloccaggio non riuscito. Altrimenti la porta ora e' sbloccata, cosi' informiamo il giocatore che stiamo aprendo la porta e rimandiamo l'esecuzione all'azione Open per effettivamente aprirla, restituendo true per sopprimere il messaggio standard. In tutto cio' c'e' una variabile di libreria chiamata keep_silent, che puo' assumere i valori false (lo stato normale) o true; quando ha il valore true, l'interprete non visualizza il messaggio relativo all'azione in corso, cosi' possiamo evitare cose come: > APRI LA PORTA Apri la porta del gabinetto. Sblocchi la porta del gabinetto e la apri. Anche se vogliamo assegnare a keep_silent il valore true per tutta la durata delle nostre routine, dobbiamo poi risistemarla subito dopo. In un caso come questo e' buona pratica preservare il suo valore iniziale (che probabilmente era false, ma dovreste sempre evitare presunzioni pericolose); usiamo una variabile locale ks per ricordare il valore iniziare, cosi' possiamo ripristinarla dopo. Ricordere che una variabile locale in una routine standalone e' dichiarata tra il nome della routine e il punto e virgola: [ GiaStatoQui questa_stanza; Nello stesso modo, una varabile locale in una routine incapsulata e' dichiarata tra il simbolo [ iniziale e il punto e virgola: before [ ks; Potete dichiarare in questo modo - separandole con spazi - fino a 15 variabili che saranno usabili solo nella routine incapsulata. Quando assegnamo loro un valore, come: ks = keep_silent; stiamo rendendo il valore di ks identico a qualsiasi valore abbia keep_silent (sia esso true o false; non ce ne importa). Dopo assegnamo a keep_silent il valore true, facciamo le nostre azioni silenziose e assegnamo: keep_silent = ks; che riassegna a keep_silent il valore originalmente memorizzato in ks. L'effetto e' che siamo riusciti a lasciare keep_silent esattamente com'era prima che la cambiassimo. Bene, questo e' tutto a proposito delle porte. Tutto? Beh, no, non esattamente; ogni oggetto puo' diventare tanto complesso quando lo permette la tua immaginazione, ma noi abbandoneremo l'argomento qui. Se volete vedere porte piu' sofisticate, controllate gli esercizi 3 e 4 nell'Inform Designer's Manual, dove una porta si apre e si sblocca da sola se il giocatore si muove nella sua direzione. Fino ad ora abbiamo il giocatore di fronte ad una porta chiusa a chiave che conduce al gabinetto. Un vicolo cieco? No, la descrizione menziona una nota scarabocchiata attaccata alla porta. Questa non dovrebbe creare problemi: Object "nota" bar with name 'nota' 'scarabocchiata', description [; if (self.letta == false) { self.letta = true; "Rivolgi la tua visione a ULTRAFREQUENZA AVANZATA verso la nota e socchiudi gli occhi concentrandoti, arrendendoti solo quando i bordi della nota iniziano ad annerirsi sotto l'incredibile intensità del tuo sguardo infuocato. Rifletti ancora una volta su quanto sarebbe stato utile se tu avessi mai imparato a leggere. ^^Una vecchia signora premurosa si avvicina e ti spiega: ~Devi chiedere la chiave a Benny, al bancone.~^^ Ti volti verso di lei e inizi a dire: ~Oh, la SAPEVO, ma...~^^ ~Di nulla, figliolo,~ dice la signora mentre esce dal bar."; } else "La nota indecifrabile e annerita non ha più SEGRETI per te ADESSO. Ha!"; ], letta false, ! il giocatore ha già letto la nota? before [; Take: "Non hai motivo di raccogliere note INDECIFRABILI."; ], has scenery female; Notate soltanto come cambiamo la descrizione dopo la prima volta che il giocatore esamina la nora, usando la proprieta' locale 'letta' creata proprio per questo scopo. Non vogliamo che il giocatore se ne vada via con la nota, cosi' intercettiamo l'azione Take e visualizziamo qualcosa di piu' adatto del messaggio standard per gli oggetti di scenografia: "E' difficilmente trasportabile". Abbiamo parlato un sacco della chiave del gabinetto; dovrebbe essere il momento per scrivere il codice che la riguarda. Originariamente la chiave e' in possesso di Benny, e il giocatore dovra' chiederla, cosi' come spiega la nota. Anche se definiremo Benny in dettagli nel prossimo capitolo, presentiamo una definizione di base, in modo che la chiave abbia un oggetto genitore. Object benny "Benny" bar with name 'benny', description "Un uomo ingannevolmente GRASSO dotato di incredibile agilità, Benny intrattiene i clienti schiacciandosi noci di cocco sulla fronte quando è dell'umore giusto.", has scenery animate male proper transparent; Object chiave_del_gabinetto "chiave del gabinetto" benny with name 'chiave' 'del' 'gabinetto' 'bagno', article "la", invent [; if (vestiti has worn) print "la chiave CRUCIALE"; else print "la chiave ormai usata e IRRILEVANTE"; return true; ], description "I tuoi sensi ULTRA-PERCETTIVI non individuano niente di particolare sulla chiave del gabinetto.", before [; if (self in benny) "SCANDAGLI i l'ambiente con la tua CONSAPEVOLEZZA POTENZIATA, ma non riesci ad individuare alcuna chiave."; ] has female; Finche' Benny ha la chiave, non c'e' logicamente modo di esaminarla (o di compiere qualsiasi azione su di essa), ma non vogliamo che l'interprete sostenga che "Non puoi vedere niente del genere". Abbiamo creato la chiave_del_gabinetto come oggetto figlio dell'oggetto benny, e potete vedere che Benny ha l'attributo transparent; questo significa che la chiave e' "in scope" (a portata di mano), e questo autorizza il giocatore a riferirsi ad essa senza che l'interprete se ne lamenti. Siccome Benny ha anche l'attributo animate, l'interprete normalmente intercetterebbe l'azione PRENDI LA CHIAVE con un "Sembra appartenere a Benny"; comunque la stessa cosa non varrebbe per altri comandi come TOCCA LA CHIAVE o ASSAGGIA LA CHIAVE. Cosi', per prevenire ogni interazione con la chiave fino a che e' nelle tasche di Benny, definiamo una proprieta' before. before [; if (self in benny) "SCANDAGLI i l'ambiente con la tua CONSAPEVOLEZZA POTENZIATA, ma non riesci ad individuare alcuna chiave."; ] Tutte le proprieta' before che abbiamo creato fino ad ora contenevano una o piu' etichette per specificare l'azione che dovevano intercettare; ricorderete che in "Guglielmo Tell" abbiamo introdotto l'azione defualt (Vedi "Una classe per le scenografie" nel capitolo 6) per significare "ogni valore ancora non trattato". Qui, invece, le cose sono piu' semplici: visto che vogliamo interpretare tutto, possiamo fare direttamente a meno delle etichetti; il nostro codice verra' eseguito all'inizio di ogni azione diretta verso la chiave. Se e' sempre in possesso di Benny, mostreremo un cortese diniego; altrimenti l'azione continuera' normalmente. Qui c'e' un'altra piccola innovazione: la proprieta' di libreria invent (non l'abbiamo fatta noi) che vi permette di controllare come gli oggetti appaiano nell'inventario, piuttosto che nel modo normale. Lasciato a se stesso, l'interprete visualizza semplicemente il nome esterno dell'oggetto, preceduto o da un articolo indeterminato standard come "un", "una" o "dei", o un articolo specificatamente definito nella proprieta' article. Qui noi rimpiazziamo "la chiave del gabinetto" con una di due piu' utili descrizioni, rendendola l'oggetto piu' prezioso nelle mani di John Covarth, e qualcosa di cui liberarsi velocemente per Capitan Fato, una volta che non e' piu' di alcun uso per lui. Quando avevamo il giocatore in strada, abbiamo affrontato il problema che egli poteva voler esaminare il bar dall'esterno. Anche se e' improbabile che provi a esaminare il gabinetto dall'esterno, ci vuole poco sforzo per fornire un risultato adatto: Object fuori_dal_gabinetto "gabinetto" bar with name 'gabinetto' 'cesso' 'toilette' 'toilet' 'ritirata' 'bagno', before [; Enter: if (porta_del_gabinetto has open) { PlayerTo(gabinetto); return true; } else "La tua SUPERBA mente deduttiva comprende che la PORTA è chiusa."; Examine: if (porta_del_gabinetto has open) "Un pensiero brillante illumina il tuo cervello SUPERLATIVO: una dettagliata esplorazione del gabinetto sarebbe ESTREMAMENTE facilitata se tu entrassi all'interno."; else "Con un TREMENDO sforzo di volontà, evochi la tua imperscrutabile VISIONE ASTRALE e la proietti in AVANTI attraverso la porta chiusa... fino a che non ti ricordi che è il Dottor Mystere ad avere i poteri mistici."; Open: <>; Close: <>; Take,Push,Pull: "Sarebbe PARTE dell'edificio."; ], has scenery openable enterable; Come per l'oggetto fuori_dal_bar, intercettiamo l'azione Enter, per teletrasportare il giocatore dentro la stanza gabinetto se egli scrive ENTRA NEL GABINETTO (o per stampare un rifiuto se la porta del gabinetto e' chiusa). Il giocatore puo' provare a scrivere ESAMINA IL GABINETTO; otterra' un differente messaggio se la porta e' aperta - lo inviteremo ad entrare - o se e' chiusa. I comandi APRI IL GABINETTO e CHIUDI IL GABINETTOI sono rediretti alle azioni Open e Close della porta del gabinetto; ricordate che le doppie parentesi angolate implicano un'istruzione return true, cosi' l'azione si ferma li' e l'interprete non prova a Apreire o Chiudere l'oggetto fuori_dal_gabinetto dopo che ha agito sulla porta. Avete ragione: il gabinetto ha una parte importante in questo gioco (diamo la colpa a infantili influenze materne). Abbiamo introdotto un problema di ambiguita' con l'oggetto fuori_dal_bagno, e ora abbiamo bisogno d'aiuto per sistemarlo.