RPG Zone, Italian Forum & Community for role playing video games - ex Gothic Zone

[Tutorial] Daedalus e struttura di Gothic.dat Parte 1, Lezione 4a

« Older   Newer »
  Share  
Frank-95
view post Posted on 17/2/2015, 14:49 by: Frank-95     +2   +1   -1

Gothic Modder

Group:
Moderatore
Posts:
1,391
Rpg Point:
+83
Location:
L'Urbe!

Status:


Nella scorsa lezione abbiamo visto come prepare la nostra "postazione di modding" per quanto riguarda lo scripting.
Ora è il momento di capire le caratteristiche del file gothic.dat e come è strutturato gothic.

NOTA BENE: Ovviamente non potrò scrivere il comportamento di OGNI file perché sono diverse migliaia, mi limiterò ad elencare le caratteristiche più importanti. Starà a poi a voi andare a spulciare ogni angolo e capire come si comporta. Io cercherò di darvi gli strumenti per farlo. Comunque come al solito se avete domande chiedete pure.

Cominciamo. Daedalus è un linguaggio di scripting orientato agli oggetti. Esso non è alla base del *motore di gioco* che si basa invece su codici che noi, senza codice sorgente, non possiamo modificare. Daedalus è invece alla base del *gioco* (scusate le ripetizioni :P), cioè ci permette di modificare il comportamento di oggetti, personaggi, mostri, ma non la struttura di questi. Non possiamo per esempio fare in modo che una spada ci parli perché non è previsto dal motore, tuttavia è previsto aggiungere effetti grafici, aumentare il danno ecc.

Ogni "cosa" che vedete quando giocate (a parte il mondo stesso), non è altro che un instanza di un particolare oggetto. Potete immaginare gli oggetti come dei progetti virtuali aventi determinati attributi e le instanze come la realizzazione pratica di questi oggetti aventi gli stessi attributi del progetto ma valori diversi.

Vi faccio un esempio. Ora che avete gothic.dat decompilato (se così non fosse tornate alla lezione precedente) andate nella cartella _intern e aprite il file classes.d. Osservate questa parte:

CODICE
class C_Npc
{
       var int id;
       var string name[5];
       var string slot;
       var string effect;
       var int npcType;
       var int flags;
       var int attribute[ATR_INDEX_MAX];
       var int HitChance[MAX_HITCHANCE];
       var int protection[PROT_INDEX_MAX];
       var int damage[DAM_INDEX_MAX];
       var int damagetype;
       var int guild;
       var int level;
       var func mission[5];
       var int fight_tactic;
       var int weapon;
       var int voice;
       var int voicePitch;
       var int bodymass;
       var func daily_routine;
       var func start_aistate;
       var string spawnPoint;
       var int spawnDelay;
       var int senses;
       var int senses_range;
       var int aivar[100];
       var string wp;
       var int exp;
       var int exp_next;
       var int lp;
       var int bodyStateInterruptableOverride;
       var int noFocus;
};


La prima riga indica che stiamo creado una classe di nome C_NPC. Una classe è proprio il progetto di cui stavamo parlando prima. Questa classe oggetto è la base per tutti gli NPC e, sorpresa, anche per i mostri! Infatti anche essi sono considerati NPC, hanno semplicemente un "corpo diverso".
Se avete esigenze particolari nulla vi vieta di creare altri oggetti classe tutti vostri, basta che non modificate questi altrimenti rischiate che vi crasha gothic :P
Vi spiego ora a cosa si riferiscono queste classi:

C_NPC: npc umani e mostri

C_MISSION: classe contenente lo stato di una missione

C_ITEM: tutti i tipi di oggetti

C_FOCUS: classe contenente le proprietà di focalizzazione (su mostri, umani, ecc..)

C_INFO: dialoghi

C_ITEMREACT: classe non utlizzata

C_SPELL: incantesimi

Come capirete, alla fine tutto ciò che vedrete in gothic.dat deriva in un modo o nell'altro da queste classi. Due cose importanti: uno, in realtà anche queste classi derivano da altre più generiche definite nel motore di gioco; due, queste non sono tutte le classi esistenti in gothic: negli altri file .dat ve ne sono altre che assolvono ad altri scopi.

