(*Nome modulo: editor*)
(*Versione: 0.7-beta*)
(*Licenza: GNU GPL*)
(*Rilascio: 06/09/2010*)
(*Autore: Francesco Marrone (alias Giarados)*)
(*Descrizione: modulo contenente l'editor di campo*)
unit editor;
interface
{unica procedura. se utilizzata avvia una nuova sessione dell'editor grafico}
procedure aneditor;
implementation
{per maggiori informazioni a riguardo della classe pulsante si consulti
la documentazione dell'omonino modulo}
uses pulsanti,messages,wincrt,wingraph,mura,dos,sysutils;
const nome='Pac-Editor '; {Titolo delle finestre grafiche}
CONST NULL='0'; {la costante NULL viene utilizzata come input non valido}
{quelli che seguono sono i valori restituibili da getkey}
const left=1;
const right=2;
const up=3;
const down=4;
const svuota=5;
const putmuro=6;
const putcibo=7;
const putscibo=8;
const putunpac=9;
const putungho=10;
const pos_iniziale_pacman=11;
const pos_iniziale_fantasma=12;
{la variabile booleana _end fa da flag nella procedura loadmap
essendo questa una procedura iterativa per stabilire quando vada terminata
si usa una variabile globale}
var _end:boolean;
{Attuali e vecchie coordinate del cursore}
var x_cursore:byte;
var y_cursore:byte;
var x_cursore_old:byte;
var y_cursore_old:byte;
{La mappa su cui si sta lavorando}
var workingon:string;
{Se alla mappa vengono apportate delle modifiche allora
modificato diventa true}
var modificato:boolean;
{getkey restituisce una delle costanti sopra dichiarate se
uno dei tasti viene pressato--altrimenti restituisce il valore 99}
function getkey():byte;
var
key:char;
begin
if keypressed then
begin
key:=readkey;
case key of
chr(72): getkey:=down;
chr(80): getkey:=up;
chr(77): getkey:=right;
chr(75): getkey:=left;
chr(118): getkey:=svuota;
chr(109): getkey:=putmuro;
chr(99): getkey:=putcibo;
chr(115): getkey:=putscibo;
chr(112): getkey:=putunpac;
chr(102): getkey:=putungho;
chr(113): getkey:=pos_iniziale_pacman;
chr(119): getkey:=pos_iniziale_fantasma;
end;
end
else getkey:=99;
end;
{spritetype restituisce una stringa con il nome della cella il cui
valore è sprite--viene utilizzata in loadmap per stampare a video
il tipo di cella selezionato dal cursore}
function spritetype(sprite:smallint):string;
begin
case sprite of
cella_vuota: spritetype:='Vuoto';
cella_muro: spritetype:='Muro';
cella_cibo: spritetype:='Cibo';
cella_super_cibo: spritetype:='Super Cibo';
unwalkable_ghosts: spritetype:='Unw. Ghosts';
unwalkable_pacmen: spritetype:='Unw. Pacmen';
pacman: spritetype:='PacMan';
ghost: spritetype:='Fantasma';
else spritetype:='Fuori Range';
end;
end;
{accendi_cursore accende un cursore rosso alle coordinate ascissa,ordinata}
procedure accendi_cursore(ascissa,ordinata:integer);
begin
setcolor(red);
rectangle((ascissa*dimensione_cella)+1,(ordinata*dimensione_cella)+1,(ascissa*dimensione_cella)+dimensione_cella-3,(ordinata*dimensione_cella)+dimensione_cella-3);
rectangle((ascissa*dimensione_cella)+2,(ordinata*dimensione_cella)+2,(ascissa*dimensione_cella)+dimensione_cella-4,(ordinata*dimensione_cella)+dimensione_cella-4);
setcolor(black);
end;
{input_str è una funzione che simula una linea di comando.
tramite essa si gestisce l'input di stringhe da tastiera.
a seconda del valore di readingmode la funzione assume comportamenti diversi.
ordinata è l'ordinata a da cui viene visualizzata la linea di comando}
function input_str(readingmode:boolean; ordinata:smallint):string;
var
buffer:string;
risposta:string[2];
bitmap:pointer;
f:textfile;
begin
{viene fatto uno screenshot della parte di schermo su cui verrà visualizzata
la riga di comando}
getmem(bitmap,imagesize(0,0,getmaxx,abs(ordinata-getmaxy)));
getimage(0,ordinata,getmaxx,getmaxy,bitmap^);
SetViewPort(0,ordinata,getmaxx,getmaxy,false);
clearviewport;
setcolor(white);
outtextxy(5,ordinata+4,'');
writebuf(' > ');
readbuf(buffer,0);
if readingmode then
begin
{se è in modalità lettura e il file non esiste allora ritorna un errore}
if not thisfileexists(buffer) then
begin
clearviewport;
setcolor(red);
outtextxy(5,ordinata+4,'');
if length(buffer)>12 then settextstyle(defaultfont,horizdir,1);
writebuf(' > Error! "'+buffer+'" does not exist!:S');
settextstyle(defaultfont,horizdir,2);
delay(2000);
buffer:=NULL; {<--costante NULL=Input non valido}
setcolor(getbkcolor);
end
else {altrimenti se il file esiste stampa a video un messaggio che comunica l'avvenuto caricamento}
begin
clearviewport;
setcolor(green);
outtextxy(5,ordinata+4,'');
writebuf(' > File correctly loaded! :) ');
delay(1000);
setcolor(getbkcolor);
end;
end
else begin {se è in modalita scrittura...}
if not thisfileexists(buffer) then {e il file non esiste...allora si preoccupa di crearlo}
begin
assign(f,buffer);
rewrite(f);
write(f,28);
write(f,' ');
write(f,28);
close(f);
clearviewport;
setcolor(green);
outtextxy(5,ordinata+4,'');
writebuf(' > Done...;)');
end
else begin {altrimenti se esiste chiede se si vuole sovrascrivere o no}
repeat
clearviewport;
setcolor(orange);
outtextxy(5,ordinata+4,'');
writebuf(' > '+buffer+' exists, rewrite? Y/N ');
readbuf(risposta,0);
until ((risposta='Y') or (risposta='N') or
(risposta='yes') or (risposta='no') or
(risposta='y') or (risposta='n'));
if ((risposta='yes') or (risposta='Y')) then {se la risposta è si allora sovrascrive il file}
begin
assign(f,buffer);
rewrite(f);
write(f,28);
write(f,' ');
write(f,28);
close(f);
clearviewport;
setcolor(green);
outtextxy(5,ordinata+4,'');
writebuf(' > Done...;)');
end
else buffer:=NULL; {altrimenti ritorna un codice di input non valido}
end;
end;
input_str:=buffer; {<---}
delay(500);
setviewport(0,0,getmaxx,getmaxy,true);
putimage(0,ordinata,bitmap^,normalput); {viene ripristinato ciò che vi era prima sullo sfondo e liberata la memoria}
freemem(bitmap);
end;
{questa procedura mostra una breve guida all'utilizzo della tastiera
per editare un file mappa}
procedure keys;
var
gd,gm:smallint;
backtomaimenu:pulsante; {per info sulla classe pulsante visualizzare l'omonimo modulo}
begin
closegraph; {viene chiusa la finestra grafica da cui è stata chiamata questa procedura}
gd:=NoPalette; gm:=mCustom;
setwindowsize(360,420);
initgraph(gd,gm,nome);
backtomaimenu.crea(1, 1, 3, 4, 1, 2,'Back'); {si crea il tasto back}
{ISTRUZIONI PER L'USO DELLA TASTIERA}
setcolor(yellow);
SetTextStyle(boldfont,HorizDir,5);
outtextxy(100,1,'Pac-Keys');
SetTextStyle(boldfont,HorizDir,3);
outtextxy(100,40,'Movimenti');
SetTextStyle(boldfont,HorizDir,2);
outtextxy(10,80, 'Movimento verso l''alto: Freccia Su');
outtextxy(10,100, 'Movimento verso il basso: Freccia Giù');
outtextxy(10,120, 'Movimento verso destra: Freccia DX');
outtextxy(10,140, 'Movimento verso sinistra: Freccia SX');
SetTextStyle(boldfont,HorizDir,3);
outtextxy(100,160,'Modificatori');
SetTextStyle(boldfont,HorizDir,2);
outtextxy(10,175, 'Svuota cella: V');
outtextxy(10,185, ' #Rende vuota la cella dal suo contenuto.');
outtextxy(10,205, 'Crea muro: M');
outtextxy(10,215, ' #Crea un pezzo di muro.');
outtextxy(10,235, 'Metti cibo: C');
outtextxy(10,245, ' #Del cibo viene posizionato.');
outtextxy(10,265, 'Metti super cibo: S');
outtextxy(10,275, ' #Del super cibo viene messo.');
outtextxy(10,295, 'Metti Unwal. Pac: P');
outtextxy(10,305, ' #PacMan non può attraversare queste celle.');
outtextxy(10,325, 'Metti Unwal. Fan: F');
outtextxy(10,335, ' #I fantasmi non possono attraversare queste');
outtextxy(10,345, ' #celle.');
outtextxy(10,365, 'Fantasma: W');
outtextxy(10,375, ' #Un fantasma verrà creato qui');
outtextxy(10,395, 'PacMan : Q');
outtextxy(10,405, ' #PacMan verrà creato qui');
{ISTRUZIONI PER L'USO DELLA TASTIERA}
while not backtomaimenu.pressato do backtomaimenu.disegna; {sino a quando back non è premuto disegnalo}
SetTextStyle(CourierNewFont,HorizDir,2);
closegraph; {chiudi la finestra}
end;
{questa procedura è l'editor vero e proprio..
essa si occupa di caricare in memoria una mappa
path e renderla modificabile in tutto e per tutto}
procedure loadmap(path:string);
var
tmp_map:campo; {la mappa viene memorizzata momentaneamente in tmp_map}
gd,gm:smallint;
allineamentox_campo,allineamentoy_campo:byte;
comandi:pulsante;
incrementax,incrementay:pulsante;
decrementax,decrementay:pulsante;
_exit:pulsante;
save:pulsante;
reset:pulsante;
clean:pulsante;
x_aggiunta,y_aggiunta:string[1]; {per sistemare la formattazione della stringa infos}
infos:string; {questa stringa contiene tutti i dati riguardanti la cella su cui si trova il cursore}
answer:string[1];
begin
if path<>NULL then {controlla che l'input sia valido}
begin
closegraph;
_end:=false;
gd:=NoPalette; gm:=mCustom;
allineamentox_campo:=0;
allineamentoy_campo:=2;
tmp_map.crea(allineamentox_campo,allineamentoy_campo,path);
{si consulti la documentazione per la classe pulsante nell'omonino modulo}
{creazione dei tasti}
_exit.crea(allineamentox_campo+23, dimensione_y_campo+allineamentoy_campo+2, 3, 4, 1, 2,'Exit');
comandi.crea(allineamentox_campo+18, dimensione_y_campo+allineamentoy_campo+2, 3, 4, 1, 2,'Tasti');
clean.crea(allineamentox_campo+13, dimensione_y_campo+allineamentoy_campo+2, 3, 4, 1, 2,'Clean');
reset.crea(allineamentox_campo+8, dimensione_y_campo+allineamentoy_campo+2, 3, 4, 1, 2,'Reset');
save.crea(allineamentox_campo+3, dimensione_y_campo+allineamentoy_campo+2, 3, 4, 1, 2,'Save');
incrementax.crea(dimensione_x_campo+allineamentox_campo+3, allineamentoy_campo, 2, 2, 1, 2,'+');
decrementax.crea(dimensione_x_campo+allineamentox_campo+1, allineamentoy_campo, 2, 2, 1, 2,'-');
incrementay.crea(allineamentox_campo, dimensione_y_campo+allineamentoy_campo+3, 2, 2, 1, 2,'+');
decrementay.crea(allineamentox_campo, dimensione_y_campo+allineamentoy_campo+1, 2, 2, 1, 2,'-');
setwindowsize((5+dimensione_x_campo+allineamentox_campo)*dimensione_cella+2,(5+dimensione_y_campo+allineamentoy_campo)*dimensione_cella+2);
initgraph(gd,gm,nome);
{INFOS}
{vengono stampati a video i dati iniziali della cella su cui si trova all'inizio il cursore}
setviewport(0,0,getmaxx,29,false);
clearviewport;
settextstyle(boldfont,horizdir,3);
if x_cursore<10 then x_aggiunta:=' '
else x_aggiunta:='';
if y_cursore<10 then y_aggiunta:=' '
else y_aggiunta:='';
{tali dati hanno il formato:
(X: XX ; Y: YY) Tipo: tipo_cella}
infos:='(X: '+x_aggiunta+inttostr(x_cursore)+' ; Y: '+y_aggiunta+inttostr(y_cursore)+') Tipo: '+spritetype(tmp_map.get_stato_cella(x_cursore,y_cursore,true));
setcolor(red);
outtextxy(0,0,infos);
setcolor(getbkcolor);
settextstyle(defaultfont,horizdir,2);
setviewport(0,0,getmaxx,getmaxy,true);
{INFOS}
tmp_map.disegna_mappa(0,0,dimensione_x_campo,dimensione_y_campo,true);
repeat {ciclo principale}
{vengono disegnati i pulsanti}
_exit.disegna;
comandi.disegna;
reset.disegna;
save.disegna;
clean.disegna;
incrementax.disegna;
decrementax.disegna;
incrementay.disegna;
decrementay.disegna;
{viene salvata la vecchia posizione del cursore}
x_cursore_old:=x_cursore;
y_cursore_old:=y_cursore;
{si valuta l'input da tastiera}
case getkey of
svuota: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,cella_vuota);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
putmuro: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,cella_muro);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
putcibo: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,cella_cibo);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
putscibo: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,cella_super_cibo);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
putungho: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,unwalkable_ghosts);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
putunpac: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,unwalkable_pacmen);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
pos_iniziale_pacman: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,pacman);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
pos_iniziale_fantasma: begin
tmp_map.mod_stato_cella(x_cursore,y_cursore,ghost);
tmp_map.disegna_mappa(x_cursore,y_cursore,x_cursore,y_cursore,true);
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
modificato:=true;
end;
left: if x_cursore<>0 then x_cursore:=x_cursore-1;
right: if x_cursore<>dimensione_x_campo then x_cursore:=x_cursore+1;
up: if y_cursore<>dimensione_y_campo then y_cursore:=y_cursore+1;
down: if y_cursore<>0 then y_cursore:=y_cursore-1;
end;
delay(10);
{evita che il cursore esca fuori dalla mappa}
if x_cursore>dimensione_x_campo then x_cursore:=dimensione_x_campo;
if y_cursore>dimensione_y_campo then y_cursore:=dimensione_y_campo;
{al cambiare della posizione del cursore cambiano anche i dati della cella
ad esso associata}
if ((y_cursore<>y_cursore_old) or (x_cursore<>x_cursore_old)) then
begin
{INFOS}
setviewport(0,0,getmaxx,29,false);
clearviewport;
settextstyle(boldfont,horizdir,3);
if x_cursore<10 then x_aggiunta:=' '
else x_aggiunta:='';
if y_cursore<10 then y_aggiunta:=' '
else y_aggiunta:='';
infos:='(X: '+x_aggiunta+inttostr(x_cursore)+' ; Y: '+y_aggiunta+inttostr(y_cursore)+') Tipo: '+spritetype(tmp_map.get_stato_cella(x_cursore,y_cursore,true));
setcolor(red);
outtextxy(0,0,infos);
setcolor(getbkcolor);
settextstyle(defaultfont,horizdir,2);
setviewport(0,0,getmaxx,getmaxy,true);
{INFOS}
tmp_map.disegna_mappa(x_cursore_old,y_cursore_old,x_cursore_old,y_cursore_old,true);
end;
{si accende il cursore}
accendi_cursore(x_cursore+allineamentox_campo,y_cursore+allineamentoy_campo);
{il tasto _exit quando premuto rende true la variabile globale _end e quindi tutte le istanze di questa
procedura vengono interrotte}
if _exit.pressato then _end:=true;
{la pressione del tasto comandi rimanda alla scheda keys}
if comandi.pressato then
begin
keys;
setwindowsize((5+dimensione_x_campo+allineamentox_campo)*dimensione_cella+2,(5+dimensione_y_campo+allineamentoy_campo)*dimensione_cella+2);
initgraph(gd,gm,nome);
tmp_map.disegna_mappa(0,0,dimensione_x_campo,dimensione_y_campo,true);
end;
{se clean è premuto il campo viene svuotato dal suo contenuto e ristampato a video}
if clean.pressato then
begin
tmp_map.svuota_campo;
tmp_map.disegna_mappa(0,0,dimensione_x_campo,dimensione_y_campo,true);
modificato:=true;
end;
{la pressione di reset ripristina la mappa per com'è salvata}
if reset.pressato then
begin
modificato:=false;
loadmap(workingon);
end;
{premendo save la mappa viene esportata..il nome del nuovo file
mappa si ottiene con input_str}
if save.pressato then
begin
if tmp_map.goodmap then
begin
tmp_map.esporta_mappa(input_str(false,(allineamentoy_campo+dimensione_y_campo+1)*dimensione_cella+2),true);
modificato:=false;
end
else display('Set pacman and ghosts position!',true,2500,false);
end;
{questo è uno dei nodi principali dell'editor.
se la mappa deve essere ridimensionata tramite la pressione
di uno dei quattro tasti sottostanti si fa come segue:
1- la mappa attuale viene ridimensionata
2- viene esportata in un file temporaneo tmp_map
3- viene richiamata ricorsivamente loadmap sul file tmp_map}
if incrementax.pressato then
begin
tmp_map.scala_campo(1,0);
tmp_map.esporta_mappa('tmp_map',true);
modificato:=true;
loadmap('tmp_map');
end;
if decrementax.pressato then
begin
tmp_map.scala_campo(-1,0);
tmp_map.esporta_mappa('tmp_map',true);
modificato:=true;
loadmap('tmp_map');
end;
if incrementay.pressato then
begin
tmp_map.scala_campo(0,1);
tmp_map.esporta_mappa('tmp_map',true);
modificato:=true;
loadmap('tmp_map');
end;
if decrementay.pressato then
begin
tmp_map.scala_campo(0,-1);
tmp_map.esporta_mappa('tmp_map',true);
modificato:=true;
loadmap('tmp_map');
end;
{se exit è pressato allora _end diventa true e tutte le istanze di questa procedura si chiudono}
until _end;
{nel caso in cui sia stata apportata qualche modifica allora viene chiesto se si vuole salvare o meno
se la variabile answer assume un valore Y o y allora la mappa viene salvata}
if modificato then
begin
setviewport(0,(allineamentoy_campo+dimensione_y_campo+1)*dimensione_cella+2,getmaxx,getmaxy,false);
repeat
clearviewport;
outtextxy(5,(allineamentoy_campo+dimensione_y_campo+1)*dimensione_cella+6,'');
setcolor(orange);
writebuf(' > Wanna save the changes? Y/N ');
readbuf(answer,0);
until ((answer='Y') or (answer='N') or (answer='s') or (answer='n'));
if (answer='Y') or (answer='y') then
begin
if tmp_map.goodmap then tmp_map.esporta_mappa(workingon,true)
else begin
_end:=false;
setviewport(0,0,getmaxx,getmaxy,true);
display('Set pacman and ghosts position!',true,2500,true);
modificato:=true;
loadmap('tmp_map');
end;
end;
setcolor(getbkcolor);
setviewport(0,0,getmaxx,getmaxy,true);
modificato:=false;
end;
end;
end;
{acquisisce il percorso di un file in modalità scrittura
della funzione input_str. Se tutto fila lisci e strf<>NULL
allora viene richiamata la procedura loadmap che si preoccupa
di aprire il nuovo file mappa vuoto di dimensione 28X28}
procedure newmap;
var
strf:string;
begin
strf:=input_str(false,321);
if strf<>NULL then loadmap(strf);
end;
{menu principale dell'editor
lo commento poco --> è di facile comprensione}
procedure menu;
var
new,load,_exit:pulsante;
gd,gm:smallint;
strf:string;
begin
cleardevice;
new.crea(9,7,3,4,1,2,'New');
load.crea(9,11,3,4,1,2,'Load');
_exit.crea(9,15,3,4,1,2,'Exit');
gd:=NoPalette; gm:=mCustom;
repeat
setcolor(white);
SetTextStyle(MSSansSerifFont,HorizDir,5);
SetTextStyle(BoldFont,HorizDir,5);
setcolor(yellow);
outtextxy(80,20,nome);
setcolor(getbkcolor);
settextstyle(defaultfont,horizdir,2);
setcolor(white);
line(0,320,350,320);
setcolor(getbkcolor);
new.disegna;
delay(10);
if new.pressato then
begin
newmap;
closegraph;
setwindowsize(350,350);
initgraph(gd,gm,nome);
end;
load.disegna;
delay(10);
{alla pressione di load vengono inizializzate tutte le variabili
che interessano la procedura load/editor_vero_e_proprio. Infine
essa viene lanciata.}
if load.pressato then
begin
x_cursore:=0;
y_cursore:=0;
x_cursore_old:=0;
y_cursore_old:=0;
strf:=input_str(true,321);
workingon:=strf;
modificato:=false;
loadmap(strf);
_end:=false;
workingon:=NULL;
if strf<>NULL then {strf anche se null viene passato a loadmap.
non accade nulla se l'input è NULL}
begin
closegraph;
setwindowsize(350,350);
initgraph(gd,gm,nome);
end;
end;
_exit.disegna;
delay(10);
until _exit.pressato;
end;
{la procedura ultima che viene chiamata nel main menu del gioco}
procedure aneditor;
var
gd,gm:smallint;
begin
gd:=NoPalette; gm:=mCustom;
setwindowsize(350,350);
initgraph(gd,gm,nome);
menu;
closegraph;
end;
end.