IF ITALIA - Il sito Italiano sull'Interactive Fiction

VI. MICCE, DEMONI E SCRIPT


Mentre tutti gli elementi menzionati in precedenza sono programmati all'interno del codice dell'interprete, i mezzi per l'esecuzione delle micce [fuse], dei demoni e degli script sono scritti completamente in Hugo e si trovano nella libreria (HUGOLIB.H).

VI.a. Micce e Demoni

Daemon [demone] è un termine tradizionale che serve a definire un'attività ricorrente. Hugo gestisce i daemon come eventi speciali allegati ad oggetti che possono essere attivati o disattivati (cioè inseriti od estratti dall'area di visibilità di runevents).

Visto che la classe daemon è definita nella libreria, per definire un daemon si usa

daemon <nome>
{}
Il corpo della definizione del daemon è vuoto. È necessaria solo per associare l'evento daemon, così che la definizione del daemon deve essere seguita da
event [in] <nome>
{
    ...
}
Attivato da
Activate(<nome>)
che muove l'oggetto daemon specificato nell'area di visibilità del giocatore. In questo modo tutte le volte che un comando 'runevents' viene eseguito (come dovrebbe essere nella routine Main), l'evento associato a <nome> viene eseguito.

Il daemon viene disattivato usando

Deactivate(<nome>)
che rimuove l'oggetto daemon dalla visibilità.

Si può vedere come un daemon sia in realtà uno speciale tipo di oggetto che che posto o meno nell'area di visibilità di 'runevents', e che è l'evento associato al daemon che contiene veramente il codice.

Un fuse [miccia] è il nome con il quale si indica un timer -- cioè qualunque evento che debba accadere dopo uno specifico periodo di tempo. Il fuse è una versione un po' più complessa di un oggetto daemon, contenendo due proprietà addizionali in aggiunta a in_scope:
 

timer  il numero di turni prima che l'evento del fuse venga eseguito
tick una routine che decrementa il timer e restituisce il numerodi turni che restano (cioè il valore di timer)

Come per i daemon la definizione di un fuse viene fatta in due passi

fuse <nome>
{}

event [in] <nome>
{
    ...
    if not self.tick
    {
        ...
    }
}

e si accende e spegne con
Activate(<nome>, <impostazioni>)
o
Deactivate(<nome>)
dove <impostazioni> è il valore iniziale della proprietà timer.

Notare che è compito dell'evento eseguire il timer e verificare la sua scadenza. La riga

if not self.tick
esegue la proprietà tick -- che decrementa il timer -- ed esegue il blocco di codice condizionale seguente se self.timer è 0.

Esempio: Un semplice Daemon ed un ancor più semplice Fuse

Il daemon più semplice è qualcosa tipo un contatore di sonno, che misura quanto un giocatore può continuare a cominciare da un determinato momento di riposo.

Assumiamo che la quantità di riposo del giocatore sia contenuta in una proprietà chiamata riposo, che si decrementa di 2 ad ogni turno.

daemon affaticamento
{}

event in affaticamento
{
    player.riposo = player.riposo - 2
    if player.riposo < 0
        player.riposo = 0

    select player.riposo
        case 20
            "Ti stai affaticando."
        case 10
            "Stai diventando \Imolto\i stanco."
        case 0
            "Ti sei addormentato!"
}

Si avvia e si ferma il daemon con Activate(affaticamento) e Deactivate(affaticamento).

Ora, per il fuse [miccia], perché non costruire l'esempio più ovvio: quello di una bomba ad orologeria? (Assumendo che esista un altro oggetto fisico bomba; bombaorologeria è solo il fuse per il conto alla rovescia).

fuse bombaorologeria
{}

event in bombaorologeria
{
    if not self.tick
    {
        if Contains(location, bomba)
            "Scompari in un elegante KABOOM!"
        else
            "Senti un KABOOM da qualche parte!"
        remove bomba
    }
}

Si attiva (con un conto alla rovescia di 25 turni) e si ferma con Activate(bombaorologeria, 25) e Deactivate(bombaorologeria).

VI.b. Script

Gli script sono decisamente più complessi dei fuse e dei daemon. Lo scopo di uno script (chiamato anche script del personaggio) è quello di consentire ad un oggetto -- di solito un personaggio -- di eseguire una sequenza di azioni in base ai turni del gioco e indipendentente dal giocatore.

Fino a 16 script possono essere eseguiti insieme. È compito del programmatore preoccuparsi di non superare tale limite.

