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

[Tutorial] Npc e dialoghi, Lezione 5

« Older   Newer »
  Share  
Frank-95
view post Posted on 18/2/2015, 15:52 by: Frank-95     +1   +1   -1

Gothic Modder

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

Status:


Nella precedente lezione abbiamo introdotto le caratteristiche del linguaggio di scripting daedalus.

Ora passiamo al nostro primo atto di scripting pratico: la creazione di un npc e di un dialogo.

NOTA: per concludere con la creazione di un npc sarà necesessario utilizzare lo spacer. Alla fine di questa lezione coprirò MOLTO brevemente cosa vi serve sapere per ora. Per una trattazione più approfondita aspettare le lezioni successive.

Allora vediamo subito di iniziare con la creazione di un nuovo npc. Una volta che avete capito come si fa non solo potrete crearne di nuovi ma anche modificare quelli già esistenti. Bene, selezionate gothic come progetto attivo da gothic sourcer (se non l'avete già fatto, tasto destro -> set as active project), poi cliccate nuovament col destro sulla cartella npc -> new script file. PER ORA come nome metteteci quello che volete basta che lo fate finire con un .d. Vi comparirà poi una finestra che vi mostra tutti i file di gothic.dat dalla quale dovrete posizionare il vostro file. Dovete sapere infatti che i file non vengono compilati tutti contemporaneamente ma uno alla volta. Di conseguenza per poter utilizzare una determinata classe/funzione/variabile in un file questa deve essere già stata dichiarata nello stesso file o in un file PRECEDENTEMENTE compilato. Se ciò non avvenisse la compilazione terminerebbe con un errore. Voi quindi scorrete in basso e appena trovate le instanze degli npc (formate da trelettere_numeri_nomepersonaggio.d; non confondetevi con i dialoghi che iniziano con DIA!!), selezionatene una a caso e poi date ok. Questo dirà a gothic di compilare il vostro file dopo il file selezionato. Se spuntate invece il riquadro "insert before cursor", questo verrà compilato prima. Selezionatene uno a caso o in ordine alfabetico, come volete, e date ok. Ecco a voi il vostro primo file aggiunto a gothic.dat :D

Adesso vi ritroverete con un file vuoto. Ora, serve "riempirlo". Tenete aperto un qualsiasi altro file di un NPC come esempio e seguitemi:

CODICE
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;
};


func void Rtn_Start_500()
{
       TA_Sit_Throne(8,0,23,0,"NW_MONASTERY_THRONE_01");
       TA_Sit_Throne(23,0,8,0,"NW_MONASTERY_THRONE_01");
};

func void Rtn_RitualInnosEyeRepair_500()
{
       TA_Stand_Guarding(8,0,23,0,"NW_TROLLAREA_RITUAL_02");
       TA_Stand_Guarding(23,0,8,0,"NW_TROLLAREA_RITUAL_02");
};

func void Rtn_RitualInnosEye_500()
{
       TA_Circle(8,0,23,0,"NW_TROLLAREA_RITUAL_02");
       TA_Circle(23,0,8,0,"NW_TROLLAREA_RITUAL_02");
};


Questa è la classe del buon vecchio pyrokar. Vediamo cosa significa riga per riga ciò che c'è scritto:

La prima riga contiene la definizione dell'istanza pyrokar, quindi il nome dell'istanza, e la classe madre (in questo caso il prototipo Npc_Default).
Dalla prima del blocco tra parentesi graffe inizia l'assegnazione di valori alle proprietà della classe.

Se andate a vedere di nuovo la classe originale C_NPC vedrete che name non è semplicemente una stringa ma un array di stringhe, quindi, in altre parole, una lista di stringhe, in questo caso di lunghezza 5. Non è chiaro il perché di ciò (noterete che non tutto quello che hanno fatto i Piranha Bytes ha senso), tuttavia a noi servirà soltanto la prima voce di questo array cioè name[0]. Il valore (tra virgolette per indicare una stringa), sarà il nome che comparirà sopra il personaggio e sopra i dialoghi.

