tutorial_pandor+_v3_5
tutorial_pandor+_v3_5
Andiamo nel menu File e creiamo un nuovo progetto scegliendo New project. Digitiamo come nome "test".
Pandor+ creerà una sotto-cartella test con dentro diversi file.
Pandor+ crea nella sotto-cartella test tutti i file che definiranno la nostra avventura. Oltre a questo, verrà
selezionato come progetto corrente proprio "test".
Esaminiamo i file contenuti nella cartella test:
Qualora le estensioni dei file non fossero visibili (".p+", ".txt", etc) conviene modificare le opzioni di Windows per
renderle tali. Per farlo cercate nelle impostazioni la voce "opzioni cartella" o "opzioni esplora risorse": nel tab
"visualizzazione" cercate la voce "nascondi le estensioni per i tipi di file conosciuti" e disattivatela.
Ogni nuovo progetto creato contiene un'avventura di default con un paio di stanze e un paio di oggetti. Andiamo
sul menu Project e scegliamo Generate HTML debug and release files.
Pandor+ assemblerà i file contenuti nella cartella test e il risultato sarà la generazione dei file
avventura_debug.html e [Link] .
Apriamo [Link] con il browser (ad esempio con tasto destro sul file e apri con...). Il browser potrebbe
richiedere l'autorizzazione a eseguire script. Vedremo quindi apparire la prima schermata dell'avventura:
Proviamo a cliccare su Inizia l'avventura!: vedremo due pagine di introduzione (clicchiamo su Continua) e quindi
la prima stanza del gioco.
L’avventura di test è ben poco entusiasmante, si può fare poco o niente! Vedremo tra poco la creazione di
un’avventura con diverse stanze, oggetti e interazioni di vario tipo. Intanto però vediamo cosa è successo con il
comando Generate HTML debug and release files.
Pandor+ utilizza uno script di base (un mix di HTML e javascript) contenente il "motore" del gioco in cui sono
lasciate incomplete le parti relative all'introduzione, alla descrizione delle stanze, ai messaggi, alle azioni possibili,
etc. Al comando di generazione, i file di progetto vengono elaborati da Pandor+ e inseriti nelle sezioni incomplete
per comporre il file finale HTML con l’avventura completa.
Possiamo decidere di generare solamente la versione di debug dell’avventura (file avventura_debug.html) oppure
anche quella di release finale (file [Link]). In quest’ultima risultano crittati i testi, in modo che non basti
aprire il file del gioco per leggere descrizioni, oggetti, messaggi e così via. In generale la versione di release viene
generata solo quando l'avventura è completa ed è stata testata adeguatamente.
Quando l'avventura è pronta per la pubblicazione, il file HTML va messo a disposizione su un proprio server web
o, ancora meglio, inviato a epaperadventures@[Link] : verrà inserita tra le avventure disponibili del sito
[Link] !
L'occhio purpureo
L'avventura che ci permetterà di imparare a sviluppare con Pandor+ si intitola "L'occhio purpureo". In questa
avventura il giocatore, che si trova in una situazione finanziaria "nera", è partito alla ricerca di un favoloso e
inestimabile gioiello chiamato "Occhio purpureo", nascosto in qualche parte del mondo.
Per sua sfortuna, il protagonista è inseguito da un ispettore delle tasse che gli renderà la vita difficile. Quando
l'ispettore compare infatti, possono succedere due cose. Se si ha con se un oggetto, egli se ne appropria, come
anticipo del rimborso dell'enorme debito col fisco. Se invece non si ha ancora niente (o non si può pagare) il
giocatore finisce in gattabuia e l'avventura termina.
Come in ogni avventura ci sono parecchi oggetti che servono al giocatore. Una lampada ci permetterà ad esempio
di trovare l'uscita in una stanza buia.
Non tutti gli oggetti che si troveranno nei vari ambienti sono però utili. Ad esempio un mattone molto pesante fa
annegare il giocatore se tenterà di attraversare un fiume a nuoto.
E infine l'occhio purpureo è nascosto dentro ad un sacchetto di biglie che il giocatore troverà e dovrà capire di
dover rovesciare. Ma non basta: per vincere l'avventura si dovrà essere nella sala del trono e tirare la catena che
pende dal soffitto un numero corretto di volte indossando dei guanti! In caso contrario moriremo fulminati o la
sala del trono diventerà una toilette gigante e si verrà sciacquati fuori dall'avventura!
Ecco la mappa del mondo in cui il giocatore si trova proiettato, composto da 12 ambienti e alcuni oggetti:
Associamo ad ogni ambiente un numero. Questo ci servirà per scrivere le descrizioni degli ambienti (detti anche
"stanze") e gli oggetti in essi.
Cominciamo!
Per iniziare creiamo un nuovo progetto e chiamiamolo "occhio". Automaticamente Pandor+ crea la sotto-cartella
occhio e imposta automaticamente come progetto corrente proprio occhio. Se ora chiudiamo Pandor+ e lo
riapriamo, vediamo che nessun progetto è selezionato. Andiamo sotto File e scegliamo Set project: sull'albero
delle cartelle che compare facciamo doppio click sulla cartella occhio (a destra vedremo i file in essa contenuti) e
quindi scegliamo OK. Il progetto corrente diventerà nuovamente occhio.
A questo punto possiamo aprire i file di progetto con Notepad++ per modificarli, salvarli e poi rigenerare
l'avventura.
Attenzione però a creare, modificare e salvare tutti file di progetto in codifica ANSI e a non introdurre
caratteri non ANSI perché questo causerebbe errori nella generazione del gioco.
In Notepad++ bisogna selezionare il menu Encoding e poi Encode in ANSI:
Oppure, prima di iniziare a lavorare, possiamo modificare le opzioni di Notepad++: sotto Settings, scegliamo
Preferences e quindi selezioniamo New document e nel box Encoding ANSI.
Attenzione anche a copia-incollare testo proveniente da applicazioni esterne in quanto, se presenti caratteri non
ANSI, questo potrebbe modificare la codifica dell'intero file.
Un'altra opzione da disabilitare sempre è il word wrap, ovvero l'opzione di "A capo automatico". In Notepad++
troviamo l'opzione sotto il menu View. Non ci deve essere la spunta.
Un ulteriore aiuto per scrivere le nostre avventure in Notepad++ ci viene dalla possibilità di caratterizzare i colori
delle parole nei file di progetto di Pandor+ in modo da rendere più riconoscibili testi, comandi, codice, etc.
Per farlo, scaricate il file p+.xml dal nostro sito alla pagina: [Link]
Andate nel menu Language e scegliete Define your language:
Nella finestra che si apre clicchiamo sul tasto Import e quindi scegliamo il file p+.xml:
Diamo OK al messaggio di importazione avvenuta e infine chiudiamo la finestra. Da questo momento alcuni file
di Pandor+ verranno colorati in modo da agevolare il riconoscimento degli elementi che li compongono.
l file di progetto messages.p+r
Il file messages.p+r contiene i testi che sono utilizzati per comandi e messaggi nel gioco. Apriamo il file con
Notepad++.
Le linee {NUMERO} vanno lasciate inalterate, mentre possiamo modificare le linee di testo sottostanti.
In messages.p+r sono ammessi solamente i seguenti caratteri:
qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890 àÀáÁèÈéÉìÌíÍòÒóÓùÙúÚ
!£$%&/()=?'|*+-,;.:<>^#_{}
Se vi sono altri caratteri, Pandor+ bloccherà la generazione del gioco riportando un errore e i caratteri non
ammessi:
Attenzione: di default il controllo dei caratteri è disabilitato. Per abilitarlo bisogna cliccare sull'opzione Skip file
characters check sotto Project, in modo da togliere la spunta:
Alcuni caratteri hanno un uso speciale. Vanno utilizzati solo se sono già previsti nei messaggi di default.
| Questo carattere indica a Pandor+ di scegliere a caso uno dei testi separati da esso
^ Questo carattere indica "a capo"
'' Due caratteri ' consecutivi vengono trasformati in virgolette: "
Ecco di seguito l'elenco dei testi di messages.p+r modificabili.
{01}
La mia Avventura!
{02}
lmv001
Identificatore dell'avventura, utilizzato per distinguere i salvataggi di avventure diverse. Non utilizzare spazi, ma
unicamente lettere e numeri.
{03}
[Link]
URL del sito che si può raggiungere dal link nella schermata iniziale.
{04}
USATO
{05}
vuoto
{06}
aiuto
<07>
Ritorna all'avventura...
{08}
Inizia l'avventura!
{11}
termina
{13}
carica
{14}
salva
{15}
Carica situazione...
{17}
e stai indossando
{18}
usa con
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{19}
esamina
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{20}
prendi
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{21}
lascia
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{22}
indossa
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{23}
togli
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{24}
conferma...
{25}
vai
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{26}
Continua...
<27>
Stai indossando
{28}
Situazione caricata!
Frase nel messaggio che compare quando si carica una situazione salvata in precedenza.
{29}
Situazione salvata!
Frase nel messaggio che compare quando si salva una situazione di gioco (opzioni #1,#2,#3).
{30}
... Non trovi niente di interessante.|... Non trovi nulla d'importante.|... Nulla di interessante.|Non hai trovato
nulla.
{31}
... Hai trovato qualcosa!|... Hai scoperto qualcosa!|È comparso qualcosa!|La tua ricerca ha rivelato qualcosa...
Frasi di default che possono apparire dopo un comando "esamina" su un oggetto che porta a rivelare un altro
oggetto.
{32}
Non puoi farlo, stai portando troppe cose!|Hai con te troppe cose e non riesci a farlo!|Hai troppe cose per le
mani...|Il tuo equipaggiamento è troppo ingombrante...
{33}
... Sembra che non funzioni.|... Non sembra funzionare.|Proprio non sembra funzionare.|Sembra proprio che non
funzioni.
Frasi di default che possono apparire dopo un comando "usa con" su un oggetto.
{34}
Frase nel messaggio che compare quando si salva una situazione di gioco (opzione #4).
{35}
te stesso
{36}
completamento:
Frase che indica il completamento percentuale del gioco. Se non viene gestito il completamento, va cancellata la
frase e lasciata una riga vuota e in questo modo la frase non apparirà nel gioco.
{38}
{39}
Frase che appare per indicare la presenza di oggetti "mobili" nell'ambiente corrente.
{40}
{43}
mappa
{44}
parla con
Comando nel menu degli oggetti. Viene aggiunto automaticamente in base alla definizione di un oggetto.
{45}
Parlare non serve a molto ora...|Parli, ma non succede niente.|Non hai nulla da dire ora...
Frase di default che appare quando il giocatore parla con un oggetto. Utilizzando il carattere separatore | ,
possiamo indicare che vogliamo che ne venga scelta una a caso automaticamente durante il gioco.
{46}
{47}
COMPLIMENTI!!!
{48}
HAI FALLITO...
Comando nella schermata di fallimento del gioco per ritornare a prima dell'ultima azione. Se non si vuole rendere
disponibile questa opzione, basta cancellare la frase e lasciare una riga vuota.
{50}
avventura
Titolo del file del gioco. Utilizzare solo lettere, numeri o spazio.
Vediamo ora quali messaggi cambiare per l'avventura "L'occhio purpureo". In generale, questi saranno i testi da
modificare per le vostre avventure, mentre gli altri possono essere lasciati inalterati.
{01}
L'Occhio Purpureo
{02}
lop001
Identificatore dell'avventura.
{36}
Cancelliamo "completamento" e lasciamo una riga vuota in quanto in questa semplice avventura non gestiremo il
valore di completamento.
{49}
Cancelliamo "Torna all'ultima azione..." e lasciamo una riga vuota in quanto in questa semplice avventura non
attiveremo questa opzione.
{50}
occhiopurpureo
{01}
La mia Avventura!
Titolo dell'avventura
{03}
[Link]
URL del sito che si può raggiungere cliccando sul link (in basso)
{46}
L'Occhio Purpureo
Anche se per questa avventura utilizzeremo l'immagine di default, vediamo ora come eventualmente scegliere
un'altra immagine e sostituirla.
Poichè Pandor+ genera un unico file HTML che contiene tutto il gioco, le immagini vanno codificate in un modo
particolare, chiamata codifica base 64. Oltre alla codifica, dovremo scegliere immagini che abbiano dimensioni
opportune anche per schermi di lettori come Kindle (200x200 pixels ad esempio).
Come esempio, col browser andiamo all'indirizzo:
[Link]/cover_example.jpg
e salviamo l'immagine (ad esempio con tasto destro sull'immagine – salva immagine) sul nostro desktop. Questa è
esattamente l'immagine che è stata usata come default, ma potrebbe essere una qualsiasi creata da voi.
Dobbiamo ora creare la codifica in base 64. Un buon sito per farlo è: [Link]
Clicchiamo su OR CLICK HERE e scegliamo l'immagine che abbiamo salvato sul desktop. Una volta terminata la
codifica, clicchiamo su COPY IMAGE.
Ora salviamo. In questo modo, quando genereremo l'avventura la nuova immagine per la schermata iniziale sarà
quella scelta e non quella di default.
Il file di progetto intro.p+i
Questo file contiene il testo che descrive la situazione iniziale dell'avventura, che compare dopo la schermata
iniziale del gioco. Apriamolo con l'editor e modifichiamolo come segue:
A seguito di un dissesto finanziario sei fuggito dal tuo paese.^
!£$%&()=?'*+#-,;.:<>^§
possono essere cancellati se esci dal browser (dipende dal tuo dispositivo).
Il contenuto del file può essere lasciato così com'è in quanto contiene già tutte le informazioni importanti.
Nel file help.p+i sono ammessi solamente i seguenti caratteri:
qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890 àÀáÁèÈéÉìÌíÍòÒóÓùÙúÚ
!£$%&()=?'*+#-,;.:<>
Allo stesso modo, modifichiamo fail.p+i, il cui testo compare nella schermata di "fallimento" del gioco:
Non sei riuscito a trovare il prezioso Occhio Purpureo.^
!£$%&()=?'*+#-,;.:<>^
{}_
Oltre ai testi sopra, nella schermata di vittoria e fallimento vengono inseriti come titolo i testi {47} e {48}
presenti nel file messages.p+r:
{47}
COMPLIMENTI!!!
{48}
HAI FALLITO...
Inoltre, tra titolo e testo, sono inserite le immagini codificate in base 64 presenti nei file successimage.p+ e
failimage.p+; Per questa avventura lasceremo quelle di default ma potete sostituirle come già descritto per il file
coverimage.p+
Il file di progetto variables.p+v
Questo file ci permette di definire le variabili che useremo per stabilire le varie condizioni e situazioni nella nostra
avventura. Ad esempio se è stata fatta una certa azione oppure no, oppure quante azioni sono state compiute da un
certo momento in poi. Apriamo il file e modifichiamolo in questo modo:
Le variabili sono definite da un numero e da un identificatore che deve iniziare e terminare con _ .
Gli identificatori (esempio: _vAzioniInForesta_) possono essere composti solo con lettere (non accentate) e
numeri. Una buona idea è iniziare gli identificatori delle variabili con la lettera v (che sta per variable), per non
confondere le variabili con gli identificatori di oggetti e stanze.
Dopo l'identificatore va definito il valore iniziale, con = NUMERO. Ad esempio, _vNuotatoInFiume_ è
inizialmente pari a 0.
Le variabili possono assumere solo valori interi che vanno da 0 a 65533. Il numero massimo di variabili è 100.
Le linee che iniziano con // sono dei commenti per ricordarci lo scopo delle variabili stesse.
Due variabili particolari sono _vCOMPLETION_ e _vMOVES_ e vanno sempre lasciate così come sono definite.
La prima viene utilizzata per la percentuale di completamento nel gioco, che però non utilizzeremo nell'avventura.
_vMOVES_ è invece una variabile che va utilizzata per tener conto del numero di azioni compiute dal giocatore.
Facciamo un esempio di uso delle variabili.
Supponiamo che vogliamo tener traccia se il giocatore è passato per la stanza "sala da pranzo" oppure no. Nel
codice del gioco scriveremo una condizione del tipo:
SE il giocatore è nella sala da pranzo
ALLORA _vGiocatoreInSalaDaPranzo_ = 1
In questo modo la variabile _vGiocatoreInSalaDaPranzo_ diventa uguale a 1 quando il giocatore si trova almeno
una volta nella sala da pranzo.
Ora immaginiamo che esista (in un'altra stanza) una fotografia raffigurante proprio la sala da pranzo. Se la si
esamina vogliamo che compaia un messaggio diverso in base all'essere stati o no in sala da pranzo:
SE il giocatore esamina la fotografia:
SE _vGiocatoreInSalaDaPranzo_ è uguale a 1
ALLORA visualizza il messaggio "Questa fotografia mostra la sala da pranzo!"
ALTRIMENTI visualizza il messaggio "Questa fotografia mostra un luogo che non hai mai visto."
In pratica il messaggio che appare nel gioco dipende dal valore di una variabile che tiene conto del fatto di essere
stati (=1) o no (=0) nella sala da pranzo.
Attenzione infine che gli unici caratteri utilizzabili nel file variables.p+v sono:
qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890 /=:_
Il file [Link]
Il file [Link] viene generato tutte le volte che si crea un progetto. Tuttavia non è un file utilizzato da Pandor+
per generare il gioco, ma solamente una guida sintetica utile per elaborare gli altri file di progetto.
Val la pena tenerlo aperto in Notepad++ mentre lavoriamo sugli altri file.
Il file di progetto rooms.p+r
Il file rooms.p+r contiene le descrizioni degli ambienti che compongono la nostra avventura.
Prima di vedere come va scritto, introduciamo il concetto di oggetto. Per Pandor+ un oggetto è tutto quello che
può essere cliccato o toccato e al quale è associato un menu di azioni. Li possiamo dividere in due gruppi.
Gli oggetti "mobili" sono quelli che sono slegati dalle descrizioni degli ambienti. Sono oggetti che compaiono
dopo la frase "Qui puoi anche vedere …" che segue la descrizione dell'ambiente corrente oppure nell'inventario.
Possono essere oggetti che si possono prendere o no, persone, creature, etc.
Gli oggetti "fissi" sono invece oggetti che compaiono all'interno delle descrizioni degli ambienti. Di norma non
sono prendibili. Possono essere invece per esempio parole legate a delle direzioni.
Vediamo come esempio la prima stanza del progetto di default di Pandor+, in cui in rosso sono qui marcati gli
oggetti mobili e in blu quelli fissi.
Quindi: "fiume impetuoso" e "Est" sono oggetti fissi a cui sono collegati delle azioni che appaiono quando li
tocchiamo o ci clicchiamo sopra.
Degli oggetti mobili parleremo quando vedremo il file objects.p+o.
Vedremo ora come introdurre oggetti fissi nelle nostre descrizioni. Apriamo rooms.p+r e vediamo il contenuto.
In pratica ogni descrizione di un ambiente (o stanza) comincia con una linea di tipo { NUMERO }. Quindi se
decidiamo che nella nostra avventura ci sono 12 stanze, avremo 12 descrizioni precedute da una riga del tipo {XX}
con XX che va da 1 a 12 (il numero massimo è 79).
Se è presente un * prima del numero, significa che quella è la stanza di partenza del gioco.
Dopo ogni riga {NUMERO} va inserita una riga con l'identificatore della stanza. L'identificatore deve iniziare e
terminare con _ , in mezzo ai quali possiamo usare solo lettere e numeri. E' una buona regola usare come primo
carattere una r (che sta per room): questo aiuterà a non confonderci con gli identificatori delle variabili e degli
oggetti.
Successivamente possiamo inserire il testo della stanza.
!£$%&()=?'*+#-,;.:<>^{}_|§
Le caratteristiche degli oggetti verranno definite nel file di progetto objects.p+o nei quali si decidono anche quali
menu di azioni sono associati all’oggetto.
Il numero massimo di oggetti è 300, tuttavia i primi 9 oggetti sono riservati al motore del gioco.
Tipicamente nello sviluppo di un'avventura inserirete le descrizioni e gli oggetti un po' alla volta. Tuttavia per
semplicità ora aggiungiamo invece tutte le descrizioni per l’avventura "L’Occhio Purpureo" in un colpo solo, con
tanto di oggetti fissi.
Apriamo il file rooms.p+r e modifichiamolo come segue:
{01}
_rFiume_
{02}
_rForesta_
{03}
_rSentiero_
{*04}
_rCancello_
{05}
_rEsternoEdificio_
situato a {Sud|_sud05_|vai|_rStanzaPolverosa_}.
{06}
_rStanzaPolverosa_
{07}
_rIngresso_
{Ovest|_ovest07_|vai|_rCancello_} e {Sud|_sud07_|vai|_rStanzaMobiletto_}.
{08}
_rStanzaMobiletto_
Sei in una stanza con un {mobiletto|_mobiletto08_}.
{09}
_rStanzaBuia_|_vTestoStanzaBuia_
a {Sud|_sud09_|vai|_rCortile_}.
{10}
_rCortile_
{Est|_est10_|vai|_rGiardino_} ed {Ovest|_ovest10_|vai|_rIngresso_}.
{11}
_rGiardino_
{12}
_rSalaTrono_
Quando utilizziamo questa scorciatoia, non sarà possibile definire altri comportamenti per l'oggetto nel file
objects.p+o .
Un'altra cosa da notare è il fatto che l'identificatore termina con il numero 01. Questa è semplicemente una buona
regola per ricordarsi in che stanza è definito l'oggetto, in modo ad esempio da distinguerlo da altri "est" presenti in
altre stanze.
La prima cosa da notare è che la seconda riga presenta l'identificatore _rStanzaBuia_ seguito da | e poi
dall'identificatore della variabile _vTestoStanzaBuia_.
Questo serve per dire a Pandor+ che la descrizione della stanza dipende dal valore della variabile
_vTestoStanzaBuia_ .
Se questa variabile è 1, il testo mostrato sarà il primo:
Sei in una stanza buia.
a {Sud|_sud09_|vai|_rCortile_}.
I testi sono separati col carattere speciale § . Si possono al massimo definire 5 testi per una stanza.
La variabile _vTestoStanzaBuia_ verrà modificata nel file conditions.p+a che vedremo più avanti, in base al fatto
che il giocatore abbia con se la lampada accesa o no.
Modificato e salvato il file, proviamo a generare l'avventura con Pandor+. Verrà visualizzato un errore, relativo al
fatto che l'oggetto _fiume01_ non è definito nel file objects.p+o:
Nota: se stavamo già testando in precedenza il gioco sul nostro browser, dobbiamo sempre utilizzare l'opzione
termina (e conferma) per riportarci alla schermata iniziale e poi effettuare il refresh della pagina (tasto F5).
Un ultima nota: se ci serve aggiungere dei nostri commenti nel file rooms.p+r, lo possiamo fare utilizzando delle
righe che iniziano con // , ad esempio:
{12}
Vedremo comunque un esempio di come creare sotto-menu con le voci che desideriamo.
Il file di progetto objects.p+o
Dopo averli introdotti nelle descrizioni, gli oggetti fissi vanno definiti in objects.p+o. Allo stesso modo
aggiungeremo gli oggetti mobili, specificandone le caratteristiche. Ogni oggetto va definito con una sequenza di
linee, in questo modo:
Fondamentalmente specificheremo per ogni oggetto solo le proprietà che ci interessano. Alcune permettono di
avere dei comportamenti "automatici":
aggiungere al menu di azioni dell'oggetto l'azione "esamina" e definire il messaggio che appare a seguito
dell'azione. Si può anche specificare che a seguito dell'azione compaia un nuovo oggetto (o più oggetti),
indicando quale altro messaggio deve apparire
aggiungere al menu di azioni dell'oggetto l'azione "usa con" con sotto-menu la lista di oggetti
dell'inventario
aggiungere al menu di azioni dell'oggetto un comando di movimento, indicando in quale ambiente porta il
giocatore
aggiungere al menu di azioni dell'oggetto l'azione "parla con" e definire il messaggio che appare a seguito
dell'azione la prima volta e le volte successive alla prima
Vediamo ora le righe e proprietà una alla volta. In Notepad++ le proprietà corrette saranno colorate in blu, in modo
da aiutarci a riconoscere subito eventuali errori. Ad esempio scrivendo [Link] invece di [Link]:
{identificatore_oggetto}
Specifica quale oggetto viene definito, ad esempio: {_lampada01_}. Per l'identificatore vanno usati solo lettere e
numeri e il primo e ultimo carattere deve essere: _
Conviene introdurre nell'identificatore l'indice della stanza nella quale si trova l'oggetto, per evitare doppioni di
oggetti simili in stanze diverse.
L'idea di base è quindi che le proprietà direction examinable takeable wearable talkable
permettono di creare oggetti di cui non dovremo aggiungere "manualmente" menu o gestire azioni e
comportamento.
La proprietà usable invece fa si che venga aggiunto automaticamente al menu la voce "usa con" con la lista
degli oggetti dell'inventario. Le possibili conseguenze delle interazioni tra oggetti andranno invece specificate
"manualmente" in actions.p+a.
:direction:
Se vogliamo aggiungere al menu dell'oggetto un azione di movimento che porta in un'altra stanza possiamo
valorizzare questa proprietà con l'identificatore della stanza. Ad esempio: _rForesta_ . Non va di norma utilizzata
per oggetti mobili.
:[Link]:
Se è stata specificata direction, qui va specificata l'azione che vogliamo compaia nel menu. Ad esempio: entra. Se
non viene specificato nulla, di default verrà associata l'azione "vai" (testo {25} di messages.p+r).
:[Link]:
E' la descrizione che appare nei menu, ad esempio: lampada. Questa proprietà va valorizzata per gli oggetti mobili
mentre non va specificata nel caso di oggetti fissi.
:[Link]:
E' la descrizione che appare nell'inventario e dopo l'indicazione "Puoi anche vedere …", ad esempio: una vecchia
lampada ad olio. Questa proprietà va valorizzata per gli oggetti mobili mentre non va specificata nel caso di
oggetti fissi.
:room:
Qui va specificata la stanza in cui si trova l'oggetto nel caso sia un oggetto mobile. Ad esempio: _rCantina_ . Nel
caso in cui l'oggetto non sia in nessuna stanza, va specificato _rOUT_. Nel caso in cui sia nell'inventario va messo
_rTAKEN_, se è indossato _rWORN_ (in questo caso l’oggetto deve avere la proprietà wearable).
:weight:
:[Link]:
E' il peso dell'oggetto se indossato. Di solito sarà inferiore al peso se non indossato. Ad esempio: 5 . Va specificato
solo se l’oggetto ha la proprietà wearable.
:tag:
E' un valore intero, da 0 a 65533. Rimane fisso nel corso di tutta l'avventura. Può essere usato per "marcare" gli
oggetti secondo le nostre esigenze.
:[Link]:
Se è selezionata la proprietà examinable nella seconda linea, questo è il messaggio che verrà mostrato a seguito
dell'azione "esamina". Ad esempio: E' una vecchia lampada rotta. Se non viene specificata questa proprietà verrà
visualizzato un messaggio di default tipo "Non trovi nulla di interessante" (testo {30} di messages.p+r).
Una nota importante, che riguarda anche [Link], [Link], [Link] è che si può fare in modo di separare
il testo in più messaggi (intervallati dal comando Continua) tramite il carattere §. Ad esempio:
Un'altra possibilità è far si che il testo venga selezionato a caso da Pandor+ tra più testi separati da |
Ad esempio:
Nel caso di utilizzo contemporaneo di | e §, la priorità va a §.
:found:
Se decidiamo che a seguito dell'azione "esamina" deve comparire un nuovo oggetto mobile, qui va specificato
l'identificatore del nuovo oggetto. Ad esempio: _moneta00_ . L'oggetto ovviamente comparirà un'unica volta nella
stanza in cui si trova il giocatore. Nel caso volessimo far apparire più oggetti, vanno elencati separati da "|", ad
esempio: _moneta00_|_spada00_
:[Link]:
Se è stato specificata la proprietà found, qui scriveremo il messaggio aggiuntivo che compare (un'unica volta)
all'azione "esamina". Se non viene specificata questa proprietà, a seguito dell'azione "esamina", verrà visualizzato
un messaggio di default tipo "Hai trovato qualcosa" (testo {31} di messages.p+r).
:[Link]:
Se è selezionata la proprietà talkable nella seconda linea, questo è il messaggio che verrà mostrato a seguito
dell'azione "parla con". Ad esempio: Il vecchio ti dice <<Buona fortuna !>> . Se non viene specificata questa
proprietà, a seguito dell'azione "parla con", verrà visualizzato un messaggio di default tipo "Parli, ma non succede
niente" (testo {45} di messages.p+r).
:[Link]:
Se è selezionata la proprietà talkable nella seconda linea, questo è il messaggio che verrà mostrato a seguito
dell'azione "parla con", dalla seconda volta in poi. Ad esempio: Il vecchio non ha più nulla da dirti.
:menu:
Qui possiamo aggiungere le voci del menu che compaiono quando si tocca l'oggetto. Vanno aggiunte solo le voci
che non sono già automaticamente presenti avendo inserito le proprietà direction examinable
takeable wearable talkable usable.
Per aggiungere altre voci inseriamo delle righe del tipo:
++"azione";
Ad esempio, immaginando di considerare un oggetto "porta", per aggiungere due azioni "apri" e "chiudi":
:menu:
++"apri";
++"chiudi";
Per aggiungere invece delle voci di menu con delle sotto-voci (come succede per "usa con"), vanno inserite delle
righe in questo modo:
-"sotto-voce-1";
-"sotto-voce-2";
...
++"azione";
Ad esempio, per aggiungere anche l'azione "bussa" e come sotto-voci "una volta", "due volte","tre volte":
:menu:
++"apri";
++"chiudi";
-"una volta";
-"due volte";
-"tre volte";
++"bussa";
if ( _vStatoPorta_ == 0 )
++"apri";
else
++"chiudi";
-"una volta";
-"due volte";
-"tre volte";
++"bussa";
if (a==b)
else
// questo blocco viene eseguito in caso contrario, cioè a e b non sono uguali
}
Naturalmente si possono creare condizioni più complesse, combinando più sotto-condizioni e usando i connettivi
logici AND (&&) e OR (||):
if ((a==b) || (c==d))
if ( _vStatoPorta_ == 0 )
++"apri";
else
++"chiudi";
-"una volta";
-"due volte";
-"tre volte";
++"bussa";
if ( f_istaken("_scure_") == true )
++"sfascia";
f_istaken è una funzione di Pandor+ che permette di sapere se un certo oggetto è stato preso dal giocatore. La
funzione restituisce true o false, cioè vero o falso.
In generale per funzioni che restituiscono true o false, possiamo anche scrivere condizioni in modo semplicato.
if ( f_istaken("_scure_") == true )
è equivalente a:
if ( f_istaken("_scure_") )
if ( f_istaken("_scure_") == false )
è equivalente a:
if ( !f_istaken("_scure_") )
Oltre a f_istaken ci sono molte altre funzioni disponibili per le nostre condizioni: le vedremo meglio quando
tratteremo il file conditions.p+a
Un'ultima nota sulla proprietà menu degli oggetti. Qualora avessimo bisogno di aggiungere tutti gli oggetti
dell'inventario, possiamo farlo tramite la funzione f_inventory( ). Ad esempio, per replicare manualmente le sotto-
voci fornite dalla proprietà usable, basta scrivere:
:menu:
-f_inventory();
-"te stesso";
++"usa con";
!£$%&/()=?^'|[]{}*+#-_,;.:<>§"
Apriamo objects.p+o e modifichiamolo come segue, per aggiungere tutti gli oggetti de "L'Occhio Purpureo".
// -------------
// oggetti fissi
// -------------
{_fiume01_}
:examinable
:menu:
++"nuota";
{_foresta02_}
:examinable
:[Link]:
:[Link]:
E' una foresta molto sinistra... | E' una foresta minacciosa... | Tutto sembra orribile qui...
{_cancello04_}
:examinable
:direction: _rIngresso_
:[Link]: entra
:[Link]:
{_edificio05_}
:menu:
++"esamina";
{_mobiletto08_}
:examinable usable
:found: _lampadaSpenta00_
{_trono12_}
:examinable usable
:menu:
++"siedi";
{_catena12_}
:examinable
:menu:
-"una volta";
-"due volte";
-"tre volte";
++"tira";
// --------------
// oggetti mobili
// --------------
{_sacchettoPieno05_}
:[Link]: sacchetto
:room: _rEsternoEdificio_
:weight: 20
:menu:
++"vuota";
{_sacchettoVuoto00_}
:[Link]: sacchetto
:room: _rOUT_
:weight: 5
{_biglie00_}
:[Link]: biglie
:room: _rOUT_
:weight: 15
:found: _occhio00_
{_occhio00_}
:examinable takeable
:[Link]: occhio
:room: _rOUT_
:weight: 5
:[Link]:
{_mattone03_}
:[Link]: mattone
:[Link]: un mattone
:room: _rSentiero_
:weight: 40
:menu:
if (f_istaken("_mattone03_"))
++"lancia";
{_pistolaCarica00_}
:takeable usable
:[Link]: pistola
:room: _rOUT_
:weight: 30
:menu:
if (f_istaken("_pistolaCarica00_"))
++"spara";
{_pistolaScarica00_}
:takeable usable
:[Link]: pistola
:room: _rOUT_
:weight: 20
{_lampadaSpenta00_}
:takeable
:[Link]: lampada
:room: _rOUT_
:weight: 20
:menu:
++"accendi";
{_lampadaAccesa00_}
:takeable
:[Link]: lampada
:room: _rOUT_
:weight: 20
:menu:
++"spegni";
{_lampadaRotta00_}
:takeable
:[Link]: lampada
:room: _rOUT_
:weight: 20
{_guanti11_}
:[Link]: guanti
:room: _rGiardino_
:weight: 20
:[Link]: 5
{_ispettore00_}
:examinable usable
:[Link]: ispettore
:room: _rOUT_
:menu:
if (f_istaken("_pistolaCarica00_"))
++"spara";
if (f_istaken("_mattone03_"))
++"colpisci";
{_cadavere00_}
:examinable usable
:[Link]: cadavere
:[Link]: R.I.P. !
:room: _rOUT_
{_pappagallo07_}
:examinable talkable
:[Link]: pappagallo
:room: _rIngresso_
Diversi oggetti hanno la proprietà examinable. Nel caso in cui non sia specificato un messaggio per "esamina"
tramite proprietà [Link] il gioco risponderà automaticamente con messaggi del tipo "Niente di
interessante…". Lo stesso accade per usable: se in actions.p+a non sarà specificata una certa interazione tra gli
oggetti, il gioco risponderà automaticamente con un messaggio tipo "Non sembra funzionare" (si veda {33} in
messages.p+r).
Da notare anche che sono state utilizzate linee di commento. I commenti vanno sempre in linee separate che
iniziano con //.
{_fiume01_}
Avendo la proprietà examinable, verrà aggiunta in automatico l'azione "esamina" al menu. Viene definito anche il
messaggio associato. Viene aggiunta in menu l'azione "nuota".
{_foresta02_}
Avendo la proprietà examinable, verrà aggiunta in automatico l'azione "esamina" al menu. Viene definito anche il
messaggio associato.
{_cancello04_}
Questo oggetto avrà le azioni "esamina" e "entra". Da notare che nella proprietà [Link] il messaggio è su
più righe.
:[Link]:
In generale nei messaggi Pandor+ trasforma sempre più spazi in uno solo e non considera i salti di linea.
Se volessimo forzare un "a capo" dopo la virgola, dobbiamo usare il carattere speciale ^:
:[Link]:
{_edificio05_}
Questo oggetto non ha nessuna proprietà "automatica" definita. Viene invece aggiunta a mano l'azione "esamina"
in menu, e il risultato dell'azione (mostreremo dei messaggi) sarà diverso di volta in volta e lo gestiremo in
actions.p+a
{_mobiletto08_}
Viene aggiunta l'azione "esamina" a seguito della quale comparirà la prima volta una lampada (oggetto
_lampadaSpenta00_ ). Viene anche aggiunta "usa con" tramite usable: come sotto-voci appariranno
automaticamente gli oggetti dell'inventario e "te stesso".
{_trono12_}
Per il trono vengono aggiunti "esamina" e "usa con". Viene aggiunta manualmente l'azione "siedi".
{_catena12_}
Anche per la catena viene aggiunto automaticamente"esamina" mediante la proprietà examinable. Verrà invece
aggiunta manualmente un'azione "tira" con sotto menu "una volta" "due volte" "tre volte". Il risultato dell'azione,
gestito in action.p+a, dipenderà dall'avere o no l'occhio purpureo, i guanti e se tiriamo la catena il numero di volte
giusto.
Per oggetti che possono avere più "stati" (lampada accesa – lampada spenta, ad esempio) è necessario definire un
oggetto "gemello" per ogni stato diverso dell'oggetto che ci occorre. Di solito, variano al massimo la descrizione,
il messaggio se esaminato e il peso. Ovviamente un solo oggetto "gemello" alla volta può avere la proprietà room
diversa da _rOUT_. Ad esempio inizialmente l'oggetto lampada spenta sarà nella stanza _rStanzaMobiletto_ e
l'oggetto lampada accesa sarà fuori dal gioco, ovvero la sua room sarà _rOUT_
Quando accenderemo la lampada spenta, la sua room diventerà _rOUT_ mentre la room della lampada accesa
diventerà quella della lampada spenta.
Il trucco sta insomma nel far sparire dal gioco un oggetto a favore del suo gemello. Per il giocatore tutto questo
sarà ovviamente trasparente, e penserà di aver a che fare con lo stesso oggetto che cambia stato.
{_sacchettoPieno05_}
{_sacchettoVuoto00_}
Il sacchetto può o no contenere delle biglie. Inizialmente viene piazzato "sacchetto pieno" nella stanza
_rEsternoEdificio_, mentre il "sacchetto vuoto" è fuori dal gioco, room è infatti _rOUT_. Per il "sacchetto pieno",
aggiungiamo manualmente l’azione "vuota" (sarà proprio questa azione a far sparire il sacchetto pieno e a far
comparire quello vuoto).
{_biglie00_}
Quando vuotiamo il sacchetto, compariranno delle biglie. Se le esaminiamo, comparirà l’Occhio Purpureo. A tal
scopo, sono utilizzate le proprietà found e [Link].
{_occhio00_}
Salta fuori automaticamente esaminando le biglie. Inizialmente la sua room è quindi _rOUT_
{_mattone03_}
Causerà la morte del giocatore se tenteremo di nuotare con esso nel nostro inventario. Viene aggiunta l’azione
"lancia" (se lo abbiamo con noi) che può far credere di poterlo utilizzare contro l’Ispettore delle tasse...
{_pistolaCarica00_}
{_pistolaScarica00_}
La pistola potrà essere carica (1 colpo) o scarica. Potrà essere usata per uccidere l’ispettore delle tasse. Viene
aggiunta l’azione "spara" in menu, ma solo se la pistola è nell'inventario.
{_lampadaSpenta00_}
{_lampadaAccesa00_}
{_lampadaRotta00_}
La lampada sarà indispensabile per uscire dalla stanza buia, o il giocatore non ne uscirà più. Vengono aggiunte le
azioni "accendi" e "spegni". Inoltre esisterà la possibilità di romperla se usata col mattone.
{_guanti11_}
I guanti sono l’unico oggetto indossabile (proprietà wearable) dell’avventura. Il peso se indossati è 5, contro 20 se
trasportati. Serviranno a tirare la catena senza restare fulminati!
{_ispettore00_}
Ecco il nostro nemico nell’avventura. Comparirà un’unica volta (come oggetto mobile, ovviamente non
prendibile) e ci ruberà un oggetto a meno di non ucciderlo con la pistola. La sua comparsa sarà casuale e avverrà
nella stessa stanza del giocatore (a meno che non sia la foresta o la stanza buia) dopo 20 azioni dall’inizio
dell’avventura. Vengono aggiunti le azioni "spara" se il giocatore possiede la pistola e "colpisci" se possiede il
mattone.
{_cadavere00_}
Se uccidiamo l’ispettore, al suo posto comparirà il suo cadavere. Inizialmente questo oggetto è fuori dal gioco
(room è posto a _rOUT_)
{_pappagallo07_}
Il pappagallo ha la proprierà talkable: significa che verrà aggiunta in automatico l'azione "parla con".
Il messaggio che compare la prima volta che l'azione viene fatta è specificato in [Link]. Dalla seconda volta in
poi, comparirà invece il messaggio specificato da [Link] .
I file di progetto conditions.p+a e counters.p+a
Nel corso dell'avventura le posizioni degli oggetti cambiano, il giocatore passa attraverso diverse stanze e compie
azioni che mettono in moto degli eventi.
Il codice scritto nei file counters.p+a e conditions.p+a ci permette di definire dei mutamenti del gioco o dei
messaggi da mostrare in base a determinate condizioni. Alcuni esempi potrebbero essere:
- se il giocatore passa per una stanza, far apparire un oggetto da qualche parte
- se il giocatore rimane troppo tempo in una stanza, fargli succedere una disgrazia o addirittura far
terminare il gioco ("L'aria velenosa ti uccide... Dovevi uscire prima!")
- aggiungere dopo la descrizione della stanza un testo che avverte il giocatore di qualcosa ("Senti uno
sparo!" , "Sei allo stremo delle forze, devi mangiare qualcosa!" …)
- modificare la descrizione di una stanza in determinate circostanze (ad esempio presenza di luce o no)
Più in generale, ogni qualvolta il giocatore compie un'azione, il gioco "risolve" l’azione e – prima di
mostrare il risultato della stessa - valuta anche il codice presente in counters.p+a e conditions.p+a.
E’ importante ora fare una distinzione sul tipo di azione nel gioco. Per azione intendiamo il comando che il
giocatore compie cliccando su un oggetto qualsiasi dell’avventura. Alcune azioni corrispondono ad una mossa del
giocatore (anche se non porta a nessun risultato) come ad esempio "esamina"o "prendi". Nel caso invece di un
comando del tipo "aiuto", "carica" o "salva" diremo che si tratta di un azione "vuota". Anche all'inizio del gioco, la
prima azione è vuota e lo stesso accade tutte le volte che viene fatto un refresh della pagina web del gioco. Le
azioni vuote insomma non sono mosse e non devono modificare mai lo "stato" del gioco e del giocatore.
In generale:
– in counters.p+a potremo modificare le variabili del gioco tutte le volte che viene compiuta una mossa
(azione non vuota) dal giocatore, per tener conto del passaggio "di tempo" o del numero di mosse che il
giocatore fa in una determinata circostanza
– in conditions.p+a potremo cambiare posizione degli oggetti, posizione del giocatore e mostrare un
messaggio "popup" o del testo aggiuntivo dopo la descrizione della stanza in base a condizioni che
dipendono dallo stato delle variabili o degli oggetti o del giocatore
Anche se alcuni dettagli saranno più chiari dopo aver visto anche actions.p+a, la sequenza (semplificata) del ciclo
di operazioni eseguite dal motore del gioco ad ogni azione è questa:
1. Se è un azione vuota (ad esempio un refresh della pagina) vai al punto 5, altrimenti prosegui al punto 2
2. viene valutato actions.p+a: se qui l'azione è gestita allora esegui il codice e poi vai al punto 4, altrimenti
continua al punto 3. Nel codice può essere chiamata la funzione f_show (per mostrare un messaggio
"popup")
3. viene gestita l'azione in modo automatico dal motore del gioco, in base alla proprietà degli oggetti
coinvolti nell'azione e chiamata f_show con l’eventuale messaggio di risposta ("Niente di interessante...",
"Hai troppe cose!", etc)
4. viene eseguito counters.p+a
5. viene eseguito conditions.p+a. In questa parte possono essere chiamate f_show per far visualizzare un
messaggio popup oppure f_append per aggiungere del testo dopo la descrizione della stanza
6. se è stata chiamata f_show, viene mostrato un messaggio "popup" dal quale si esce toccando "Continua"
7. se è stato chiamato f_lose, il gioco termina (schermata di fallimento)
8. se è stato chiamato f_win, il gioco termina (schermata di successo)
9. viene mostrata la descrizione in base alla stanza corrente ed agli oggetti presenti. Viene eventualmente
aggiunto un messaggio addizionale in grassetto maiuscoletto se è stata chiamata f_append
In pratica:
– in caso di azioni vuote viene eseguito solamente conditions.p+a
– In caso di mosse (azioni non vuote) vengono invece eseguiti actions.p+a, counters.p+a, conditions.p+a
L'importanza di questa distinzione sarà chiara tra poco.
Vediamo nel dettaglio il file counters.p+a. Dentro ad esso vanno modificate le variabili per tener conto del
"tempo che passa", ovvero del fatto che il giocatore si è realmente "mosso" in termini temporali o spaziali nel
gioco.
_vMOVES_ = _vMOVES_ + 1;
Questo pezzo di codice fa si che _vMOVES_ sia incrementato ad ogni azione. In questo modo _vMOVES_ può
essere usata per tener conto del "passaggio di tempo", ovvero di quante mosse abbia fatto il giocatore dall’inizio
del gioco. Questo è molto utile per determinare eventi nel gioco che dipendono dal "tempo". Ad esempio
aggiungere un testo con scritto "Il pendolo fa DONG!" ogni tot mosse.
Vediamo altri esempi. Se volessimo incrementare la variabile _vMosseInCantina_ solo se il giocatore si trova
nella stanza _rCantina_ , il codice di counters.p+a diventerebbe:
_vMOVES_ = _vMOVES_ + 1;
if ( f_isplayerin("_rCantina_") == true )
_vMosseInCantina_ = _vMosseInCantina_ + 1;
In generale le condizioni che scriviamo possono utilizzare le variabili definite in variables.p+v e una serie di
funzioni messe a disposizione da Pandor+, qui di seguito elencate:
giocatore
ad esempio "_rForesta_"
In counters.p+a vanno quindi modificate le variabili per tener conto che il giocatore ha effettivamente eseguito
una azione non vuota in una certa circostanza.
Veniamo ora invece lo scopo di conditions.p+a.
Fondamentalmente in questo file vanno invece inserite le condizioni che portano a modifiche allo stato
dell’avventura. Tuttavia, a differenza di counter.p+a, questo file viene eseguito dal motore del gioco anche in caso
di azioni vuote (come un refresh della pagina web).
Ne consegue che quando scriviamo il codice in questo file, dobbiamo porci la seguente domanda: se il giocatore
effettuasse 1000 volte il refresh della pagina, lo stato del gioco rimane coerente?
Facciamo un esempio. Vogliamo che se il giocatore si trova in cantina, venga incrementata la variabile
_vQuanteVolteGiocatoreInCantina_ ad ogni mossa del giocatore. Quando questa variabile diventa uguale a 5
(ovvero se il giocatore ha fatto 5 mosse in cantina) il giocatore muore.
Immaginiamo di scrivere il seguente codice in conditions.p+a:
if ( f_isplayerin("_rCantina_") )
_vQuanteVolteGiocatoreInCantina_ = _vQuanteVolteGiocatoreInCantina_ + 1;
if ( _vQuanteVolteGiocatoreInCantina_ == 5 )
f_show("OH NO! Un enorme ragno scende dal soffitto e ti inietta il suo veleno mortale...");
f_lose();
// da mettere in counters.p+a
if ( f_isplayerin("_rCantina_") )
_vQuanteVolteGiocatoreInCantina_ = _vQuanteVolteGiocatoreInCantina_ + 1;
// da mettere in conditions.p+a
if ( _vQuanteVolteGiocatoreInCantina_ == 5 )
f_show("OH NO! Un enorme ragno scende dal soffitto e ti inietta il suo veleno mortale...");
f_lose();
In questo modo, anche se facciamo molte volte il refresh della pagina, _ vQuanteVolteGiocatoreInCantina_ non
viene incrementato.
Se ora volessimo che, se siamo in cantina, venga anche aggiunto (dopo la descrizione della stanza) un testo
"minaccioso", basterebbe aggiungere a conditions.p+a il seguente codice:
if ( f_isplayerin("_rCantina_") )
E' corretto che questa ultima condizione stia in conditions.p+a? Si, perchè anche in caso di refresh della pagina
web è corretto che ogni volta appaia la frase minacciosa.
<05>
_rStanzaDellaFontana_|_vTestoStanzaDellaFontana_
Sei dentro una grande stanza con una {fontana|_fontana05_} che non sgorga nulla.
_vTestoStanzaDellaFontana_ è una variabile inizialmente uguale a 1. Come abbiamo visto, vuol dire che la
descrizione della stanza mostrata nel gioco sarà la prima:
Sei dentro una grande stanza con una zampillante {fontana|_fontana05_}.
Vogliamo però che dopo 50 azioni dall’inizio dell’avventura, la descrizione della stanza diventi la seconda:
Sei dentro una grande stanza con una {fontana|_fontana05_} che non sgorga nulla.
if ( _vMOVES_ >= 50 )
_vTestoStanzaDellaFontana_ = 2;
Altro esempio. Vogliamo ora che se il giocatore passa almeno una volta per la stanza della fontana, la variabile
_vGiocatorePassatoPerStanzaFontana_, inizialmente uguale a 0, venga messa a 1:
if ( _vMOVES_ >= 50 )
_vTestoStanzaDellaFontana_ = 2;
if ( f_isplayerin("_rStanzaDellaFontana_") )
_vGiocatorePassatoPerStanzaFontana_ = 1;
In questo modo avremo "registrato" il passaggio del giocatore nella stanza della fontana attraverso l’uso di una
variabile _vGiocatorePassatoPerStanzaFontana_ .
Da questo momento, in qualsiasi altra condizione, per sapere se siamo passati per la stanza della fontana basterà
chiedersi se _vGiocatorePassatoPerStanzaFontana_ è uguale a 1.
Aggiungiamo ad esempio ora che se il giocatore si trova nella stanza _rSalone_ ed è passato per la stanza della
fontana, verrà mostrato un messaggio ma una volta sola. Per farlo abbiamo bisogno di un'altra variabile,
_vMostratoMessaggioOmbra_, inizialmente uguale a 0.
if ( _vMOVES_ >= 50 )
_vTestoStanzaDellaFontana_ = 2;
if ( f_isplayerin("_rStanzaDellaFontana_") )
_vGiocatorePassatoPerStanzaFontana_ = 1;
if ( (f_isplayerin("_rSalone_"))
&& (_vGiocatorePassatoPerStanzaFontana_ == 1)
&& (_vMostratoMessaggioOmbra_ == 0)
_vMostratoMessaggioOmbra_ = 1;
}
Diamo un'occhiata ora a come apparirebbe il codice appena visto in Notepad++.
Come si può vedere le parole chiave e i simboli di programmazione come if, &&, f_show,etc sono in blu, i testi
sono in nero e le variabili sono in viola chiaro. Eventuali parti non conosciute sono segnalate in rosso (sfondo
giallo) per permettere di riconoscerle e verificarne la correttezza più facilmente. Ad esempio, se sbagliassimo di
scrivere il nome della funzione f_show:
Il messaggio verrà mostrato un’unica volta in quanto _vMostratoMessaggioOmbra_ diviene 1 e la condizione non
può più verificarsi (perchè per poter essere eseguita, _vMostratoMessaggioOmbra_ deve essere 0).
f_show è una funzione che permette di mostrare messaggi popup che "bloccano" il gioco finchè non si tocca
Continua. Ecco un esempio di messaggio popup:
Se viene chiamata più volte f_show, Pandor+ accorpa i vari testi in un unico messaggio.
Ecco l'elenco di funzioni che possiamo utilizzare per agire sul gioco:
questi sotto-testi
dei sotto-testi
In alternativa:
Una delle funzioni più importanti è f_append, che permette di aggiungere testo dopo quello della descrizione della
stanza e degli eventuali oggetti presenti in essa. Eccone un esempio:
Da notare che il messaggio sarà sempre in maiuscoletto grassetto, e se f_append viene chiamata più volte, Pandor+
accoderà i vari testi.
Ecco un esempio di uso di f_append in conditions.p+a. Vogliamo far aggiungere al testo della stanza _rGrotta_
una frase scelta a caso tra 3. La frase appare a intervalli casuali di 5-15 azioni:
if ( vProssimaScossa == 0 )
// della grotta
_vProssimaScossa_ = 0;
// aggiungi una delle tre frasi, scegliendola a caso, dopo la descrizione della stanza
f_append("
");
}
Infine ecco un’altra funzione che ci può essere utile nelle nostre condizioni: f_getcurrentweight() che ritorna il
peso di tutto quello che trasportiamo. Ad esempio, con troppo peso, il giocatore potrebbe affondare nelle sabbie
mobili appena mette piede nella relativa stanza:
f_lose();
Chiamare la funzione f_lose significa andare alla schermata di "fallimento" del gioco (vedi fail.p+i). Al contrario,
chiamare f_win significa andare a quella di successo (success.p+i) e terminare l’avventura.
Prima di vedere cosa scrivere in counters.p+a e conditions.p+a per l'Occhio Purpureo, una nota sui caratteri
ammessi in questi file. Questi sono:
qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890 àÀáÁèÈéÉìÌíÍòÒóÓùÙúÚ
!"£$%&/()=?^'|[]{}*+#-_,;.:<>§
Dedichiamoci ora alle condizioni dell’avventura "L’Occhio Purpureo". Apriamo counters.p+a e modifichiamolo
come segue:
_vMOVES_ = _vMOVES_ + 1;
if ( f_isplayerin("_rForesta_") == true )
_vAzioniInForesta_ = _vAzioniInForesta_ + 1;
else
_vAzioniInForesta_ = 0;
}
// se è comparso l'ispettore, incrementa vAzioniConIspettore
if ( _vStatoIspettore_ == 1 )
_vAzioniConIspettore_ = _vAzioniConIspettore_ + 1;
La prima condizione incrementa _vAzioniInForesta_ solo se il giocatore si trova nella foresta, e in caso contrario
la azzera. In questo modo ogni volta che il giocatore esce dalla foresta il conteggio delle mosse fatte nella stessa
riparte da 0.
La seconda condizione invece incrementa _vAzioniConIspettore_ se _vStatoIspettore_ è uguale a 1. Vedremo
quando _vStatoIspettore_ viene messa a 1 e perchè.
_vTestoStanzaBuia_ = 2;
else
_vTestoStanzaBuia_ = 1;
// se sono nella foresta per una o due azioni, scrivi una frase aggiuntiva
if ( f_isplayerin("_rForesta_") )
if ( _vAzioniInForesta_ == 1 )
else if ( _vAzioniInForesta_ == 2 )
}
// se non è mai stata valorizzata, mettiamo in vTirareCatena il numero di volte
if ( _vTirareCatena_ == 0 )
_vTirareCatena_ = f_getrandomint(1,3);
f_show("
");
f_lose();
return;
// se non sono nella foresta o nella stanza buia e se non è mai apparso.
if (
( _vStatoIspettore_ == 0 )
_vStatoIspettore_ = 1;
}
// se l'ispettore è comparso e abbiamo fatto una azione non vuota in sua presenza
_vStatoIspettore_ = 2;
// a caso e se ne va
var lConta = 0;
var lOggettoScelto;
var lPunteggio = 0;
foreachobject{
if (f_istaken(id))
lConta = lConta + 1;
lPunteggio = lTemp;
lOggettoScelto = id;
if (lConta > 0)
// l'ispettore scompare
f_moveout("_ispettore00_");
// l'oggetto scompare
f_moveout(lOggettoScelto);
f_show(
e preleva uno dei tuoi oggetti come anticipo! Per tua fortuna
poi se ne va via...");
else
f_show(
"L'ispettore delle tasse esige il riscatto per i tuoi debiti ma tu non hai nulla
f_lose();
return;
Inizialmente vi sono delle condizioni per modificare la descrizione della stanza buia se possediamo la lampada
accesa e per aggiungere alcune frasi di avvertimento tramite f_append se siamo nella foresta.
Viene ucciso il giocatore se rimane per più di due azioni nella foresta.
Viene deciso il numero di volte che la catena va tirata. Poiché _vTirareCatena_ all’inizio è 0, la prima volta la
condizione è verificata e _vTirareCatena_ viene valorizzata con un valore casuale da 1 a 3. Da questo momento in
poi quindi la condizione non potrà più essere verificata perché _vTirareCatena_ non sarà mai più uguale a 0.
Viene fatto comparire l’ispettore dopo 20 azioni dall’inizio dell’avventura. L’ispettore compare nella stanza del
giocatore. Quando appare, _vStatoIspettore_ viene messo a 1.
Se l’ispettore è comparso e abbiamo già fatto un’azione con lui presente (che non ha portato alla sua morte), egli
deruberà il giocatore di un oggetto tra quelli trasportati (se ne ha) oppure si finirà in gattabuia e il gioco terminerà.
In questa parte del codice viene fatto uso di variabili locali javascript, ad esempio:
var lConta = 0;
Queste variabili perdono il loro valore una volta usciti dalla condizione in cui sono definite. Possono contenere
interi o testi, a seconda delle necessità. Vengono definite con la sintassi: var nomevariabile = valore iniziale;
Per il nome di queste variabili, non usate mai il carattere _ !
Tramite un ciclo foreachobject vengono poi passati in rassegna tutti gli oggetti dell’avventura e scelto tramite un
punteggio casuale quale di quelli trasportati prelevare.
foreachobject{ … } è un istruzione speciale che "cicla" il codice scritto tra { } per ogni oggetto dell'avventura, il
cui identificatore è disponibile nella variabile locale id, definita implicitamente. Ad esempio:
var ls = "";
foreachobject{
if ( f_tag(id) == 1 )
ls = ls + f_desclong(id) + "^";
Si noti infine che tutte le volte che il giocatore "muore", viene bloccata l'esecuzione di conditions.p+a tramite
l’istruzione return, in quanto non ci interessa considerare altre condizioni.
Il file di progetto actions.p+a
Siamo arrivati all’ultimo file di progetto che modificheremo. Nel file actions.p+a vanno intercettate tutte le azioni
fatte dal giocatore e che non sono gestite automaticamente dal motore del gioco (come ad esempio
"prendi"/"lascia" per oggetti con proprietà takeable, "esamina" per la proprietà examinable, etc).
Quando il giocatore esegue un’azione non vuota, action.p+a viene passato in rassegna per vedere se abbiamo
deciso di gestirla.
Se l'azione non è gestita da noi e non è tra quelle "automatiche" basate sulle proprietà degli oggetti, ci penserà il
motore del gioco a rispondere con un messaggio "Non sembra funzionare!" o simili.
Vediamo quindi come catturare un’azione. Dovremo creare delle condizioni ( if …) utilizzando:
– la funzione f_action
– le funzioni già viste in conditions.p+a (f_istaken, f_whereis, etc. )
– le variabili definite in variables.p+v (ad esempio: _vApertaCassa_ )
f_action è una funzione che ritorna true se è stata compiuta una certa azione. Ad esempio:
...
...
Il codice tra { } verrà eseguito solo se il giocatore ha toccato l'oggetto _ruscello14_ , selezionato l'azione "usa con"
e poi _borraccia00_ , ovvero ruscello – usa con – borraccia
f_action può essere usata anche per azioni senza sotto-voci, come ruscello – bevi
if (f_action("_ruscello14_", "bevi"))
...
Dentro il corpo dell' if, possiamo poi specificare ulteriori condizioni basate sulle funzioni già viste per
conditions.p+a, oppure sulle variabili definite da noi:
if ( _vBorracciaPiena_ == 0 )
_vBorracciaPiena_ = 1;
}
else
return true;
La condizione quindi verrà eseguita solo se gli oggetti coinvolti sono la borraccia e il ruscello e l’azione è "usa
con". In realtà la funzione accetta anche l’inversione degli oggetti: andrebbe bene sia "ruscello – usa con –
borraccia" che il viceversa "borraccia – usa con – ruscello".
Nel corpo della condizione, che viene eseguito quando la condizione è verificata, viene valorizzata la variabile
_vBorracciaPiena_ e visualizzato un messaggio in base allo stato della borraccia.
L'altra cosa importante è il comando return true che chiude il blocco dell'if. Tutte le volte che infatti un'azione
viene gestita, dobbiamo ricordarci di scrivere l’istruzione return true o l'azione verrà erroneamente valutata
e risolta anche dal motore del gioco (che risponderebbe coi messaggi di default).
Si noti che la condizione di esempio appena vista non richiede che il giocatore sia in una particolare stanza in
quanto l’oggetto _ruscello14_ è in questo caso un oggetto fisso, che può essere presente solo nella stanza
appropriata.
Ecco un altro esempio. Catturiamo l’azione "accendino – usa con – torcia" tenendo conto se il giocatore è nelle
stanze _rCantina_ o _rGrotta_ oppure no:
if ((f_isplayerin("_rCantina_")) || (f_isplayerin("_rGrotta_")))
...
else
...
Per la maggior parte dei casi useremo quindi f_action, ma se vogliamo costruire delle condizioni particolari per
catturare una certa gamma di azioni, possiamo usare le seguenti funzioni:
– f_obj1( ) : restituisce identificatore oggetto toccato nel testo
– f_verb( ) : restituisce l'azione
– f_obj2( ) : restituisce identificatore oggetto scelto nell'eventuale sotto-menu dell'azione oppure è uguale a
"" se non esiste sotto-menu
Ad esempio, volendo catturare tutte le azioni sull'oggetto _pistola00_ basta scrivere:
if ( f_obj1() == "_pistola00_" )
...
Poi si può pensare di gestire i verbi differenti all'interno del corpo della condizione:
if ( f_obj1() == "_pistola00_" )
if (f_verb == "spara")
...
return true;
...
return true;
Anche azioni di direzione possono essere gestite in modo manuale se non è stato fatto in modo automatico tramite
le proprietà direction e [Link]. Ad esempio, immaginiamo che _porta08_ abbia nel proprio menu (creato
manualmente) l'azione "entra". Vogliamo che se il giocatore esegue questa azione l'esito dipenda dall'avere con se
o no un certo oggetto:
if (f_action("_porta08_","entra"))
if (f_istaken("_medaglione11_"))
f_moveplayer("_rStanzaFinale_");
return true;
else
// fine gioco
f_lose();
return true;
L'aspetto più interessante è quello della gestione di oggetti "gemelli" che rappresentato il diverso stato di uno
stesso oggetto. Ad esempio, immaginiamo di aver definito due oggetti gemelli in objects.p+o:
{_torciaaccesa00_}
:[Link]: torcia
:room: _rOUT_
:weight: 5
:menu:
++"spegni";
{_torciaspenta05_}
:[Link]: torcia
:room: _rSala_
:weight: 5
:menu:
++"accendi";
if (f_action("_torciaspenta05_","accendi"))
// il giocatore ha l'accendino!
if (f_istaken("_accendino00_"))
f_moveout("_torciaspenta05_");
return true;
if (f_action("_torciaaccesa00_","spegni"))
f_move("_torciaspenta05_", f_whereis("_torciaaccesa00_"));
f_moveout("_torciaaccesa00_");
return true;
Il giocatore non si accorgerà dell'esistenza di due oggetti distinti ma sembrerà che ci sia un oggetto unico che
cambia stato (ovvero descrizioni e proprietà) in base alle azioni che facciamo su di esso. Un modo più veloce per
fare la stessa cosa è usare la funzione f_swap(obj1, obj2) che scambia di posizione i due oggetti indicati. Lo stesso
codice diventa quindi:
if (f_action("_torciaspenta05_","accendi"))
if (f_istaken("_accendino00_"))
f_swap("_torciaaccesa00_", "_torciaspenta05_");
return true;
if (f_action("_torciaaccesa00_","spegni"))
f_swap("_torciaaccesa00_", "_torciaspenta05_");
return true;
}
Anche per actions.p+a vediamo come appare il file in Notepad++:
Come per conditions.p+a, le parole chiave, simboli e operatori sono visualizzati in blu, i testi in nero e le variabili
in viola chiaro.
Eventuali parole sconosciute (e quindi possibili errori) sono segnalati in rosso con sfondo giallo. Ad esempio, se si
dimentica le " prima di un testo:
ci si accorge velocemente dell'errore in quanto buona parte del codice non risulta del colore corretto.
E' arrivato il momento di modificare il file action.p+a per l'avventura "L'Occhio Purpureo". Apriamo il file e
modifichiamolo in questo modo:
if (f_action("_fiume01_","nuota"))
if ( _vNuotatoInFiume_ == 0 )
_vNuotatoInFiume_ = 1;
if (f_istaken("_mattone03_"))
f_lose();
else
f_move("_pistolaCarica00_","_rFiume_");
else
return true;
if (f_action("_edificio05_","esamina"))
var lScritta =
"Sulla parete c'è una scritta scolorita: << ZUCAR " + _vTirareCatena_ + " ";
// zucar 1 volt-A
else
// zucar 2 o 3 volt-E
f_show(lScritta);
return true;
if (f_action("_trono12_","siedi"))
return true;
// accendi lampada
if (f_action("_lampadaSpenta00_","accendi"))
f_swap("_lampadaAccesa00_", "_lampadaSpenta00_");
return true;
// spegni lampada
if (f_action("_lampadaAccesa00_","spegni"))
f_swap("_lampadaAccesa00_", "_lampadaSpenta00_");
return true;
if (f_action("_lampadaAccesa00_","usa con","_mattone03_"))
f_swap("_lampadaAccesa00_", "_lampadaRotta00_");
return true;
if (f_action("_lampadaSpenta00_","usa con","_mattone03_"))
f_swap("_lampadaSpenta00_", "_lampadaRotta00_");
return true;
if (f_action("_sacchettoPieno05_","vuota"))
f_swap("_sacchettoVuoto00_", "_sacchettoPieno05_");
f_move("_biglie00_", f_whereisplayer());
return true;
var lx = 0;
lx = 1;
lx = 2;
lx = 3;
if (
( lx == _vTirareCatena_ )
&&
// e abbiamo l'occhio
f_istaken("_occhio00_")
)
// se indossiamo i guanti
if (f_isworn("_guanti11_"))
f_win();
else
f_lose();
else
f_lose();
return true;
if (
f_action("_pistolaCarica00_","spara")
|| f_action("_pistolaCarica00_","usa con","_ispettore00_")
// ispettore - spara
|| f_action("_ispettore00_","spara")
if (f_ishere("_ispettore00_"))
f_moveout("_ispettore00_");
// compare il suo cadavere
f_move("_cadavere00_",f_whereisplayer());
// ispettore è morto
_vStatoIspettore_ = 2;
else
f_swap("_pistolaScarica00_", "_pistolaCarica00_");
return true;
if (
// mattone - lancia
f_action("_mattone03_","lancia")
|| f_action("_mattone03_","usa con","_ispettore00_")
// ispettore - colpisci
|| f_action("_ispettore00_","colpisci")
if (f_ishere("_ispettore00_"))
// schiva il mattone
f_move("_mattone03_",f_whereisplayer());
else
return true;
}
Il file è abbastanza commentato quindi il codice dovrebbe essere chiaro. Vediamo comunque di analizzare le parti
più significative.
Interessante è l’azione "edificio – esamina" che mostrerà un messaggio che rivelerà al giocatore il numero di volte
che è stato stabilito come corretto per vincere il gioco tirando la catena (in _vTirareCatena_). Il messaggio da
mostare con f_show viene composto da testo e dal contenuto della variabile stessa.
Le azioni "lampada spenta – accendi" e "lampada accesa – spegni" mostrano come far comparire un oggetto
gemello e far sparire l’altro tramite f_swap. Allo stesso modo viene anche gestita l’azione di rottura della lampada.
La condizione più complessa è quella dell’azione "catena – tira" che non usa f_action ma valuta f_obj1, f_verb,
f_obj2 per poter distinguere tramite f_obj2 quante volte è stata tirata. In base a questo, al fatto che si abbia
l’Occhio e indossato i guanti, si hanno diversi esiti.
Le penultima condizione è relativa allo sparare all’ispettore (possibile in vari modi) che lo fa uscire dal gioco (al
suo posto compare il suo cadavere). Se se si spara quando non c’è, si spreca il colpo. In entrambi i casi la pistola
carica sparisce e al suo posto compare quella scarica.
L’ultima condizione è relativa al lancio del mattone, che l’ispettore schiverà (se presente) senza conseguenze o che
porterà ad un messaggio che ci invita a pensare meglio a quello che facciamo.
Con questa modifica l’avventura dell’Occhio Purpureo è pronta! Tutti i file di progetto sono stati modificati e non
ci resta che generare l’avventura e provarla.
I prossimi capitoli saranno dedicati alla risoluzione di errori e problemi nella scrittura delle nostre avventure.
Warning ed Error durante la generazione
Man mano che sviluppate la vostra avventura conviene generare spesso il file di debug e correggere eventuali
errori.
Qui di seguito una lista dei possibili messaggi che bloccano la generazione dell'avventura. Quando si presenta un
errore, va risalito il log per individuare il file nel quale si è verificato.
Ad esempio, se introduciamo un carattere @ in messages.p+r :
l'errore è segnalato sotto la riga indicante l'operazione di verifica dei caratteri usati in messages.p+r
Sarà necessaria una ricerca in tutti i files di progetto per individuare dove si trova l'errore.
Ad esempio, immaginando di avere in actions.p+a una questa riga:
Di seguito l'elenco completo di tutti gli errori possibili e del loro significato.
Unknown objects/rooms/variables:_xxx_
Identificatore _xxx_ sconosciuto
Il debug dell'avventura
Anche se i file dell'avventura vengono generati, non è detto che non vi siano refusi, errori "logici" o errori nel
codice scritto.
I refusi sono semplici da correggere: basta modificare i file di progetto che li contengono e rigenerare l'avventura.
Conviene aprire tutti i file (tranne quelli generati) in Notepad++ e cercare il testo del refuso tramite l'opzione
Search → Find in files :
Tramite "Find All in All Opened Documents" troveremo poi in basso tutte le posizioni in tutti i file in cui si trova il
refuso. Basta fare doppio-click sulla linea e andremo direttamente al file e alla posizione dove effettuare la
correzione.
Per gli errori logici, conviene utilizzare le informazioni aggiuntive di debug che compaiono aprendo in Firefox il
file generato avventura_debug.html.
Infatti, sotto il normale testo del gioco, vi è una parte composta dalla posizione del giocatore e 2 elenchi:
La posizione del giocatore è specificata con Player is in room #N [ _xxx_ ]. Il giocatore si trova nella stanza di
identificatore _xxx_, numero N.
Il gruppo (1) contiene le variabili definite da noi e il loro valore attuale. Questo è molto utile per capire se le
variabili stanno "funzionando" come ci aspettiamo in base a quello che il giocatore ha fatto.
Il gruppo (2) elenca gli oggetti mobili dell'avventura. Accanto ad essi possiamo trovare scritto:
– is out: significa che l'oggetto è fuori dal gioco
– is taken: significa che l'oggetto è trasportato dal giocatore
– is worn: significa che l'oggetto è indossato dal giocatore
– is in room #N [ _xxx_ ]: significa che l'oggetto si trova nella stanza _xxx_ che è la numero N
– [found] : significa che l'oggetto è stato esaminato ed è comparso o sono comparsi gli oggetti specificati
nella sua proprietà found
– [talked] : significa che il giocatore ha parlato almeno una volta con l'oggetto (definito con la proprietà
talkeable)
Può esserci un terzo gruppo relativo agli oggetti fissi dell'avventura con queste proprietà:
– [found] : significa che l'oggetto è stato esaminato ed è comparso o sono comparsi gli oggetti specificati
nella sua proprietà found
– [talked] : significa che il giocatore ha parlato almeno una volta con l'oggetto (definito con la proprietà
talkeable)
– is fixed but in room #N [ _xxx_ ] → ???? : indica un errore di programmazione, l'oggetto fisso è stato
spostato nella stanza _xxx_
– is fixed but taken/worn → ERROR : indica un errore di programmazione, l'oggetto è trasportato o
indossato
Esiste anche un quarto gruppo di informazioni, relativo alle mappe, che analizzeremo alla fine del tutorial.
Durante i nostri debug col browser, tutte le volte che modificheremo uno o più dei nostri file di progetto sarà
necessario resettare la nostra avventura ovvero:
- salvare tutti i file di progetto con le modifiche
- rigenerare il file xxx_debug.html con Pandor+
- riaprire il file xxx_debug.html nel browser (se non aperto)
- eventualmente terminare l’avventura per riportarla all’inizio (comando termina → conferma)
- fare il refresh della pagina (con F5)
Nel caso di modifiche limitate ai testi o alle immagini, gli evenuali salvataggi effettuati (1,2,3 o 4) saranno ancora
utilizzabili. Questo non sarà invece possibile nel caso in cui:
– modifichiamo codice in counters.p+a , conditions.p+a, actions.p+a (oltre ai testi descrittivi)
– modifichiamo/aggiungiamo/cancelliamo variabili
– modifichiamo/aggiungiamo/cancelliamo oggetti (oltre ai testi descrittivi)
– modifichiamo/aggiungiamo/cancelliamo stanze (oltre ai testi descrittivi)
Veniamo ora al debug in caso di errori nel codice scritto nei file counters.p+a, conditions.p+a e actions.p+a .
Il codice in questi file è fondamentalmente codice pseudo-javascript ,in quanto in esso sono introdotti alcuni
elementi non javascript come ad esempio le nostre variabili definite in variables.p+v .
Tuttavia il file HTML generato è completamente javascript, in quanto tutti i nostri file di progetto vengono
"tradotti" in codice javascript o HTML e assemblati nel file generato.
Se vi sono errori nel codice della nostra avventura vedremo comportamenti errati nel gioco (testi sbagliati, oggetti
fuori posto, azioni che non fanno quello che devono…) oppure la pagina si bloccherà e rimarrà bianca o verremo
avvertiti da messaggi di errore del browser stesso.
Possiamo però utilizzare il debugger di FireFox per individuare dove si trova l'errore o scoprire il motivo di un
comportamento errato dell'avventura. Il file da utilizzare è sempre e solamente quello di debug, in quanto quello di
release ha i testi criptati e sono rimossi i commenti.
Proveremo ora a creare qualche errore nell’avventura dell’Occhio Purpureo per vedere come individuarli e
correggerli.
Apriamo actions.p+a e modifichiamolo in questo modo:
if (f_action("_fiume01_","nuota")) )
if ( _vNuotatoInFiume_ == 0 )
Abbiamo cioè aggiunto una parentesi in più alla condizione. Salviamo actions.p+a, rigeneriamo e resettiamo
l'avventura e vedremo che la pagina del gioco sarà completamente bianca!
Iniziamo dunque il nostro debug. In FireFox, premiamo F12. Attendiamo e nella parte bassa del browser apparirà
un menu orizzontale a TAB, clicchiamo sul TAB "Console":
Ecco che ci viene indicato che c’è un errore di sintassi dovuto ad una parentesi non attesa alla linea 998. Cliccando
a destra su "occhiopurpureo_debug.html" ci sposteremo nel tab Debugger e potremo andare alla linea 998 dove si
trova l'errore.:
Pandor+ ha aggiunto, subito sopra la linea effettiva di codice, una linea di commento che indica la posizione della
linea originale sottostante, in questo caso:
// actions.p+a line:11
Ovvero, l'errore si trova alla linea 11 del file actions.p+a . Andrà quindi corretto in Notepad++.
Una volta individuato e eliminato l'errore, sarà necessario resettare l’avventura e riprovare.
Ci sono però errori che non sono altrettanto evidenti, ad esempio modifichiamo actions.p+a in questo modo:
if (f_action("_fiume01_","muota"))
if ( _vNuotatoInFiume_ == 0 )
Abbiamo cioè scritto "muota" invece di "nuota". Se ora resettiamo l’avventura e proviamo a giocare, e proviamo a
nuotare nel fiume, vedremo che invece di vedere i messaggi attesi il gioco mostrerà un messaggio:
Proviamo ora a ripetere l’azione "fiume – nuota". La pagina si bloccherà e vedremo che verremo posizionati
proprio sulla linea del breakpoint.
A questo punto possiamo usare i pulsanti in alto a destra per verificare passo-passo il nostro codice.
Prima di farlo però, clicchiamo a destra dove è scritto "Add watch expression" e scriviamo:
– "f_obj1( )" e poi invio
– "f_obj2( )" e poi invio
– "f_verb( )" e poi invio
Il debugger ci mostrerà l’identificatore dell’oggetto su cui stiamo agendo (ovvero il risultato della funzione
f_obj1), l'azione (f_verb) e l'eventuale sotto-voce (f_obj2). Non resta che capire il perché non "entriamo" nella
nostra condizione, visto che tutto sembra corretto. Premiamo il pulsante in alto:
Che esegue una linea di codice alla volta. Vedremo che il codice "salta" molto più sotto, saltando tutta la parte
interna della nostra condizione nonostante oggetto e verbo siano quelli voluti (osservando la sezione Watch
Expressions a destra).
Quindi significa che la condizione in cui ci aspettavamo di entrare non è appunto stata eseguita in quanto false. A
questo punto premiamo il tasto:
per far ripartire il gioco e ripetiamo l’azione "fiume – nuota". Il codice si bloccherà nuovamente sul nostro
breakpoint. Un’ispezione visiva della linea porterà a individuare l’errore di scrittura e quindi a risolvere il
problema.
Tipicamente metteremo i nostri breakpoints all’inizio delle nostre azioni o all’inizio delle nostre condizioni (per
trovare il punto nel codice basta cercare con CTRL+F le parole "conditions.p+a" o "counters.p+a").
Possiamo anche scorrere il codice manualmente tramite la barra di scorrimento o cercare delle frasi usate nei
nostri commenti per arrivare più velocemente al punto che ci interessa.
Per terminare il debug, basta cliccare sulla "X" in alto a destra: Per togliere invece un breakpoint basta ricliccare
sopra di esso.
Di solito dovremo correggere un errore alla volta. Via via che li correggiamo, val la pena cancellare il log degli
errori nel TAB "Console", tramite il pulsante "cestino":
per poi rieffettuare l’azione di interesse o fare il refresh della pagina (F5) per rivedere l’errore corrente.
Altri sistemi per debuggare il nostro progetto è quello di utilizzare aggiungere nel nostro codice una chiamata alla
funzione javascript alert che permette di visualizzare un messaggio popup con un contenuto voluto. Può essere
comodo ad esempio per capire se stiamo "passando" in un certo punto del nostro codice (ad esempio in una
condizione).
Ad esempio, modifichiamo l’azione "fiume – nuota" in actions.p+a:
if (f_action("_fiume01_","nuota"))
alert("Dentro la condizione!");
if ( _vNuotatoInFiume_ == 0 )
Se ora resettiamo l’avventura e proviamo a fare "fiume – nuota" un paio di volte, vedremo apparire il messaggio
popup.
Infine qualora un oggetto non si comportasse come ci attendiamo (mancano azioni, non è cliccabile, etc) può
anche essere dovuto ad un errore in objects.p+o nella parte della proprietà menu che può ospitare del codice.
In questo caso il browser potrebbe puntarci ad un errore in una zona di codice di questo tipo (linea 1399):
La riga di commento:
// objects.p+o :menu: for object: _edificio05_
indica che il codice sottostante si trova in origine nella parte relativa alla proprietà menu dell'oggetto _edificio05_
in objects.p+o. Sarà lì che dovremo cercare eventuali errori.
Un'ultima nota su un errore molto subdolo: se nel codice sbagliamo il nome di una variabile, ad esempio al posto
di _vMOVES_ scriviamo vMOVES (senza _ ) , sia Pandor+ che il browser non segnaleranno nessun errore. In
javascript infatti non è obbligatorio definire le variabili in modo esplicito e quindi, un'istruzione del tipo:
vMOVES = _vMOVES_ + 1;
causerà la creazione "al volo" di una nuova variabile vMOVES_ , diversa da _vMOVES_ e quindi l'istruzione non
farà quello che ci aspettiamo. In caso di comportamenti anomali, controllate quindi le che tutte le variabili siano
scritte correttamente e in questo può aiutarvi molto l'utilizzo dei colori come spiegato inizialmente:
Finalizzare il gioco
Una volta che la nostra avventura è pronta e il file HTML di debug funziona correttamente, possiamo passare a
generare la versione di release nella quale i testi del gioco saranno criptati e le informazioni di debug nascoste.
completamento:
In questo modo, il valore di _vCOMPLETION_ sarà sempre visibile dopo i comandi di default.
A questo punto dovremo decidere noi dove incrementare la percentuale di completamento. Tipicamente lo faremo
a seguito di azioni del giocatore o quando scoprirà qualcosa di importante. Quindi, nelle nostre condizioni in
actions.p+a e conditions.p+a basterà aggiungere nel codice:
dove XX è la percentuale da incrementare. E’ chiaro che dovremo fare in modo che la somma totale non sia
superiore a 99% (Pandor+ comunque al caso mostrerà al massimo 99). Inoltre, dovremo stare attenti a
incrementare un’unica volta il punteggio se l’azione può essere ripetuta più volte.
Bisogna fare anche attenzione che non è possibile incrementare _vCOMPLETION_ nel caso di azioni gestite
automaticamente con le proprietà degli oggetti (examinable, takeable, talkeable, etc). Ad esempio, se abbiamo un
oggetto examinable (e relativa proprietà [Link]) non possiamo incrementare _vCOMPLETION_ . Per farlo,
dobbiamo rinunciare alla proprietà examinable, aggiungere nella proprietà menu l’azione "esamina", e gestire la
stessa in actions.p+a:
if (f_action("_oggetto_","esamina"))
If (_vEsaminatoOggetto_ == 0)
_vEsaminatoOggetto_ = 1;
return true;
In questo codice è anche mostrato come incrementare un’unica volta _vCOMPLETION_: basta definire una
variabile inizialmente uguale a 0 e che diviene 1 una volta fatto l’incremento.
Nel caso in cui volessimo mostrare il completamento raggiunto nella schermata di fallimento (fail.p+i), basta
aggiungerlo in questo modo nel testo della stessa:
Ovvero tramite le parentesi { }. Con questo sistema è possibile mostrare qualsiasi variabile, e questo può essere
utile per creare dei punteggi alternativi da mostrare anche nella schermata di vittoria (success.p+i):
Aggiungere mappe
Pandor+ v3.2 permette di aggiungere mappe che si "svelano" via via che il giocatore visita le diverse stanze
dell’avventura. L'opzione mappa sarà utilizzabile solo in browser con HTML5.
E’ possibile creare fino a 3 mappe e selezionare quale deve vedere il giocatore in base allo stato del gioco. Le
mappe devono essere create alla fine di tutto lo sviluppo dell’avventura.
La prima cosa da fare è disegnare le mappe. Poiché devono avere dimensioni 560x600 ed essere in formato GIF,
Pandor+ ci viene in aiuto fornendoci, in ogni cartella di progetto, un’immagine di base da modificare,
map_example.gif . Apriamola col programma Paint di Windows:
A questo punto possiamo salvare come [Link] e iniziare a modificarla. Teoricamente è possibile usare colori e
trasparenza ma conviene usare solo toni di grigio o bianco/nero (così da rendere migliore la visualizzazione sui
reader) e l’idea di base è quella di disegnare le stanze cercando di collocarle utilizzando una griglia di 20 x 20
pixels.
In Paint si può ottenere questo attivando la griglia nel menu Visualizza e zoomando al 50%:
Ecco ad esempio la mappa dell’avventura "L’occhio purpureo":
L’idea è quella che le varie stanze non devono "condividere" nessuno dei quadratini 20x20 della griglia.
Una volta salvata, l’immagine va trasformata in codifica base 64 (come già visto in precedenza) e il risultato
inserito in mapimage1.p+ :
La procedura va ripetuta in caso per la mappe 2 e 3 se presenti, andando a modificare mapimage2.p+ e
mapimage3.p+
A questo punto apriamo Pandor+, selezioniamo il nostro progetto e carichiamo la mappa 1, tramite il menu Tools:
In caso di errore, possiamo deselezionare cliccando col tasto destro del mouse o scegliendo dal menu none e poi
cliccando dove vogliamo togliere i numeri.
Oltre a coprire la stanza, dobbiamo anche indicare dove si troverà il "mirino" indicante la posizione del giocatore
se ci troviamo in quella stanza. Per farlo, basta cliccare ancora una volta su uno degli "1":
L'operazione va ripetuta per tutte le stanze (e ogni gruppo di numeri deve avere il numero per il "mirino"). Ecco il
risultato finale:
Le eventuali parti non coperte da numeri, come il bordo e la scritta "Mappa Città Segreta", saranno sempre
visualizzate. Invece, come già detto, ogni parte coperta da numeri si "scoprirà" solo quando il giocatore sarà
passato per la stanza definita con quel numero.
L'ultima cosa da fare è scrivere l'elenco di connessioni tra le stanze. Ovvero, se il giocatore può passare da una
stanza ad un'altra, bisogna scrivere la coppia numerica delle due stanze (separata da virgola) nella sezione
Connections.
Il risultato è che Pandor+ creerà automaticamente delle linee tra i numeri "mirino" delle varie stanze connesse.
Ecco l'elenco di coppie di stanze completo per la mappa dell'avventura "L'Occhio purpureo":
A questo punto possiamo chiudere la finestra e salvare le modifiche effettuate.
Una volta generato il gioco, il risultato sarà la presenza del comando "mappa":
{#01}
_rFiume_
// in conditions.p+a
if ( f_isplayerin("_rIngressoCastello_")
|| f_isplayerin("_rSalaCastello_")
|| f_isplayerin("_rCucinaCastello_")
// seleziona la mappa 2
f_selectmap(2);
else
f_selectmap(1);
Vi sono alcune informazioni relative alle mappe che possiamo vedere durante il debug col browser. Questo sono
presenti nella parte più bassa della pagina di gioco:
Anzitutto è evidenziata la mappa selezionata (1,2 o 3). Quindi, per ogni stanza, è indicato se la stessa è "scoperta",
ovvero se le zone della mappa coperte dal numero della stanza sono visibili (is discovered) o no (is NOT
discovered).
Una situazione ulteriore che può verificarsi è quella di voler far puntare il mirino nella mappa su una zona diversa
rispetto a quella in cui si trova il giocatore. Per farlo basta usare la funzione f_forcemaptag(X) dove X è l'indice
della stanza dove vogliamo forzare il mirino. Ad esempio, se vogliamo che nella mappa per la stanza _rTavolo_ il
mirino sia puntato nella stanza _rCucina_ (di indice 10), basterà scrivere in conditions.p+a :
// in conditions.p+a
if ( f_isplayerin("_rTavolo_") )
f_forcemaptag(10);
Questo può essere molto utile per le stanze dialogo, che di solito sono "appendici" di stanze già definite nella
mappa.
Ecco infine un elenco di possibili errori in Pandor+ relativi alla gestione delle mappe:
File connections.p+ not found! / File [Link] not found! / File [Link] not found!
Il file non è stato trovato
Invalid connections.p+ file! / Invalid [Link] file! / Invalid [Link] image file!
Il file non è valido
Invalid connections!
Si stanno specificando delle connessioni non valide (numeri di stanza non validi)
Invalid [Link] image size (not 560x600)
L'immagine [Link] non è 560x600 pixels
Missing connection point for area: X
Bisogna specificare il numero "mirino" per il gruppo di numeri X
Unable to save [Link] file!
Non è possibile salvare il file con le informazioni della mappa X
Stanze dialogo
Pandor+ v3.2 aggiunge la possibilità di avere stanze nelle quali non vengono mostrati oggetti mobili e inventario.
Questo può servire per creare delle schermate di descrizione o di scelta multipla fissa.
Stanze di questo tipo sono definite con ":" , ad esempio:
Come si vede la stanza 02 è definita con {:02} e questo indica a Pandor+ che in questa stanza non vanno
visualizzati oggetti mobili o l'inventario:
Esiste però l'oggetto "continua", che se cliccato, ci offre l'azione "conferma" che ci porta nella stanza
_rSceltaMultipla03_
La stanza 03 è anche definita allo stesso modo, ma offre due oggetti cliccabili "scegli".
Possiamo definirne il menu in objects.p+o :
Si noti infine che sotto DEBUG INFORMATION, Pandor+ ci avverte che questa è una stanza nella quale non
compariranno oggetti mobili e inventario.
Creare una libreria di funzioni
Pandor+ v3.2 aggiunge anche la possibilità di creare funzioni da utilizzare più volte in actions.p+a o
conditions.p+a
Per farlo, create un file testuale di nome functions.p+a nella cartella del vostro progetto. In questo file potete
creare le vostre funzioni. Ad esempio, immaginiamo di creare due funzioni, la prima che sposta tutti gli oggetti
dell'inventario nella stanza che viene passata come parametro e la seconda, senza parametri, che ritorna un
messaggio di qualche tipo.
// in functions.p+a
function myMoveTakenToRoom(pRoom)
foreachobject{
if (f_istaken(id))
f_move(id, pRoom);
function myShow()
var s = "
";
return s;
// in conditions.p+a
if ( ... )
...
f_show( myShow() );
}
if ( ... )
...
// tutti gli oggetti trasportati dal giocatore finiscono nella stanza _rPrigione_
myMoveTakenToRoom("_rPrigione_");
Fate sempre attenzione a non usare nei nomi delle funzioni, dei parametri e delle variabili locali il carattere
underscore ( _ ) .
Note alle versione 3.5
Pandor+ v3.5 risolve alcuni bug delle versioni precedenti e aggiunge alcune nuove funzionalità, ma alcuni bug
minori sono ancora presenti. Qui di seguito alcune note e suggerimenti a riguardo.
1. E' stato aggiunto il peso attuale trasportato nel file html di debug:
z=1
else
z=2;
con:
if (x==y)
z=1;
else
{
z=2;
diventa
var appoggio1 = "xx..."; ← meno di 4092 caratteri
f_show(appoggio1);
6. Attenzione a non utilizzare linee vuote in mezzo alle proprietà degli oggetti. Sono possibili invece commenti
CORRETTO:
{_mezzopanino00_}
:[Link]: cheeseburger
:[Link]:
E' un Royale con formaggio... il gusto è proprio strano, non capisci che salsa ci abbiano messo.
// aggiungere cane
Sa di cane bagnato.
:room: _rOUT_
:weight: 5
NON CORRETTO:
{_mezzopanino00_}
:[Link]:
E' un Royale con formaggio... il gusto è proprio strano, non capisci che salsa ci abbiano messo.
// aggiungere cane
Sa di cane bagnato.
:room: _rOUT_
:weight: 5
dell'operatore radio.
{centrale operativa|_centrale04_|vai|_rCentraleOperativa_}.
Ti trovi nel locale radio, l'acqua si sta alzando occupando tutto l'ambiente
velocemente.
{centrale operativa|_centrale04B_|vai|_rCentraleOperativa_}.
Mentre l'identificatore _idrofoni04_ può essere riutilizzato nella descrizione alternativa, lo stesso non si può fare
con gli identificatori relativi alle azioni di direzione. Nell'esempio, si sono distinti gli identificatori della
descrizione alternativa aggiungendo una "B" finale. Sono a tutti gli effetti quindi degli oggetti diversi (mentre
_idrofoni04_ è lo stesso oggetto in entrambe le descrizioni).
_vFlag_ = 1;
if (_vFlag_ == 1)
Appena arriviamo alla undicesima mossa, il primo if è verificato e la variabile diventa uguale a 1. Poi viene
eseguito il blocco if successivo, e quindi verrà aggiunta la scritta "E' successo qualcosa!".
Se l'ordine degli if fosse stato opposto:
if (_vFlag_ == 1)
if (_vMOVES_>10)
_vFlag_ = 1;
allora alla mossa numero 11, la frase NON apparirebbe, perché _vFlag_ viene settata dopo che è già stato eseguito
il codice che valuta se questa variabile è uguale a 1.
Allo stesso modo, è importante ricordare che in caso di blocchi di codice che eseguono più f_append, l'ordine delle
frasi appese sarà proprio quello in cui si trovano nel codice.
_vPrimaVoltaNellaStanza_ = 1;