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) = Chair
sibling(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 stesso

In 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).name

III.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).
 



Torna alla pagina iniziale Torna alla Home Page Torna alla pagina iniziale

©2000 Simone Zanella e ©2000 IF Italia. E' vietata la riproduzione.