guild invece è un intero. Nella classe constants.d ad ogni gilda è associato un numero. Non tratteremo per ora della creazione di nuove gilde, quindi dovete sceglierne una tra quelle già esistenti (milizia, paladini, mercenari, cacciatori di draghi, contadini, novizi, maghi, pirati, maghi dell'acqua, banditi). Andate a cercare in constants.d quella che vi serve. Se non appartiene a nessuna di queste c'è anche la possibilità di non attribuirne nessuna: GIL_NONE.

id corrisponde all'identificativo del npc. Deve essere UNIVOCO!! Gli unici a non avercelo unico nel gioco originale sono i personaggi di test (storyhelper, charachterhelper, ecc..) e l'eroe che hanno zero. Io però ho assegnato zero solo all'eroe e ai primi 9999, proprio per rimarcare l'univocità ed evitare errori. Ogni gilda ha un id identificativo che aumenta gradualmente. Osservate bene le altre istanze della gilda relativa al vostro personaggio, e scegliete di conseguenza un numero maggiore.

Ora possiamo quindi dare un nome decente alla nostra classe. Le classi degli npc sono infatti date da: GILDA_ID_NOME. E così dovrete rinominare anche il vostro file. Cliccateci col destro nel menu a sinistra -> rename e dategli lo stesso nome terminato da .d.

Bene, vediamo il resto. voice indica la voce che avrà il personaggio, NON durante i dialoghi. Cioè quando impreca, e parla con altri di sottofondo. Cercate un personaggio simile al vostro e dategli lo stesso numero. Non date il numero 15 che è univoco dell'eroe!!

flags nella maggior parte dei casi qui ci sarà zero. Ma capita che così non è. Ci sono tre possibili flag definite sempre in constants.d: npc_flag_immortal, _friend, _ghost. Diciamo che le diciture sono abbastanza chiare, il primo lo rende immortale e viene usato per i personaggi senza i quali non riusciresti a finire il gioco, il secondo viene usato per gli amici (quindi lares, lee, diego, ecc..), il terzo per i fantasmi. Potete anche utilizzarli insieme. Come? Non con un +, ma con il simbolo di unione, per esempio NPC_FLAG_IMMORTAL | NPC_FLAG_FRIEND.

npcType indica il tipo di npc. Fondamentalmente si dividono in tre tipi: main, ambient e friend. Main sono tutti queli con i nomi, quelli che hanno dialoghi propri, ambient sono tutit quelli senza nome che hanno dialoghi "generici", per friend vale lo stesso discorso di flags. Cercate npctype in ai_constants (dentro la cartella ai_intern) e vedete tutti i vari tipi. I numeri dal 3 al 7 indicano semplicemente che: ocmain/ambient sta a campo vecchio, bl_main/ambient, sta DENTRO il campo dei banditi, bl_ambient sta FUORI il campo dei banditi. Siccome penso che voi vogliate inserire personaggi principali alla fine quelli che più userete è npctype_main nel relativo ambiente.

B_SetAttributesToChapter è una funzione che serve ad assegnare valori standard agli attributi dei personaggi. self indica che verranno assegnati all'istanza in cui è chiamata la funzione, e 6 indica che gli verranno assegnati i valori più alti. La funzione la troverete in npc_scripts se volete studiarla/modificarla.

fight_tactic indica che tipo di tattica di combattimento usa. Per gli umani ci sono coward, normal, strong e master.

B_SetNpcVisual crea la visuale del personaggio. I parametri sono self (questo personaggio), il sesso (male, female), la mesh della testa (vedere dentro anims.vdf per i nomi o cercare dentro altre instanze per quello che vi serve) come stringa, la texture della testa (dentro ai_constants vi sono tutte le possibilità, che potete anche vedere da goman aprendo il file textures.vdf), la texture del corpo (lasciate quella standard visto che porterà comunque un vestito o un'armatura), l'ultima è l'instanza dell'armatura che potete vedere in it_Armor o in it_addon_armor).

Mdl_SetModelFatness indica la "stazza" del personaggio. Potete dargli un valore tra -6 e 6 a seconda di quanto lo volete scheletrico od obeso :D

Mdl_ApplyOverlayMds applica un overlay, cioè delle animazioni particolari che si sovrapporanno a quelle di base. Cercate dentro la cartella _work/data/anims/mds_overlay per tutte le possibilità.

B_GiveNpcTalents è una funzione standard che da semplicemente tutti i talenti tranne le acrobazie.

B_SetFightSkills imposta le abilità di combattimento al valore dato come parametro in percentuale. La funzione la trovate sempre in npc_script.

Arriviamo all'ultima, daily_routine. Indica appunto la routine giornaliera. Quella che chiamerete qui sarà quella di partenza, e la potete vedere sotto. Poi queste potranno essere cambiate tramite script. Il nome delle routine DEVE essere Rtn_Nomeroutine_IDpersonaggio. Nelle istruzioni a seguire dovrete impostare che tipo di routine sarà, chiamando la funzione corretta. Le routine le potete trovare in AI -> Human -> TA. I quattro parametri sono: ora inizio, minuto inizio, ora fine, minuto fine. Per indicare che farà tutto il giorno la stessa cosa dovrete inserirla due volte. La prima con l'orario che volete voi, la seconda con lo stesso orario con inizio e fine scambiati. Per esempio dalle 8 alle 22, e dalle 22 alle 8. Così create un ciclo infinito. Il quinto parametro indica il waypoint in cui eseguirete l'azione. A fine lezione una piccola appendice per trovare quello che vi serve.


Bene ora avete il vostro personaggio. Ma non lo troverete nel mondo, perché dovete ancora inserirlo. Aprite npc_globals e create un altra variabile di tipo c_npc col nome del vostro personaggio. Nella funzione B_InitNpcGlobals sotto, create infine la variabile del personaggio così: nomevariabile = Hlp_GetNpc(nomeinstanza). Prendete come modello gli altri!
Infine aprite il file startup. Lì troverete diverse funzioni che rappresentano diverse zone dei mondi. Non posso dirvi dove dovrete metterlo, dovrete capirlo dal nome. newworld significa khorinis, oldworld valle delle miniere, addonworld jharkendar e dragonisland irdorath. Poi ci sono delle sottozone in inglese ma potete benissimo capire più o meno a che zone corrispondono se ci avete mai giocato a gothic :D. Se avete dubbi su questo scrivete pure qui sotto :). Comunque una volta trovata la zona prendete come modello gli altri spawn e chiamate la funzione Wld_InsertNpc(NPC,"WAYPOINT"). Al posto di npc usate la variabile dichiarata in npc_globals e al posto di waypoint, o il waypoint della routine iniziale o uno di quelli standard (li troverete guardando gli altri npc in zona).
Bene ora avete il vostro npc nel gioco :D