Adesso cercherò con un corso piuttosto accellerato di parlarvi di sintassi e semantica, un argomento piuttosto lunghetto.
Durante la modifica degli script prima o poi si fa qualche errore, è inevitabile. Ora sta a noi capire di che tipo di errore si tratta.

CITAZIONE
In informatica, la sintassi di un linguaggio di programmazione o di un altro linguaggio formale (di markup, di query e così via) è l'insieme delle regole che una porzione di codice deve seguire per essere considerata conforme a quel linguaggio.

Detto in altre parole, per scrivere in daedalus, dovete seguire delle determinate regole per far capire al compilatore cosa state facendo. Un errore di sintassi provoca l'annullamento della compilazione.

CITAZIONE
Nel campo dell'informatica teorica, il termine semantica formale riguarda i modelli matematici che definiscono formalmente i linguaggi di programmazione o, più generalmente, la computazione stessa.

Cosa significa? A differenza della sintassi che riguarda propriamente come DEVE essere scritto il codice, la semantica studia il suo significato. Cioè potete scrivere bene il codice, ma potrebbe non fare quello che dovrebbe.

Diamo uno sguardo alla sintassi, approfittando per spiegare qualche altra nozioncina, e in seguito vediamo qualche esempio pratico.

Commenti: i commenti sono parti di codice ignorate dal compilatore. Vi servono per spiegare a cosa serve ciò che state scrivendo (se lavorate in gruppo per esempio), o prendere delle note. Ci sono due tipi di sintassi per i commenti
CODICE
//Commenti su di una sola riga

/* Commenti
su più
righe */



Classi: progetti software. Permettono la creazioni di instanze (oggetti), basati su di essi.
CODICE
class NomeClasse
{
   //qui vanno tutti gli attributi (proprietà della classe)
}


Prototipi: possiamo considerarli delle sottoclassi, quindi progetti che rappresentano una gamma più ristretta di oggetti. Essi rappresentano un insieme di istanze, ma più ristretto dell'intera classe (una classe è il progetto di una tv generica, un prototipo possibile è il progetto di una tv led, e l'instanza è la tv led uscita dalla fabbrica, per esempio :D). Il prototipo più utilizzato è Npc_Default. Esso è la base per tutti gli NPC, ma non per i mostri, anche se appartengono entrambi alla classe C_NPC.
CODICE
prototype NomePrototipo(NomeClasse) //tra parentesi il nome della classe da cui eredita gli attributi
{
   //gli attributi devono essere gli stessi della classe madre, ovviamente scegliendo voi il valore
}


Instanze: oggetti software. Possono derivare da classi o da prototipi
CODICE
instance NomeInstanza(NomeClasse O NomePrototipo) //classe O prototipo madre
{
   //attributi
}


Costanti e variabili: sono insiemi di dati aventi un determinato valore, assegnati ad un simbolo (un nome). Le variabili come dice la parola stessa possono mutare, le costanti no. Una variabile è la x che devi trovare in un equazione per esempio, il pi greco è una costante invece. Le variabili e le costanti inoltre sono di un certo tipo, che una volta definito non può essere cambiato. Alla fine della definizione, assegnazione e manipolazione di costanti e variabili ci va un punto e virgola! Non metterlo causerà un errore di sintassi e la fine della compilazione. Le variabili sono gli attributi delle classi di cui abbiamo parlato prima.
CODICE
//possibili tipi in daedalus: string (stringhe letterali), int (numeri interi), float (numeri decimali)

tipocostante nomecostante = valorecostante;    //definizione di una costante al di fuori di funzioni e classi

tipovariabile nomevariabile;    //definizione di variabili dentro o fuori di classi e funzioni
nomevariabile = valorevariabile;    //assegnazione di variabili dentro classi e funzioni



