I D R AIpertesto Dinamico per Racconti d'AvventuraEstensioni per oggetti e inventariorev 1.0, © 2000 Enrico Colombini |
Per impiegare oggetti.js
e per capire a fondo questo manuale è necessario innanzitutto
conoscere adeguatamente Idra, per il quale rimandiamo alla
lettura della relativa documentazione.
Queste righe vanno inserite in mezzo tra le corrispondenti righe riferite a idra.js e quelle riferite a gioco.js.<!-- include le estensioni per gli oggetti -->
<script language="JavaScript1.1" src="oggetti.js" type="text/javascript"></script>
Infine, va fatta una copia del file area.html, denominandola area2.html, sempre all'interno della cartella Dati.<frameset cols="95,*" border=0>
<frame src="Dati/ctrl.html" name="ctrl" marginwidth=12 scrolling=no>
<frameset rows="*,90" border=0>
<frame src="Dati/area.html" name="area" marginwidth=20>
<frame src="Dati/area2.html" name="area2" marginwidth=20>
</frameset>
</frameset>
Per effetto di queste modifiche, l'area di lettura originale viene suddivisa in due riquadri: "area" e "area2": la prima è sempre l'area di lettura, mentre la seconda è destinata a contenere l'inventario, cioè l'elenco degli oggetti posseduti:
|
L'aspetto complessivo della finestra è naturalmente modificabile
a piacere agendo sulle istruzioni contenute nel file ctrl.html, ad esempio per cambiare
dimensione o posizione relativa dei riquadri (frame), o per scegliere
il colore del frame di controllo (quello degli altri due si controlla
dal programma in gioco.js).
Per indicare quali di queste variabili corrispondano a oggetti che devono apparire nell'inventario, subito dopo avere definito le variabili stesse si deve chiamare la funzione definisciOggetti(), in questo modo:v.spada = 0
v.coppa = 0
v.chiave = 0
v.strega = 0
v.amuleto = 0
v.drago = 0
Ciascuna coppia di stringhe consiste nel nome di una variabile (senza la "v.") e nella corrispondente descrizione che verrà presentata al lettore.definisciOggetti(new Array(
"spada", "Una spada di cristallo",
"coppa", "La Coppa del Limbo",
"chiave", "Una minuscola chiave"
"amuleto", "Un ciondolo di ossidiana"))
Le variabili elencate nella definisciOggetti() diventano speciali: se il loro valore è maggiore di zero (di solito si usa 1, ma ogni altro valore è valido), la corrispondente descrizione verrà mostrata nell'inventario.
Affinché ciò accada in modo automatico, occorre inserire al termine della funzione PiePagina() la riga:
La funzione PiePagina() andrà ovviamente aggiunta nel caso non fosse già presente in gioco.js, come illustrato nel manuale di Idra e nel gioco.js allegato.mostraOggetti("")
l'inventario mostrerà:v.spada = 0
v.coppa = 1
v.chiave = 0
v.strega = 1
v.amuleto = 1
v.drago = 0
La Coppa del Limbo Un ciondolo di ossidiana |
Le descrizioni della coppa e dell'amuleto sono mostrate perché il valore delle corrispondenti variabili, rispettivamente v.coppa e v.amuleto, è maggiore di zero. Quelle della spada e della chiave non sono mostrate perché il valore della corrispondente variabile è zero.
Strega e drago non sono invece oggetti attivi, in quanto le corrispondenti variabili non sono state in precedenza elencate nella chiamata a definisciOggetti(), quindi il loro valore non ha effetto sull'inventario.
L'inventario viene automaticamente aggiornato a ogni nuova presentazione di pagina, inclusa quella della pagina stessa ottenuta con aggiorna(), per cui esso riflette sempre correttamente lo stato delle variabili: se il valore di una di esse diventa positivo appare la corrispondente descrizione, e viceversa sparisce se diventa zero o negativo.
Il lettore può selezionare un oggetto facendo clic sulla relativa descrizione nell'inventario; la descrizione verrà evidenziata (mostriamo qui la forma più semplice, l'aspetto dell'inventario è personalizzabile come il resto di Idra):
La Coppa del Limbo Un ciondolo di ossidiana |
La selezione di un oggetto non ha alcun effetto immediato, ma
può modificare l'effetto del clic su un rinvio (link)
nell'area principale di lettura, in base alle scelte dell'autore.
È però di solito più comodo usare la funzione rinvioOgg(), che nella sua versione più semplice funziona come la rinvio() di Idra, ossia inserisce un rinvio nel testo della pagina; ad esempio, scrivendo:if (ogg() == "spada") { ... }
si otterrà come al solito:testo("Vedi un getto d'")
rinvioOgg("acqua", Beve)
testo(" che zampilla dalla roccia)
vedi un getto d'acqua che zampilla dalla roccia. |
Si possono però aggiungere altri argomenti alla rinvioOgg(), per indicare l'azione da compiere nel caso che un certo oggetto sia selezionato quando il lettore fa clic sul rinvio:
All'apparenza nulla è cambiato, ma l'effetto di un clic sul rinvio dipende dall'oggetto selezionato: se è la coppa si va alla pagina Riempie(), se è la spada si passa alla pagina Incanta(), altrimenti alla pagina Beve(). Per migliore leggibilità può convenire allineare le coppie nome-azione:rinvioOgg("acqua", Beve, "coppa", Riempie, "spada", Incanta)
Per semplicità abbiamo indicato come azioni delle semplici pagine da mostrare, ma come sempre in Idra si possono indicare anche istruzioni da eseguire; in questo caso esse vanno racchiuse tra virgolette o apici:rinvioOgg("acqua", Beve, //testo e azione di default
"coppa", Riempie, //coppie oggetto-azione
"spada", Incanta)
Va ricordato che il nome di una pagina (come la Beve nella prima riga qui sopra) si può scrivere direttamente come azione solo nel caso in cui non vi siano altre istruzioni, altrimenti bisogna mettere il tutto tra virgolette o apici e usare "vai(Pagina)", come nella terza riga. Da notare l'uso di aggiorna() in seconda riga per far eseguire nuovamente la funzione di pagina dopo la modifica di una variabile.rinvioOgg("acqua", Beve, //testo e azione di default
"coppa", "v.piena = 1; aggiorna()",
"spada", "v.magica = 1; vai(Caverna)")
Infine, si può indicare un'azione da eseguire nel caso che vi sia un oggetto selezionato, ma non si tratti di alcuno di quelli elencati; lo si fa col simbolo "+":
In questo caso, se ad esempio il giocatore ha selezionato l'amuleto, si passerà alla pagina Inutile(). Questa azione speciale può stare in qualsiasi punto della lista, non necessariamente alla fine come nell'esempio.rinvioOgg("acqua", Beve, //testo e azione con nessun oggetto
"coppa", Riempie, //coppie oggetto-azione
"spada", Incanta,
"+", Inutile) //qualsiasi altro oggetto
Un'eventuale azione nulla ("") non ha alcun effetto:
In questo esempio un clic con un oggetto selezionato, ma diverso da quelli elencati, non produce alcuna risposta.rinvioOgg("acqua", Beve, //testo e azione con nessun oggetto
"coppa", Riempie, //coppie oggetto-azione
"spada", Incanta,
"+", "") //qualsiasi altro oggetto
A volte però uno stesso messaggio può essere riutilizzato in diverse pagine, cosa impossibile con la soluzione mostrata perché essa rimanda alla fine a una pagina precisa (Sorgente() nell'esempio).function Beve {
testo("Ahh... rinfrescante!")
continua(Sorgente)
}
Una possibile soluzione consiste nell'impiegare una pagina temporanea da presentarsi con la funzione mostra():
In questo caso, l'azione "mostra(Beve)" potrà essere usata in più pagine differenti, dato che in ogni caso la aggiorna() finale richiamerà la pagina corretta.rinvioOgg("acqua", "mostra(Beve)", ...
...
function Beve {
testo("Ahh... rinfrescante!")
aggiorna()
}
L'uso di aggiorna() anziché ridisegna() causa una nuova eseguzione del codice della pagina da cui si proviene, in contrasto con quanto suggerito nel cap.6 del manuale di Idra; spesso si desidera infatti che un'azione legata a un oggetto abbia qualche conseguenza, nel senso di cambiare il valore di una o più variabili, ed è quindi desiderabile che di ciò si tenga conto al momento di ridisegnare la pagina.
Occorre però fare attenzione a non eseguire operazioni cumulative nella pagina contenente il rinvio, in quanto esse sarebbero ripetute a ciascun aggiornamento della pagina, come in questo caso:
v.miglia += 10
Questo inconveniente, che d'altronde si presenta anche se non si usano le pagine temporanee, si risolve mettendo sempre le operazioni cumulative nell'azione che porta a una pagina, anziché nel codice della pagina stessa:
In questo modo, l'operazione di somma viene eseguita una sola volta nel momento in cui si passa alla pagina in questione: essa puograve; venire aggiornata anche più volte senza conseguenze; con questa precauzione si evitano vari possibili problemi.rinvio("Stremato, arrivi infine alla sorgente", "v.miglia += 10; vai(Sorgente)")
Essa crea una pagina di risposta in cui viene presentato il testo t passatole come argomento; ad esempio, eseguendo:function Risp(t, act) {
apriPagina('bgcolor="#ffccff"')
testo("<br> <br>", t)
if (act) { continua(act) } else { continua("aggiorna()") }
chiudiPagina()
}
verrà presentata la pagina:Risp("Ahh... rinfrescante!")
Ahh... rinfrescante! |
In questo caso la Risp() utilizza volutamente un diverso colore di fondo, definito nella chiamata alla apriPagina(), ma lo si può naturalmente cambiare, ad esempio per uniformarlo con le altre pagine.
Si possono anche chiamare le funzioni Intestazione() e PiePagina() dalla stessa Risp(), in modo da presentare queste pagine di risposta esattamente come tutte le altre:
Facendo clic su "Continua" verrà nuovamente presentata la pagina originaria; si può anche indicare un'azione da eseguire in risposta al clic, al posto di aggiorna(), ad esempio nel caso si desideri passare a un'altra pagina dopo il messaggio:function Risp(t, act) {
apriPagina(leMieOpzioniPreferite)
Intestazione()
testo(t)
PiePagina()
if (act) { continua(act) } else { continua("aggiorna()") }
chiudiPagina()
}
Come sempre in Idra, se l'azione non consiste in un semplice cambio di pagina ma ci sono anche istruzioni da eseguire, occorre mettere le istruzioni tra virgolette e usare la funzione vai():Risp("Scivoli sull'erba bagnata...", Fosso)
Poiché questa Risp() è soltanto una delle varie possibili soluzioni per semplificare il lavoro dell'autore, essa non è inclusa in oggetti.js ma è semplicemente fornita a titolo di esempio in gioco.js (e per questo il nome della funzione inizia con una lettera maiuscola).Risp("Scivoli sull'erba bagnata...", "v.caduto = 1; vai(Fosso)")
La M_ iniziale, seguita dal nome della pagina, è una semplice convenzione per assicurarsi di non utilizzare accidentalmente un nome già usato altrove. I messaggi così definiti possono essere facilmente usati in questo modo:var M_fonte1 = "Ahh... rinfrescante!"
var M_fonte2 = "Riempi la coppa fino al colmo."
var M_fonte3 = "La spada sembra per un attimo sciogliersi"
var M_fonte4 = "Ci provi, ma non ottieni nulla."
Si può evitare l'uso delle variabili M_fonte1, M_fonte2, ecc. mettendo direttamente le stringhe nella stessa rinvioOgg(); occorre però in questo caso racchiudere l'intera azione tra apici singoli:function Fonte {
...
rinvioOgg("acqua", "Risp(M_fonte1)",
"coppa", "v.piena = 1; Risp(M_fonte2)",
"spada", "v.incanta = 1; Risp(M_fonte3)",
"+", "Risp(M_fonte4)")
...
}
Questa forma è sintetica ma può creare confusione in chi non abbia una buona esperienza con JavaScript; è inoltre una comune fonte di errori, specie se la stringa comprende apici:rinvioOgg("acqua", 'Risp("Ahh... rinfrescante!")',
"coppa", 'v.piena = 1; Risp("Riempi la coppa fino al colmo.")',
"spada", 'v.incanta = 1; Risp("La spada sembra per un attimo sciogliersi")',
"+", 'Risp("Ci provi, ma non ottieni nulla.")')
L'apostrofo in "L'acqua" viene infatti interpretato come l'apice che chiude la stringa aperta con 'Risp. Per evitarlo occorre precedere apostrofi e virgolette contenuti nel testo con una barra inversa:rinvioOgg("acqua", 'Risp("L'acqua ti rinfresca.")') //Errata!
Purtroppo i browser (in particolare Internet Explorer) danno spesso indicazioni errate o fuorvianti sulla reale causa di errore e sul punto in cui si trova l'errore stesso, per cui occorre prestare molta attenzione ad apici, virgolette e parentesi; conviene scrivere poche righe alla volta e, al limite, disattivare alcune righe trasformandole in commenti (cioè precedendole con //) per meglio identificare quelle errate.rinvioOgg("acqua", 'Risp("L\'acqua ti rinfresca.")') //corretta
l'inventario avrà più o meno questo aspetto:opzioniOggetti('bgcolor="#ffcccc"', "<hr> ", " ... ", "<hr>")
Una spada di cristallo ... Una minuscola chiave |
È infatti disegnato con il colore di fondo indicato, inzia con un separatore orizzontale (<hr>) e uno spazio ( ), separa gli oggetti con " ... " e termina con un altro separatore orizzontale.
Volendo personalizzare le descrizioni degli oggetti, basta definire una funzione DescriviOggetto(): essa verrà chiamata automaticamente per presentare ciascun oggetto nell'inventario, in base al suo nome, alla sua descrizione e al fatto che sia o meno selezionato dal giocatore. Per esempio, scrivendo:
la descrizione dell'oggetto selezionato apparirà in colore #cc0000 e più grande, mentre quella degli altri sarà mostrata in colore #006600 in dimensioni normali:function DescriviOggetto(ogg, desc, selez) {
if (selez) {
testoOggetti('<font color="#cc0000" size="+1">', desc, "</font>")
} else {
testoOggetti('<font color="#006600">', desc, "</font>")
}
}
Una spada di cristallo ... Una minuscola chiave |
Da notare che per scrivere nel riquadro (frame) dell'inventario si usa la funzione testoOggetti(), non la normale testo().
La funzione DescriviOggetto() si può usare anche per presentare immagini invece delle scritte, ad esempio in questo modo:
In questo caso ciascun oggetto sarà rappresentato da un'immagine, letta da un file corrispondente al nome della variabile, ad esempio spada.jpg, che verrà sostituita con spada-s.jpg nel caso che l'oggetto sia selezionato. Per consentire la lettura anche a chi non sia in grado di vedere o scaricare le immagini, il tag <alt> fornisce comunque la descrizione dell'oggetto.function DescriviOggetto(ogg, desc, selez) {
var ifile = ogg //costruisce il nome del file
if (selez) { ifile = ifile + "-s" }
ifile = ifile + ".jpg"
testoOggetti('<img src="', ifile, '" alt="', desc, '">')
}
La funzione DescriviOggetto() si può anche usare per presentare alcuni oggetti in modo speciale, ad esempio quelli dotati di una quantità:
con un risultato di questo tipo:function DescriviOggetto(ogg, desc, selez) {
if (selez) {
testoOggetti('<font color="#cc0000" size="+1">', desc)
} else {
testoOggetti('<font color="#006600">', desc)
}
if (ogg == "soldi") {
testoOggetti(" (", v.soldi, ")") //mostra la quantità
}
testoOggetti("</font>")
}
Una spada di cristallo ... Sonanti cocuzze (12) ... Una minuscola chiave |
Dato che un oggetto viene presentato nell'inventario solo se il
valore della corrispondente variabile (v.) è positivo,
i soldi saranno visibili indipendentemente dalla quantità,
purché essa non sia zero o negativa; i numeri negativi possono
essere utili per conservare informazioni non visibili al lettore.
In questo caso, i rinvii creati con la rinvioOgg() appariranno di colore diverso. Ad esempio, una pagina contenente queste istruzioni:opzioniRinvioOgg('<font color="#cc0099">', '</font>')
verrà mostrata in questo modo:testo("L'unico sentiero torna al ")
rinvio("villaggio", Villaggio)
testo("; al centro della radura vedi un ")
rinvioOgg("albero", ...) //varie risposte dipendenti dall'oggetto
testo(" millenario.")
L'unico sentiero torna al villaggio; al centro della radura vedi un albero millenario. |
Il lettore potrà così capire che il primo link agisce
direttamente, mentre sul secondo converrà probabilmente fare
clic dopo avere selezionato un particolare oggetto.
Non è previsto al momento un modo immediato, per il lettore, di deselezionare tutti gli oggetti, una volta che ne abbia selezionato uno.
Non è possibile agire con un oggetto su un altro degli oggetti posseduti, o comunque combinare gli oggetti.
Se il lettore usa la funzione Indietro del browser, il riquadro degli oggetti potrà temporaneamente indicare come selezionato un oggetto diverso da quello effettivo; il meccanismo continua in ogni caso a funzionare regolarmente (questa operazione va comunque sconsigliata).
Il debugger di Idra non è in grado di mostrare le
azioni associate a una rinvioOgg(): si vede solamente la
chiamata alla relativa funzione di smistamento.
Desidero a questo proposito ringraziare Tommaso Percivale, che mi ha stimolato a scrivere questa estensione e l'ha collaudata sul campo; l'interfaccia verso il lettore è simile a quella del sistema ILES (Interactive Literature Editing System) creato da Alberto Morena e Tommaso Percivale e impiegato nei libri gioco chiamati "IperLibri".
Per contattarmi si può scrivere all'indirizzo sotto elencato; anche in questo caso non sono in grado di garantire una pronta risposta, ma leggo sempre tutti i messaggi che ricevo (tranne le pubblicità e lo spam).
Come sempre, la pagina di riferimento è www.erix.it/idra.html, salvo variazioni sempre possibili nel mutevole universo della Rete.
Enrico Colombini
La sintassi di questa guida è identica a quella dell'appendice
A del manuale di Idra; in particolare, una funzione o variabile
inizia con un lettera maiuscola se è definita dall'autore,
tipicamente in gioco.js.
function DescriviOggetto(oggetto, descrizione, selezionato) {
...
}
oggetto : stringa contenente il nome dell'oggetto da presentare.
descrizione : stringa contenente la descrizione dell'oggetto.
selezionato : vero se l'oggetto è selezionato, falso in caso contrario.Opzionale. Se esiste, viene chiamata automaticamente al momento di scrivere (o comunque presentare, anche con immagini) la descrizione di un oggetto nel riquadro dell'inventario. L'argomento oggetto contiene una stringa corrispondente al nome della variabile che lo controlla, ma senza la v., ad esempio "spada" (senza le virgolette) se la variabile corrispondente è v.spada. Questa funzione consente di personalizzare la presentazione dell'inventario, come nell'esempio completo sotto riportato che impiega la testoOggetti() descritta più avanti (altri esempi più complessi sono riportati nel manuale). Se questa funzione non esiste, l'oggetto selezionato è semplicemente mostrato in grassetto.
function DescriviOggetto(ogg, desc, selez) {
if (selez) {
testoOggetti('<font color="#cc0000" size="+1">', desc, "</font>")
} else {
testoOggetti('<font color="#006600">', desc, "</font>")
}
}
definisciOggetti(elenco)
elenco : un array contenente un numero pari di stringheVa chiamata da gioco.js subito dopo avere definito le variabili, passando come elenco un nuovo array contenente coppie di stringhe nome-descrizione, dove ciascun corrisponde al nome di una variabile, ma senza la v., come in questo esempio completo:
definisciOggetti(new Array(
"spada", "Una spada di cristallo",
"coppa", "La Coppa del Limbo",
"chiave", "Una minuscola chiave"
"amuleto", "Un ciondolo di ossidiana"))
mostraOggetti(oggetto)
oggetto : una stringa che può corrispondere al nome di un oggetto.Va chiamata per aggiornare l'inventario al termine della presentazione di ciascuna pagina, tipicamente come ultima istruzione di PiePagina() come nell'esempio completo sotto riportato. Se il parametro oggetto corrisponde a un oggetto mostrato, esso viene selezionato; normalmente si passa invece una stringa nulla ("") in modo da non selezionare alcun oggetto. Può essere chiamata anche in altre occasioni, se si desidera aggiornare immediatamente il riquadro degli oggetti senza attendere la presentazione di una nuova pagina, oppure per selezionare un determinato oggetto.
function PiePagina(pag) {
...
mostraOggetti("")
}
rinvioOgg(espressione, azione )
rinvioOgg(espressione, azione, oggetto, azione, ..., ... )
espressione : un elemento stampabile: stringa, variabile o altro.
azione : un nome di pagina (senza parentesi) oppure una stringa contenente istruzioni JavaScript.
oggetto : una stringa corrispondente al nome di un oggetto, o la stringa "+".Nella forma più semplice equivale alla normale rinvio(): scrive nella pagina un rinvio (link) col testo espressione che, se attivato con un clic dal lettore, causa l'esecuzione di azione. È possibile indicare azioni da compiere al posto di questa nel caso che sia selezionato un particolare oggetto oppure, usando la speciale stringa "+" come oggetto, nel caso che sia selezionato un oggetto diverso da quelli elencati. Può essere conveniente allineare le coppie oggetto-azione per migliore leggibilità.
rinvioOgg("acqua", Beve)
rinvioOgg("acqua", Beve, "coppa", Riempie, "spada", Incanta)
rinvioOgg("acqua", Beve,
"coppa", Riempie,
"spada", Incanta,
"+", Inutile)
rinvioOgg("acqua", Beve,
"coppa", "v.piena = 1; aggiorna()",
"spada", "v.magica = 1; vai(Caverna)")
ogg()
Ritorna il nome dell'oggetto correntemente selezionato, sotto forma di stringa.if (ogg() == "coppa") { ... }
testoOggetti(espressione)
testoOggetti(espressione1, espressione2, espressione3, ... )
espressione : un elemento stampabile: stringa, variabile o altro.Funziona esattamente come la testo() di Idra, ma scrive nel riquadro (frame) dell'inventario anziché nell'area principale dove viene mostrato il testo della pagina; si usa nella DescriviOggetto() per mostrare la descrizione di un oggetto o far apparire l'immagine corrispondente.
testoOggetti('<font color="#006600">', desc, "</font>")
testoOggetti('<img src="', ifile, '" alt="', desc, '">')
opzioniOggetti(opzioni, intesta, spazia, fine)
opzioni : una stringa contenente opzioni valide per il <body> del riquadro oggetti.
intesta : una stringa contenente comandi HTML da eseguire a inizio riquadro oggetti.
spazia : una stringa contenente comandi HTML da eseguire tra le descrizioni degli oggetti.
fine : una stringa contenente comandi HTML da eseguire a fine riquadro oggetti.Può essere opzionalmente chiamata per modificare il modo in cui gli oggetti vengono presentati, come nell'esempio completo sotto riportato. Se non viene chiamata, il riquadro ha fondo bianco e le descrizioni degli oggetti sono separate da due spazi.
opzioniOggetti('bgcolor="#ffcccc"', "<hr> ", " ... ", "<hr>")
opzioniRinvioOgg(prima, dopo)
prima : una stringa contenente comandi HTML da eseguire prima del testo di un rinvio.
dopo : una stringa contenente comandi HTML da eseguire dopo il testo di un rinvio.Può essere opzionalmente chiamata per modificare il modo in cui i rinvii dipendenti dagli oggetti vengono presentati, come nell'esempio completo sotto riportato. Se non viene chiamata, i rinvii creati con la rinvioOgg() sono visivamente identici a quelli ottenuti con una normale rinvio().
opzioniRinvioOgg('<font color="#cc0099">', '</font>')