Questo però sarà un npc piuttosto piatto poverino. Infatti non vi rivolgerà mai la parola. Creiamo quindi un piccolo dialogo anche per lui.
Create un nuovo file di dialogo in story -> dialoge e come nome DIA_nomeinstanza.d. Come al solito anche questo posizionatelo insieme agli altri dialoghi.

E prendiamo un esempio di dialogo (guarda caso pyrokar :D):

CODICE
instance DIA_Pyrokar_Hagen(C_Info)
{
       npc = KDF_500_Pyrokar;
       nr = 10;
       condition = DIA_Pyrokar_Hagen_Condition;
       information = DIA_Pyrokar_Hagen_Info;
       important = FALSE;
       permanent = FALSE;
       description = "Devo parlare con i paladini. È urgente.";
};


func int DIA_Pyrokar_Hagen_Condition()
{
       if(other.guild == GIL_NOV)
       {
               return TRUE;
       };
};

func void DIA_Pyrokar_Hagen_Info()
{
       AI_Output(other,self,"DIA_Pyrokar_Hagen_15_00");        //Devo parlare con i paladini. È una questione urgente.
       AI_Output(self,other,"DIA_Pyrokar_Hagen_11_01");        //Vuoi dirci come mai vuoi parlare con loro?
       AI_Output(other,self,"DIA_Pyrokar_Hagen_15_02");        //Ho un messaggio urgente da riferirgli.
       AI_Output(self,other,"DIA_Pyrokar_Hagen_11_03");        //E sarebbe?
       AI_Output(other,self,"DIA_Pyrokar_Hagen_15_04");        //Un esercito del male guidato dai draghi si sta radunando nella Valle delle Miniere! Dobbiamo fermarli prima che sia troppo tardi.
       AI_Output(self,other,"DIA_Pyrokar_Hagen_11_05");        //Mmmh. Dovremo valutare le tue parole, novizio. Quando sarà il momento, ti faremo sapere cos'ha deciso il nostro consiglio.
       AI_Output(self,other,"DIA_Pyrokar_Hagen_11_06");        //Nel frattempo faresti meglio a tornare ai tuoi compiti da novizio.
       if(Npc_KnowsInfo(other,DIA_Pyrokar_Auge))
       {
               AI_Output(self,other,"DIA_Pyrokar_ALL_11_07");        //Bene, non vogliamo trattenerti ulteriormente dal fare il tuo lavoro. Ora puoi andare.
               AI_StopProcessInfos(self);
       };
};