Uno script è rappresentato da due array: scriptdata e setscript. Il secondo è stato nominato per la chiarezza della programmazione piuttosto che per il suo contenuto. Ecco perché:

Per definire uno script si usa la seguente notazione:

setscript[Script(<ogg>, <numero>)] = &CharRoutine, ogg,
                            &CharRoutine, ogg,
                            ...
(ricordandosi che una virgola alla fine di una riga dice al compilatore che la riga prosegue alla successiva).

Fare caso al fatto che "setscript" in realtà è un array, che prende il suo elemento iniziale dal valore di ritorno della routine Script, che ha <oggetto> e <numero> come argomenti.

Script restituisce un puntatore all'interno del grande array "setscript" dove il <numero> di passi di uno script per <oggetto> si trovano. Un singolo script può avere fino a 32 passi. Un passo in uno script è composto da una routine ed un oggetto -- sono entrambi necessari anche se la routine non richiede un oggetto. (Si usa l'oggetto nothing (0); vedere la routine CharWait in HUGOLIB.H per le informazioni).

La consuetudine in HUGOLIB.H vuole che le routine di script del personaggio abbiano il prefisso "Char", sebbene questo non sia obbligatorio. Al momento le routine fornite comprendono:
 

CharMove [Muovi] (richiede un oggetto direzione)
CharWait [Attendi] (si usa l'oggetto nothing)
CharGet [Prendi] (richiede un oggetto prendibile)
CharDrop [Lascia] (richiede un oggetto in possesso del personaggio)

così come la routine speciale
 

LoopScript [RipetiScript] (si usa l'oggetto nothing)

che indica uno script che verrà eseguito in continuazione. (È compito del programmatore assicurarsi che la posizione finale di un personaggio o di un oggetto sia adatta a ricominciare con lo script se LoopScript viene usato. Vale a dire che se lo script è composto da una complessa serie di direzioni, il personaggio deve sempre ritornare allo stesso punto di partenza.)

La sequenza di routine ed oggetti di ogni script è memorizzata nell'array setscript.

Gli script vengono eseguiti tramite la routine RunScripts, simile a runevents, da cui differisce per il fatto che runevents è un comando dell'interprete mentre RunScripts è contenuta in HUGOLIB.H.

La riga

RunScripts
eseguirà tutti gli script personaggio/oggetto attivi, un turno alla volta, liberando lo spazio usato da ognuno una volta che sono terminati.

Quello che segue è uno script di esempio per un personaggio di nome "Ned":

setscript[Script(ned, 4)] = &CharMove, s_obj,
                &CharGet, palladicannone,
                &CharMove, n_obj,
                &CharWait, 0,
                &CharDrop, palladicannone
Ned andrà a sud, raccoglierà l'oggetto palladicannone, la porterà con sé a nord, attenderà un turno e lascerà la palladicannone. (Le routine di script per i personaggi fornite dalla libreria sono relativamente elementari; ad esempio, CharGet assume che l'oggetto specificato sia presente quando il personaggio cerca di prenderlo).

Altre routine di gestione script in HUGOLIB.H comprendono:
 

CancelScript(ogg) per terminare immediatamente l'esecuzione dello script per <ogg>
PauseScript(ogg) ferma temporaneamente l'esecuzione dello script per <ogg>
ResumeScript(ogg) riprende l'esecuzione di uno script fermato
SkipScript(ogg) salta lo script per <ogg> per la successiva esecuzione di RunScripts

La routine RunScripts controlla anche le proprietà before e after. Continua con l'azione predefinita -- la routine di azione del personaggio specificata nello script -- se trova un valore false.

Per ignorare una routine di azione predefinata per un personaggio si include una proprietà before per l'oggetto del personaggio usando la forma seguente:

before
{
    actor CharRoutine
    {
        ...
    }
}
dove CharRoutine è CharWait, CharMove, CharGet, CharDrop, ecc.

VI.c. Una nota sulla globale event_flag

Le routine della libreria -- in particolare le routine verbo DoWait... -- si aspettano che la variabile globale event_flag venga impostata con un valore non falso se qualcosa accade (in un evento o uno script) così che al giocatore venga notificato e venga data l'opportunita si smettere si aspettare. Ad esempio le routine di script dei personaggi in HUGOLIB.H impostano event_flag ogni volta che un personaggio fa qualcosa nella stessa locazione del giocatore.

Se si usa HUGOLIB.H deve essere seguita la regola di impostare event_flag dopo ogni evento significativo.
 



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

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