Funzioni: in gothic non ci possono essere pezzi di codice al di fuori di classi o funzioni ad eccezione della dichiarazione di variabili e costanti. Le funzioni servono quindi a definire una porzione di codice che può essere chiamata, una ma anche più volte. Un dialogo per esempio, oppure il cambiamento di un attributo: invece di scrivere ogni volta il codice, si raggruppa tutto in una funzione che può essere chiamata più volte. Una funzione inoltre può avere un determinato "risultato", chiamato valore di ritorno. Questo può essere di tipo string, int o float. In caso non ci sia la funzione verrà definita come void (vuota). Quando si chiama una funzione va messo il ; alla fine
CODICE
func tipodiritorno nomefunzione(tipoparametro1 nomeparametro1, ..., tipoparametroN nomeparametroN)
{
   //operazioni
};


Istruzioni if-else: queste estruzioni sono alla base dei linguaggi di programmazione, sono le prime che si studiano in genere perché sono le più semplici da capire ma anche molto molto importanti. Quando nella lezione precedente vi ho parlato di logica vero/falso è qui che questa entra in gioco. Cosa succede infatti quando creiamo questi rami if-else? Creiamo delle conidizioni. Cioè quello che succederà dipenderà dalle condizioni di partenza. if = se, else if = altrimenti se, else = altrimenti. La sintassi è questa:
CODICE
if(condizione)
{
   //istruzioni
}
else if(altra_condizione)
{
   //istruzioni
}
else
{
   //istruzioni
}

NOTA: non è obbligatorio l'inserimento dei rami else if ed else una volta chiamato il primo if.

Vediamo di fare degli esempi pratici per farvelo entrare in testa. Faremo finta che sia tutto in un unico file per ora:

CODICE
/* Definizione della classe NPC. Tutte le proprietà sono qui semplicemente definite e non assegnate
perché potrebbere assumere qualsiasi valore. Vediamo proprietà (variabili) di tipo int e string.
Si possono notare anche degli array, cioè variabili aventi non un valore del tipo scelto, ma una lista di valori.*/

class C_Npc
{
       var int id;                        //variabile di tipo int
       /*array di tipo string di lunghezza 5: la variabile name avrà quindi 5 valori diversi a seconda della posizione in lista (name[0], name[1], name[2], name[3], name[4])*/
       var string name[5];
       var string slot;
       var string effect;                //variabili di tipo string
       var int npcType;
       var int flags;
       var int attribute[ATR_INDEX_MAX];    //array di tipo int di lunghezza uguale a ATR_INDEX_MAX che è il nome di una costante definita in constants.d
       var int HitChance[MAX_HITCHANCE];
       var int protection[PROT_INDEX_MAX];
       var int damage[DAM_INDEX_MAX];
       var int damagetype;
       var int guild;
       var int level;
       var func mission[5];
       var int fight_tactic;
       var int weapon;
       var int voice;
       var int voicePitch;
       var int bodymass;
       var func daily_routine;
       var func start_aistate;
       var string spawnPoint;
       var int spawnDelay;
       var int senses;
       var int senses_range;
       var int aivar[100];
       var string wp;
       var int exp;
       var int exp_next;
       var int lp;
       var int bodyStateInterruptableOverride;
       var int noFocus;
};


/* Definizione del prototipo Npc_Default che raggruppa tutti gli NPC umani e che eredita dalla classe C_NPC. Vengono assegnati dei valori standard a molte delle proprietà di C_NPC ma non è detto che le manterrà durante la creazione dell'instanza */