La prima è la vera e propria instanza di dialogo. Come vedete eredita infatti da C_INFO. La struttura del nome è DIA_NomePersonaggio_DescrizioneDialogo. Vediamo ora le sue proprietà:

npc indica ovviamente a quale personaggio appartiene il dialogo.

nr è un numero che indica la priorità del dialogo. Numeri più alti posizionano la scelta del dialogo più in alto. Non è assolutamente univoco, quindi ce ne possono essere più d'uno contemporaneamente con lo stesso numero, solo che in quel caso lasciate a gothic l'ordine ;)

condition indica la funzione che determina se il dialogo avverrà o meno

information indica la funzione contenente le stringhe di dialogo

permanent se vero (TRUE) indica che fintanto che ci sono le condizioni il dialogo potrà essere ripetuto

important se vero il dialogo viene iniziato dal npc e non dall'eroe

description indica la stringa che comparirà nelle opzioni di dialogo, da omettere se important è impostato su TRUE


Bene ora passiamo alla condizione di dialogo. È un funzione int cioè ritorna un intero. In daedalus infatti i valori booleani di vero e falso sono rappresentati da interi, 0 è falso, tutti gli altri numeri è vero. In questa funzione sarà presente una condizione if in cui starà a voi impostare la condizione necessaria. Questa è determinata in genere da variabili o stato di missione. Osservate altri dialoghi per capire meglio. Qui non avviene ma in realtà è buona norma fare in modo che la funzione ritorni sempre un valore. Qui per esempio ritorna vero se la gilda dell'eroe è quella dei novizi, ma non ritorna niente se non lo è. In questo caso sarebbe opportuno un ramo else che ritorna falso.

ATTENZIONE: state bene attenti alle condizioni! In quanto tantissimi bug nelle missioni derivano appunto da condizioni male impostate! Inoltre se impostate male le condizioni e nell'istanza di dialogo assegnate sia a important sia a permenent il valore vero, andrete incontro ad un dialogo infinito!!!


Infine arriviamo alla funzione di dialogo. Questa non dovrà ritornare alcun valore, quindi sarà di tipo void. La funzione che dovete chiamare per i dialoghi è AI_Output(PARLANTE,RICEVENTE,"NOMEDIALOGO"). Senza scomodarci ad utilizzare le instanze precise ce ne sono due che fanno al caso nostro: self e other. Sono due instanze variabili, che cambiano a seconda della situazione. Nei dialoghi self corrisponde al npc e other all'eroe. Quindi con AI_Output(self,other,..) parla l'npc viceversa l'eroe. Il nome del dialogo indica il file audio del doppiaggio. Anche se non ce lo avete però è buona norma inserirlo. La struttura è "Nomeistanzadialogo_voceparlante_numerocrescente". Cioè in questo caso il nome del dialogo è DIA_Pyrokar_Hagen, la voce di pyrokar è 11, quella dell'eroe è 15 e quindi a seconda di chi parla si mette l'uno o l'altro. L'ultimo è un numero crescente. Come si fa fa a mettere le stringhe di dialogo quindi? Beh, vedete quei commenti accanto? I commenti (a linea singola!!) accanto ad AI_Output sono gli unici che non vengono ignorati e vengono registrati nel file ou.bin!.
AI_StopProcessInfos(self) indica infine la fine del dialogo.


Bene spero di essere stato chiaro. Qui la questione non è difficile ma si tratta solo di tanta pratica per prendere domestichezza con le condizioni di dialogo e le proprietà degli npc.

Nella prossima lezione parlerò invece di mostri e oggetti.

Ciao :camberman.gif: :camberman.gif:

Torna all'indice delle lezioni



Edited by Frank-95 - 23/2/2015, 19:48
 
Top
27 replies since 18/2/2015, 15:52   708 views
  Share