IF ITALIA - Il sito Italiano sull'Interactive Fiction
III. OGGETTI
Gli oggetti sono i mattoni per la costruzione di ogni programma Hugo. Qualunque cosa debba essere usata da un giocatore durante il gioco -- comprese le stanze, gli oggetti, altri personaggi, ed anche le direzioni -- debbono essere definiti come oggetti.La definizione base di un oggetto appare come:
object <nomeoggetto> "nome oggetto"
{
...
}
Ad esempio un oggetto valigia potrebbe essere definito come segue:
object valigia "valigia"
{}
Le parentesi graffe sono necessarie anche se la definizione dell'oggetto non ha un corpo. Gli unici dati assegnati all'oggetto valigia sono -- da destra a sinistra -- un nome, un identificatore, e l'appartenenza alla classe base object.Il compilatore assegna all'oggetto identificato da <nomeoggetto> il prossimo numero sequenziale disponibile. Cioè, se il primo oggetto definito è l'oggetto "nothing" [niente] (oggetto 0), allora al successivo oggetto che verrà definito, qualunque esso sia, viene assegnato il numero 1; quello che seguirà sarà il 2, ecc. Tutto questo è accademia, comunque, visto che un programmatore non avrà mai bisogno di sapere che numero di oggetto ha un particolare oggetto -- tranne che in alcune situazioni di debug -- e si può sempre riferire ad un oggetto tramite la sua etichetta <nomeoggetto>.
Se nessuno esplicito "name" (la proprietà nome) è fornito, il compilatore gli assegna automaticamente il nome "(<nomeoggetto>)", cioè <nomeoggetto> tra parentesi.
(Il compilatore crea automaticamente un oggetto chiamato "display" [schermo] come ultimo oggetto definito. L'oggetto display può essere usato per ottenere informazioni riguardanti la visualizzazione dell'interprete. Consultate la sezione sull'oggetto display più avanti in "Funzionalità Avanzate".)
III.a. L'albero degli oggetti
Gli oggetti per avere una posizione all'interno del gioco, ad esempio, essere in una stanza o contenuti in un altro oggetto o accanto ad un altro oggetto, devono occupare una posizione nell'albero degli oggetti. L'albero degli oggetti è una mappa che rappresenta le relazioni che ci sono tra gli oggetti nel gioco. Il numero totale di oggetti si trova nella variabile globale objects.L'oggetto nothing [niente] viene definito dalla libreria come oggetto 0. Questo è la radice dell'albero degli oggetti, sul quale tutti gli oggetti si basano.
Quando si riferisce ai numeri degli oggetti, questo manuale si riferisce generalmente al nome dato all'oggetto nel codice sorgente: ed esempio, <nomeoggetto>. Il compilatore assegna automaticamente ad ogni oggetto un numero di oggetto, e si riferisce a quello ogni volta che uno specifico <nomeoggetto> viene incontrato.
(NOTA: Quando si usano le routine standard della libreria, assicuratevi che nessun oggetto (o classe, che verrà discussa in seguito) sia definito prima dell'inclusione di HUGOLIB.H. Potrebbero sorgere dei problemi se il primo oggetto definito -- l'oggetto 0 -- non è l'oggetto "nothing").
Questo è un esempio di albero degli oggetti:
Nothing
|
Room
|
Table------Chair------Book------Player
| |
Bowl Bookmark
|
Spoon
Una serie di funzioni può essere usata per leggere l'albero degli oggetti.
parent [genitore]
sibling [fratello]
child [figlio]
youngest [minore]
elder [maggiore]
eldest (uguale a child)
younger (uguale a sibling)
e
children [figli]
Ogni funzione accetta un oggetto singolo come argomento, così che
parent(Table) = Room
parent(Bookmark) = Book
parent(Player) = Room
child(Bowl) = Spoon
child(Room) = Table
child(Chair) = 0 (Nothing)
sibling(Table) = Chairsibling(Player) = 0 (Nothing)
youngest(Room) = Player
youngest(Spoon) = 0 (Nothing)
elder(Chair) = Table
elder(Table) = 0 (Nothing)
e
children(Room) = 4
children(Table) = 1
children(Chair) = 0
(Ritornando alla spiegazione precedente riguardante i numeri degli oggetti e <nomeoggetto>, le funzioni del primo insieme in realtà restituiscono un numero intero che si riferisce ad un particolare <nomeoggetto>).Per capire meglio come l'albero degli oggetti rappresenti il mondo fisico, il tavolo (table), la sedia (chair), il libro (book), ed il giocatore (player) sono tutti nella stanza (room). Il segnalibro (bookmark) è nel libro (book). La scodella (bowl) è sul tavolo (table), ed il cucchiaio (spoon) è sulla scodella (bowl). La libreria di Hugo cosidererà l'oggetto giocatore dell'esempio come se stesse in piedi; se il giocatore fosse stato seduto, l'albero degli oggetti sarebbe apparso come:
Nothing
|
Room
|
Table------Chair-----Book
| | |
... Player ...
e
child(Chair) = Player
parent(Player) = Chair
children(Chair) = 1
(Provate a compilare SAMPLE.HUG con l'opzione -o per vedere l'albero degli oggetti per il gioco di esempio. Oppure, se il flag DEBUG è stato impostato durante la compilazione, usate il comando HugoFix "$ot" o "$ot <oggetto>" durante il gioco per vedere lo stato attuale dell'albero degli oggetti e vedere come cambia nel corso del gioco. Compilando con l'opzione -d viene generata una versione per il debug (.HDX) del file -- l'albero degli oggetti può essere visto direttamente dal debugger).I test logici possono essere valutati per quel che riguarda gli oggetti ed i figli. La struttura
<oggetto> [not] in <genitore>
restituirà true (vero) se <oggetto> si trova in <genitore> (o false (falso) se viene usato 'not' (non)).Per posizionare inizialmente un oggetto nell'albero degli oggetti si usa
in <genitore>
nella definizione dell'oggetto, o, in alternativa
nearby <oggetto>
o, più semplicemente
nearby
per dare all'oggetto lo stesso genitore di <oggetto> o, se <oggetto> non viene specificato, lo stesso genitore dell'ultimo oggetto definito.Se questa indicazione non viene fornita, l'oggetto genitore assume automaticamente il valore 0 -- l'oggetto nothing così come definito nella libreria. Tutti i normali oggetti room (stanza) hanno 0 definito come genitore.
Quindi la versione espansa della definizione di un oggetto è
object <nomeoggetto> "nome oggetto"
{
in <oggetto genitore>
...
}
(Assicuratevi che la parentesi graffa aperta '{' non si trovi sulla stessa riga dello speficificatore 'object'.
object <nomeoggetto> "nome oggetto" {...
non è permesso).Il tavolo (table) dell'esempio probabilmente aveva una definizione come
object table "table"
{
in room
...
}
Per mettere l'oggetto suitcase (valigia) definito prima nella stanza vuota (emptyroom) in SHELL.HUG:
object suitcase "suitcase"
{
in emptyroom
}
Gli oggetti possono poi essere mossi all'interno dell'albero degli oggetti tramite il comando 'move' (muovi) come in
move <oggetto> to <nuovo genitore>
Che, praticamente, rimuove <oggetto> dal vecchio genitore, rende il fratello di <oggetto> il fratello del maggiore di <oggetto>, e sposta <oggetto> (con tutto quello che gli appartiene) dal nuovo genitore.Perciò nell'esempio di prima, il comando
move bowl to player
avrebbe modificato l'albero degli oggetti come segue:
Nothing
|
Room
|
Table------Chair-----Book------Player
| |
Bookmark Bowl
|
Spoon
Esiste anche un comando per rimuovere un oggetto dalla sua posizione nell'albero:
remove <oggetto>
che è lo stesso di
move <oggetto> to 0
L'oggetto può, in seguito, essere spostato in qualsiasi posizione.
III.b Attributi
Gli attributi rappresentano sostanzialmente le qualità che un oggetto può o non può avere. Sono utili per qualificare o meno gli oggetti da prendere in considerazione in ogni situazione.Un attributo viene definito come segue
attribute <nome attributo>
Possono essere definiti fino a 128 attributi. Quelli definiti in HUGOLIB.H comprendono:
known se un oggetto è conosciuto dal giocatore moved se un oggetto è stato spostato visited se una stanza è stata visitata static se un oggetto non può essere preso plural per oggetti al plurale (ad es., some hats [alcuni cappelli]) living se l'oggetto è un personaggio female se il personaggio è femminile unfriendly se un personaggio non è amichevole openable se un oggetto può essere aperto open se è aperto lockable se un oggetto può essere chiuso a chiave locked se è chiuso a chiave light se un oggetto fornisce luce readable se un oggetto può essere letto switchable se un oggetto può essere acceso o spento switchedon se un oggetto è acceso clothing per oggetti che possono essere indossati worn se l'oggetto è indossato mobile se l'oggetto può essere spinto, ecc. enterable se è possibile entrare nell'oggetto container se l'oggetto può contenere altri oggetti platform se si possono posare oggetti sull'oggetto (NOTA: container e platform sono mutualmente esclusivi) hidden se l'oggetto non deve essere indicato quiet se il container o il platform sono quieti (cioè, se l'indicazione iniziale degli oggetti che contengono non deve essere prodotta) transparent se l'oggetto non è opaco already_listed se l'oggetto è già stato indicato (cioè, prima di un listato WhatsIn) workflag usato dal sistema special per varii usi Alcuni di questi attributi sono in realtà lo stesso attributo con un nome differente. Questo viene ottenuto tramite
attribute <attributo2> alias <attributo1>
dove <attributo1> è già stato definito. Ad esempio, la libreria equipara visited (visitato) a moved (mosso) (visto che, probabilmente, non si applicheranno mai allo stesso oggetto), così:
attribute visited alias moved
In questo caso un oggetto che è stato visitato (visited) risulta anche mosso (moved). Ci si aspetta che gli attributi che sono alias di altri non avranno mai bisogno di essere usati con gli originali nella stessa circostanza.Gli attributi vengono assegnati ad un oggetto durante la sua definizione come segue:
object <nomeoggetto> "nome oggetto"
{
is [not] <attributo1>, [not] <attributo2>, ...
...
}
NOTA: La parola chiave 'not' (non) nella definizione di un oggetto è importante quando si usa una classe invece della definizione base di un oggetto (object), visto che la classe può avere alcuni attributi predefiniti che non sono utili all'oggetto corrente.Anche se ad un oggetto non è stato dato un particolare attributo durante la definizione dello stesso, è possibile assegnargli l'attributo in qualunque punto seguente nel programma con il comando
<oggetto> is [not] <attributo>
dove la parola chiave 'not' rimuove l'attributo invece di impostarlo.Gli attributi possono anche essere letti usando le strutture 'is' (è) e 'is not' (non è). Come una funzione,
<oggetto> is [not] <attributo>
ritorna true (1) se l'<oggetto> è (o non è, se viene indicato 'not') <attributo>. Altrimenti ritorna false (0).Per fornire all'oggetto suitcase (valigia) gli attributi appropriati, si espande la definizione dell'oggetto per includere
object suitcase "suitcase"
{
in emptyroom
is openable, not open
...
}
Adesso le equzioni che seguono restituiscono true (vero):
suitcase is openable = 1
suitcase is open = 0
suitcase is locked = 0
III.c Proprietà
Le proprietà sono decisamente più complesse degli attributi. Prima di tutto, non tutti gli oggetti possono avere delle proprietà; per fare in modo che un oggetto abbia delle proprietà deve essere specificato nella definizione dello stessoIn più le proprietà non sono dei semplici flag sì/no. Sono un insieme di dati validi associati ad un oggetto, i cui valori possono rappresentare quasi ogni cosa, compresi i numeri degli oggetti, indirizzi del dizionario, valori interi, ed indirizzi di codice eseguibile. Il numero massimo di valori associabili non è definito, ma manegevolezza ed efficienza suggeriscono un numero di otto o meno.
Queste sono alcune delle proprietà che potrebbero essere presenti nella definizione di un oggetto (usando i nomi di proprietà definiti in HUGOLIB.H):
nouns "albero", "cespuglio", "arbusto", "pianta"size 20
found_in tinello, ingresso
long_desc
{"Ci sono delle uscite a nord ed ovest. Nel muro a
sudest c'è una porta."}short_desc
{
"C'è una scatola. Ed è ";
if self is open
print "aperta";
else
print "chiusa";
print "."
}before
{
object DoGet
{
if Acquire(player, self)
{"Raccogli ";
print Art(self); "."}
else
return false
}
}
La proprietà nouns (nomi) contiene 4 indirizzi del dizionario; la proprietà size (dimensione) è un valore intero; la proprietà found_in (trovato_in) contiene due numeri di oggetti; e le proprietà long_desc (descrizione_estesa) e short_desc (descrizione_corta) sono entrambi dei valori che rappresentano gli indirizzi delle routine associate.La proprietà before (prima) è un caso speciale. Questa proprietà complessa viene definita dal compilatore e viene trattata differentemente dall'interprete rispetto alle altre proprietà. In questo caso, il valore della proprietà rappresentante l'indirizzo della routine viene restituito solo se le varabili globali object e verbroutine contengono, rispettivamente, l'oggetto in questione e l'indirizzo della routine DoGet. (C'è anche una routine after [dopo], che viene controllata dopo che la routine del verbo è stata chiamata).
(Nota per chiarificare: la routine Art di HUGOLIB.H stampa l'articolo corretto, se presente, seguito dal nome dell'oggetto. La routine Acquire restituisce true solo se la proprietà holding [quanto contiene] del primo oggetto più la proprietà size [dimensione] del secondo oggetto non eccedono la proprietà capacity [capacità] del primo oggetto).
Tutto questo può apparire poco chiaro per il momento. Più avanti si parlerà ancora delle routine proprietà. Per adesso pensate ad una proprietà come se contenesse semplicemente un valore (od un insieme di valori).
Una proprietà viene definita in maniera simile ad un attributo
property <nome proprietà>
Un valore di default può essere definito per la proprietà usando
property <nome proprietà> <valore di default>
dove <valore di default> può essere una costante od una parola del dizionario. Per gli oggetti senza una determinata proprietà, cercando di trovare quella proprietà di otterrà il valore di default. Se il valore di default non viene dichiarato esplicitamente viene impostato a 0.La lista delle proprietà definita in HUGOLIB.H è:
name il nome base dell'oggetto before prima delle routine verbo after dopo le routine verbo noun il nome od i nomi con cui riferirsi all'oggetto adjective l'aggettivo o gli aggettivi che descrivono l'oggetto article "a" "an" "the" "some" ecc. preposition "in" "inside" "outside of" ecc. pronoun pronome appropriato per l'oggetto in questione short_desc descrizione base tipo "X è qui" initial_desc sostituisce short_desc (o long_desc per le locazioni) long_desc descrizione dettagliata found_in in caso di locazioni multiple (oggetti genitore virtuali, NON fisici) type per identificare il tipo di un oggetto n_to ne_to e_to se_to s_to sw_to (solo per le stanze indicano dove conducono le uscite) w_to nw_to u_to d_to in_to out_to cant_go messaggio se una direzione non è valida size dimensione per l'inventario o la capacità di oggetti contenitore capacity la capacità di un oggetto contenitore holding quanto un oggetto contenitore possiede reach per limitare l'accessibilità degli oggettti list_contents per sostituire il listato normale door_to per consentire "Enter <oggetto>" key_object se l'oggetto è lockable (chiudibile), la chiave giusta when_open quando aperto sostituisce short_desc when_close quando chiuso sostituisce short_desc ignore_response per i personaggi order_response per i personaggi contanins_desc invece di "inside X are..." inv_desc per descrizioni speciali nell'inventario desc_detail descrizione dettagliata per il listing dell'oggetto parse_rank per la differenziazion degli oggetti con nomi simili exclude_from_all per l'interpretazione di "all" (tutto) nell'input misc per usi diversi (Per una descrizione dettagliata di come ogni proprietà viene usata, consultate l'Appendice B: La libreria).
(Le seguenti proprietà sono inoltre definite ed usate esclusivamente dall'oggetto display:
screenwidth larghezza dello schermo, in caratteri screenheight altezza dello schermo, in caratteri linelength larghezza della finestra di testo corrente windowlines altezza della finestra di testo corrente cursor_column posizione orizzontale e verticale del cursor_row cursore nella finestra di testo corrente hasgraphics true (vero) se lo schermo è in grado di visualizzare la grafica title_caption voce del dizionario che fornisce il nome esatto del programma (opzionale) statusline_height altezza dell'ultima riga di stato visualizzata Notate che mentre alcune di queste, da screenwidth a title_caption, sono definite come costanti nella libreria, possono essere comunque usate come indicatori di proprietà, visto che sia i numeri di proprietà che le costanti sono semplici interi).
Anche i nomi di proprietà possono avere degli alias tramite
property <proprietà2> alias <proprietà1>
dove <proprietà1> è stata definita in precedenza.La libreria definisce i seguenti alias (tra gli altri):
nouns alias noun
adjectives alias adjective
prep alias preposition
pronouns alias pronoun
Una proprietà viene espressa tramite
<oggetto>.<proprietà>
Il numero di elementi che possiede una proprietà che ha più di un valore singolo possono essere trovati con
<oggetto>.#<proprietà>
ed un singolo elemento viene espresso tramite
<oggetto>.<proprietà> #<numero elemento>
NOTA: "<oggetto>.<proprietà>" è semplicemente la versione ridotta di "<oggetto>.<proprietà> #1".Per aggiungere qualche proprietà all'oggetto suitcase (valigia) è necessario espandere la definizione dell'oggetto con
object suitcase "big green suitcase"
{
in emptyroom ! fatto prima
is openable, not open !nouns "suitcase", "case", "luggage"
adjective "big", "green", "suit"
article "a"
size 25
capacity 100
}
Basandosi sulle regole dell'interprete per l'identificazione degli oggetti, il giocatore si può riferire all'oggetto valigia come "big green suitcase" (grande valigia verde), "big case" (grande valigia), o "green suitcase" (valigia verde) tra le varie combinazioni. Anche "big green" (grande verde) e "suit" possono essere valide, garantendo che queste espressioni non si riferiscano anche ad altri oggetti disponibili come "a big green apple" (una grande mela verde) o "your suit jacket" (la giacca del tuo vestito).(NOTA: il formato base di identificazione del parser è
<aggettivo 1> <agg. 2> <agg. 3>...<agg. n> <nome>
dove ogni sottoinsieme di questi elementi è consentito. Comunque il nome deve apparire per ultimo e solo un nome viene riconosciuto, così che
<nome> <nome> and <nome> <aggettivo>
come in
"luggage case" e "suitcase green"
non vengono riconosciuti).Una sorgente occasionale di codice stupido che non si comporta nel modo che il programmatore intendeva è quando non viene riservato abbastanza spazio per una proprietà di un determinato oggetto. Cioè se l'oggetto è stato originariamente definito con la proprietà
found_in kitchen
e poi il programma cerca di impostare
<oggetto>.found_in #2 = livingroom
non avrà nessun effetto evidente. In pratica non ci sarà dello spazio nella tabella delle proprietà di <oggetto> per un secondo valore di found_in. Cercando di leggere <oggetto>.found_in #2 si avrà un valore di ritorno pari a 0 -- una proprietà non esistente -- non il numero dell'oggetto livingroom. (Eseguendo il debugger con gli avvertimenti a runtime [runtime warnings] abilitati aiuta ad individuare situazioni come questa).Per ovviare a ciò, se si prevede che probabilmente un secondo (o terzo, o quarto, o nono) valore andrà impostato -- anche se solo un valore viene definito -- usate
found_in kitchen, 0[, 0, 0,...]
nella definizione dell'oggetto.(Una scorciatoia utile per inizializzare una serie di valori a 0 è quella di usare
found_in #4
invece di
found_in 0, 0, 0, 0
nella definizione dell'oggetto).Come ci si potrebbe aspettare le combinazioni di proprietà sono lette da sinistra a destra, così che
location.n_to.name
viene interpretata come
(location.n_to).nameIII.d Classi
Le classi sono sostanzialmente degli oggetti che servono come prototipi per uno o più oggetti simili.
Una classe viene definita come segue:
class <nomeclasse> ["<nome opzionale>"]
{
...
}
con il corpo della definizione che è simile a quello usato per la definizione di un oggetto, dove le proprietà e gli attributi definiti valgono per tutti i membri della classe.Ad esempio:
class scatola
{
noun "scatola"
long_desc
"Sembra una normale vecchia scatola."
is openable, not open
}scatola scatolagrande "scatola grande"
{
article "una"
adjectives "grande", "larga"
is open
}scatola scatolaverde "scatola verde"
{
article "una"
adjective "verde"
long_desc
"Sembra una normale vecchia scatola, solo verde."
}
(Cominciando la routine della proprietà long_desc sulla riga sotto il nome della proprietà, questa viene interpretata dal compilatore come:
long_desc
{
"Sembra una normale vecchia scatola, solo verde."
}
Visto che la proprietà è lunga solo una riga -- un comando di stampa -- le parentesi graffe non sono necessarie).La definizione di un oggetto di una certa classe comincia con il nome dell'oggetto prototipo invece di "object". Tutte le proprietà e gli attributi della classe vengono ereditati (tranne la posizione all'interno dell'albero degli oggetti), a meno che essi siano esplicitamente definiti nel nuovo oggetto.
In pratica, sebbene la classe scatola sia stata definita senza l'attributo open (aperto), l'oggetto scatolagrande comincierà il gioco aperta (open), visto che così è stata impostata nella definzione di scatolagrande. Comincierà il gioco comunque apribile (openable) visto che questo attributo è stato ereditato dalla classe scatola.
Mentre l'oggetto scatolagrande avrà la proprietà long_desc della classe scatola, l'oggetto scatolaverde sostituisce la descrizione di default con una nuova descrizione. (Un'eccezione a questo è la proprietà "$additive", spiegata in seguito, dove nuove proprietà vengono aggiunte a quelle delle classi precedenti).
Poiché una classe è fondamentalmente un oggetto, è possibile definire un oggetto usando un oggetto precedente come classe anche se l'oggetto precedente non è stato esplicitamente definito come classe.
Perciò
scatolagrande scatolagranderossa "scatola grande rossa"
{
adjectives "grande", "larga", "rossa"
}
è perfettamente valido.Occasionalmente può essere necessario avere un oggetto o una classe che ereditano da più di una classe definita in precedenza. Questo può essere ottenuto usando l'istruzione "inherits" (eredita).
<classe1> <nomeoggetto> "nome"
{
inherits <classe2>[, <classe3>,...]
...
}
o anche
object <nomeoggetto> "nome"
{
inherits <classe1>, <classe2>[, <classe3>,...]
...
}
La precedenza dell'ereditarietà è in ordine di definizione. Negli esempi precedenti, l'oggetto eredita prima da <classe1>, poi da <classe2>, e così via (o anche <oggetto1>, <oggetto2>, ecc.).La libreria degli oggetti di Hugo (OBJLIB.H) contiene una serie di definizioni di classi utili per cose come stanze, personaggi, scenario, veicoli, ecc. Alcune volte, comunque, può essere utile usare una definizione differente per, ad esempio, la classe room (stanza) mantenendo tutte le altre della libreria degli oggetti.
Invece di modificare il file OBJLIB.H si usa:
replace <classe> ["<nome opzionale>"]
{
(...nuova definizione dell'oggetto...)
}
dove <classe> è il nome di un oggetto o di una classe definiti in precedenza, come "room" (stanza). Tutti i riferimenti seguenti a <classe> useranno questo oggetto invece di quello definito precedentemente.
(Questo significa che la sostituzione [replace] deve avvenire PRIMA dell'uso della classe dagli altri oggetti).
©2000 Simone Zanella e ©2000 IF Italia. E' vietata la riproduzione.