prototype Npc_Default(C_Npc)
{
       /* Prima avevamo visto un array intero di nome attribute e di lunghezza pari a ATR_INDEX_MAX. Il valore di quella costante è 5 perché 5 sono gli attributi di un npc, e qui di seguito vengono assegnati i valori di base.*/
       attribute[ATR_STRENGTH] = 10;
       aivar[REAL_STRENGTH] = 10;
       attribute[ATR_DEXTERITY] = 10;
       aivar[REAL_DEXTERITY] = 10;
       attribute[ATR_MANA_MAX] = 10;
       aivar[REAL_MANA_MAX] = 10;
       attribute[ATR_MANA] = 10;
       attribute[ATR_HITPOINTS_MAX] = 40;
       attribute[ATR_HITPOINTS] = 40;
       HitChance[NPC_TALENT_1H] = 0;
       HitChance[NPC_TALENT_2H] = 0;
       HitChance[NPC_TALENT_BOW] = 0;
       HitChance[NPC_TALENT_CROSSBOW] = 0;
       protection[PROT_EDGE] = 0;
       protection[PROT_BLUNT] = 0;
       protection[PROT_POINT] = 0;
       protection[PROT_FIRE] = 0;
       protection[PROT_MAGIC] = 0;
       /* Qui di seguito valori standard di alcune proprietà di C_NPC per gli umani */
       damagetype = DAM_BLUNT;
       senses = SENSE_HEAR | SENSE_SEE;
       senses_range = PERC_DIST_ACTIVE_MAX;
       aivar[AIV_MM_FollowTime] = NPC_TIME_FOLLOW;
       aivar[AIV_FightDistCancel] = FIGHT_DIST_CANCEL;
       bodyStateInterruptableOverride = FALSE;
};


/* E finalmente ecco l'istanza, l'"oggetto" pyrokar. Come vedete non eredita dalla classe C_NPC direttamente come fanno i mostri ma dal prototipo Npc_Default. Una descrizione più approfondita delle proprietà di C_NPC nelle prossime lezioni. */

instance KDF_500_Pyrokar(Npc_Default)
{
       name[0] = "Pyrokar";
       guild = GIL_KDF;
       id = 500;
       voice = 11;
       flags = NPC_FLAG_IMMORTAL;
       npcType = npctype_main;
       B_SetAttributesToChapter(self,6);
       fight_tactic = FAI_HUMAN_STRONG;
       B_SetNpcVisual(self,MALE,"Hum_Head_Bald",Face_N_ImportantOld,BodyTex_N,ItAr_KDF_H);
       Mdl_SetModelFatness(self,1);
       Mdl_ApplyOverlayMds(self,"Humans_Mage.mds");
       B_GiveNpcTalents(self);
       B_SetFightSkills(self,30);
       daily_routine = Rtn_Start_500;
};


/* Esempio di una funzione per un dialogo (non è tutto qui ho preso solo quello che mi serviva) */

func void DIA_Pyrokar_RUNNING_Info()
{
       var int randomizer;                        //Dichiarazione di variabile locale di tipo int
       /* Alla variabile viene assegnato come valore il risultato della funzione Hlp_Random che prende come parametro un intero e da come risultato un intero compreso tra 0 e il parametro meno 1 (2 in questo caso) */
       randomizer = Hlp_Random(3);

       /* Inizia il ciclo di if che si base sul valore di randomizer, quindi sul risultato di Hlp_Random */
       if(randomizer == 0)        //Se il risultato è zero allora...
       {
               AI_Output(self,other,"DIA_Pyrokar_RUNNING_11_00");        //Fino a quando sarai impegnato con la prova, non abbiamo altro da dirti.
       }
       else if(randomizer == 1)                //Altrimenti se il risultato è uno, allora...
       {
               AI_Output(self,other,"DIA_Pyrokar_RUNNING_11_01");        //Perché sei ancora qui? Vai ad affrontare la tua prova!
       }
       else                                //Altrimenti...
       {
               AI_Output(self,other,"DIA_Pyrokar_RUNNING_11_02");        //È il momento di trasformare le tue parole in azioni concrete. Non credi, novizio?
       };
       /* (qui il codice original era un altra else if, ma siccome l'ultimo caso possibile era 2, va bene anche inserire semplicemente else) */
       AI_StopProcessInfos(self);
};


Bene, purtroppo per chi non ha mai programmato può risultare un po' "fuori dall'ordinario", ma fidatevi che una volta capito andrete a manetta. Studiatevi bene la logica vero-falso; non solo per il modding, ma anche per voi perché è davvero importante credetemi.

Nella prossima lezione concluderemo con gli operatori e la truttura di gothic.dat.

Ciao :camberman.gif: :camberman.gif:

Torna all'indice delle lezioni



Edited by Frank-95 - 15/10/2015, 13:34
 
Top
3 replies since 17/2/2015, 14:49   220 views
  Share