World of Gothic - Forum
Patrunjelu's Modding Tutorials - Printable Version

+- World of Gothic - Forum (https://worldofgothic.ro/forum)
+-- Forum: The Colony (https://worldofgothic.ro/forum/forumdisplay.php?fid=4)
+--- Forum: Modding (https://worldofgothic.ro/forum/forumdisplay.php?fid=43)
+--- Thread: Patrunjelu's Modding Tutorials (/showthread.php?tid=373)



Patrunjelu's Modding Tutorials - Patrunjelu' - 07-25-2009

Patrunjelu's modding tutorial

Lectia 1 - NPC Scripting

NPC-urile sunt extrem de importante, acestea fiind cunoscute ca 'personaje' din joc. In lectia 1 vom arunca o privire asupra unui script al unui NPC, scriptul care, practic da viata NPC-ului nostru. Sa luam de exemplu un NPC existent... Rufus.
Scriptul arata asa:
Code:
instance BAU_903_Rufus (Npc_Default)
{
    //-------- primary data --------
    
    name         =    "Rufus";
    npctype        =    npctype_main;
    guild         =    GIL_BAU;      
    level         =    4;
    voice         =    2;
    id             =    903;

    //-------- abilities --------
    attribute[ATR_STRENGTH] =        20;
    attribute[ATR_DEXTERITY] =        10;
    attribute[ATR_MANA_MAX] =        0;
    attribute[ATR_MANA] =            0;
    attribute[ATR_HITPOINTS_MAX] =    88;
    attribute[ATR_HITPOINTS] =        88;

    //-------- visuals --------
    //                 animations
    Mdl_SetVisual        (self,"HUMANS.MDS");
    Mdl_ApplyOverlayMds    (self,"Humans_Tired.mds");
    //            body mesh     ,bdytex,skin,head mesh     ,headtex,teethtex,ruestung    
    Mdl_SetVisualBody (self,"hum_body_Naked0",2,2,"Hum_Head_Bald", 2, 1, -1);
    
    B_Scale (self);
    Mdl_SetModelFatness(self,0);
    
    fight_tactic    =    FAI_HUMAN_COWARD;
                                    
    //-------- Talente --------                                       
    Npc_SetTalentSkill    (self,NPC_TALENT_1H,1);        
    
    
    //-------- inventory --------                                    

        
    EquipItem (self, ItMw_1H_Scythe_01);
    CreateInvItems (self, ItFoRice,3);
    
    

    //-------------Daily Routine-------------
    daily_routine = Rtn_start_903;
};



FUNC VOID Rtn_start_903 ()
{
    TA_PickRice        (07,10,20,10,"NC_PATH83_MOVEMENT");
    TA_SitCampfire    (20,10,07,10,"NC_PATH_PEASANT_OUTSIDE5");
};
Acum sa aruncam o privire asupra lui...
Scriptul incepe cu:
Code:
instance BAU_903_Rufus (Npc_Default)
Instance (Instanta) noastra este BAU_903_Rufus... Instanta este absolut, complet personalizabila, astfel ca o putem modifica chiar ca simpla instanta:
Code:
instance Rufus (Npc_Default)
Partea "instance" arata faptul ca textul introdus dupa "instance" este instanta propriu-zisa. Ca sa apelam la NPC-ul nostru, in alte scripturi, folosim numele instantei, nu numele NPC-ului.
Partea
Code:
(Npc_Default)
Arata atribuirea unor anumite "setari" (o sa le denumesc asa pentru ca este mai simplu...) caracteristice NPC-urilor umane, NPC-ului nostru. Trecem peste asta deoarece este prima lectie...
Dupa asta urmeaza:
Code:
{
    //-------- primary data --------
    
    name         =    "Rufus";
    npctype        =    npctype_main;
    guild         =    GIL_BAU;      
    level         =    4;
    voice         =    2;
    id             =    903;
Se observa sus
Code:
//-------- primary data --------
. Acesta este un 'comment'. Cum recunoastem unul? Daca introducem text dupa cele 2 slashuri
Code:
"//"
, automat acea parte de script este ignorata de compiler. Astfel, commenturile pot fi folosite pentru a oferi o "idee" asupra a ceea ce se afla in acea parte de script, sau pentru a marca anumite lucruri, sau chiar pentru mindless messages...
Apoi avem
Code:
name = "Rufus";
Aceasta parte "seteaza" numele NPC-ului nostru. Astfel, ca in joc nu va fi recunoscut ca si "BAU_903_Rufus", ci ca "Rufus". Numele este, la fel, personalizabil. Numele trebuie sa fie scris intre ghilimele!
Code:
npctype = npctype_main;
Aceasta parte arata, dupa cum se observa, tipul NPC-ului nostru. Pentru acest tutorial voi folosi doar npctype_main, dar trebuie mentionat ca sunt si altfel de tipuri, acestea influentand activitatea NPC-ului nostru. De exemplu, pentru npctype_friend, NPC-ul nostru nu va reactiona la un eventual atac al PJ-ului (personaj jucator).
Apoi urmeaza
Code:
guild = GIL_BAU;
Aceasta parte desemneaza ghilda in care este personajul nostru. In functie de ghilda, acesta va reactiona diferit fata de alte personaje. Vom discuta mai larg despre ghilde pe parcurs.
Partea
Code:
    level         =    4;
    voice         =    2;
    id             =    903;
Arata nivelul personajului, vocea (folosita la taunts and stuff) si ID-ul. Toate sunt personalizabile, si nu vor avea impact "imens" asupra gameplay-ului (cu exceptia nivelului).
Sa trecem mai departe:
Code:
    //-------- abilities --------
    attribute[ATR_STRENGTH] =        20;
    attribute[ATR_DEXTERITY] =        10;
    attribute[ATR_MANA_MAX] =        0;
    attribute[ATR_MANA] =            0;
    attribute[ATR_HITPOINTS_MAX] =    88;
    attribute[ATR_HITPOINTS] =        88;
Aici sunt trecute atributele personajului nostru, personalizabile, si variind de la strenght (putere) pana la hitpoints (viata).

Code:
    //-------- visuals --------
    //                 animations
    Mdl_SetVisual        (self,"HUMANS.MDS");
    Mdl_ApplyOverlayMds    (self,"Humans_Tired.mds");
    //            body mesh     ,bdytex,skin,head mesh     ,headtex,teethtex,ruestung    
    Mdl_SetVisualBody (self,"hum_body_Naked0",2,2,"Hum_Head_Bald", 2, 1, -1);
    
    B_Scale (self);
    Mdl_SetModelFatness(self,0);
    
    fight_tactic    =    FAI_HUMAN_COWARD;
                                    
    //-------- Talente --------                                       
    Npc_SetTalentSkill    (self,NPC_TALENT_1H,1);
In aceasta parte sunt desemnate informatii despre partea viziuala a NPC-ului nostru. In cel de-al 3-lea capitol al tutorialului voi explica ce este cu texturile & co. Momentan e bine daca intelegeti ce explic acum.
Pe langa partea vizuala gasim si comanda "fight_tactic = FAI_HUMAN_COWARD;" aceasta arata tactica de lupta a personajului nostru. Aceasta este, la fel customizabila, tacticile fiind:
Code:
FAI_HUMAN_COWARD, FAI_HUMAN_STRONG,FAI_HUMAN_MASTER,FAI_HUMAN_MAGE etc...
Code:
Npc_SetTalentSkill    (self,NPC_TALENT_1H,1);
arata faptul ca personajul nostru are "skill" pentru 1 handed weapons. Npc_SetTalentSkill este o functie, iar in paranteza sunt informatiile necesare functiei... ca un fel de schita pentru un mecanism. "Self" arata faptul ca functia se refera la personajul nostru. NPC_TALENT_1H este "instanta" pentru 1h weapons skill, iar dupa virgula, se afla o valoare, si anume 1. Pentru moment spunem ca e level 1 la 1 handed weapons...
Next:
Code:
    //-------- inventory --------                                    

        
    EquipItem (self, ItMw_1H_Scythe_01);
    CreateInvItems (self, ItFoRice,3);
    
    

    //-------------Daily Routine-------------
    daily_routine = Rtn_start_903;
};



FUNC VOID Rtn_start_903 ()
{
    TA_PickRice        (07,10,20,10,"NC_PATH83_MOVEMENT");
    TA_SitCampfire    (20,10,07,10,"NC_PATH_PEASANT_OUTSIDE5");
};
Ce avem aici? In primul rand, inventarul personajului nostru. EquipItem este o functie, la fel ca si npc_settalentskill. Prin aceasta functie setam un anumit item (de exemplu, o arma) care sa fie echipat de personaj. In cazul nostru, folosim instanta armei, si anume "ItMw_1H_Scythe_01". Vom discuta despre arme/items/armuri in alta parte a tutorialului nostru.
CreateInvitems este la fel, o functie, care "creeaza" obiecte in inventarul personajului nostru. Se observa legatura "self" cu personajul nostru, instanta obiectului(ItFoRice - Orez), si valoarea(3). Astfel Rufus va avea 3 bucati de Rice (orez).
Fiecare personaj are o rutina proprie. Rutinele sunt dificile, astfel ca le lasam pe alta data. O sa explic bazele acum.
daily_routine = Rtn_start_903;
daily routine (sau rutina zilnica) este o ... rutina care practic "explica" personajului ce sa faca. Cand un personaj este insertat, rutina default va fi "daily_routine".
In scriptul nostru, daily_routine este "egala" cu rtn_start_903, a carei "informatii" se afla in partea
"FUNC VOID Rtn_start_903 ()
{
TA_PickRice (07,10,20,10,"NC_PATH83_MOVEMENT");
TA_SitCampfire (20,10,07,10,"NC_PATH_PEASANT_OUTSIDE5");
};"
Aici avem o functie. TA_PickRice si TA_SitCampFire sunt actiuni intreprinse de personajul nostru. Astfel avem:
TA_PickRice (este actiunea intreprinsa de personaj... in cazul nostru, culesul de orez) , si "(07,10,20,10,"NC_PATH83_MOVEMENT"); " Toate cifrele alea par sa nu aiba sens, insa le voi explica mai tarziu... e destul sa intelegeti ca se refera la durata, perioada in care se petrece, etc. Cel mai important in aceasta faza este Waypointul (sau locatia) in care se petrece rutina.
Astfel, avem "NC_PATH83_MOVEMENT". Aceasta este locatia in care personajul nostru va culege orez "la orele" desemnate de 'cifrele alea Smile'. O sa discutam despre waypointuri in lectia speciala pentru ele.
Asta ar fi tot. Acum trebuie doar sa va ajut sa inseratati propriul personaj. Il vom numi... Tutie. uitati scriptul final.
Code:
instance PAT_001_Tutie (Npc_Default)
{
    //-------- primary data --------
    
    name         =    "Tutie";
    npctype        =    npctype_main;
    guild         =    GIL_BAU;      
    level         =    4;
    voice         =    2;
    id             =    903;

    //-------- abilities --------
    attribute[ATR_STRENGTH] =        20;
    attribute[ATR_DEXTERITY] =        10;
    attribute[ATR_MANA_MAX] =        0;
    attribute[ATR_MANA] =            0;
    attribute[ATR_HITPOINTS_MAX] =    88;
    attribute[ATR_HITPOINTS] =        88;

    //-------- visuals --------
    //                 animations
    Mdl_SetVisual        (self,"HUMANS.MDS");
    Mdl_ApplyOverlayMds    (self,"Humans_Tired.mds");
    //            body mesh     ,bdytex,skin,head mesh     ,headtex,teethtex,ruestung    
    Mdl_SetVisualBody (self,"hum_body_Naked0",2,2,"Hum_Head_Bald", 2, 1, -1);
    
    B_Scale (self);
    Mdl_SetModelFatness(self,0);
    
    fight_tactic    =    FAI_HUMAN_COWARD;
                                    
    //-------- Talente --------                                       
    Npc_SetTalentSkill    (self,NPC_TALENT_1H,1);        
    
    
    //-------- inventory --------                                    

        
    EquipItem (self, ItMw_1H_Scythe_01);
    CreateInvItems (self, ItFoRice,3);
    
    

    //-------------Daily Routine-------------
    daily_routine = Rtn_start_001;
};



FUNC VOID Rtn_start_001 ()
{
    TA_PickRice        (07,10,20,10,"NC_PATH83_MOVEMENT");
    TA_SitCampfire    (20,10,07,10,"NC_PATH_PEASANT_OUTSIDE5");
};
Am facut schimbari minore, dar se observa. Nu uitati sa modificati instanta! De asemenea rutina personajului nou trebuie sa fie diferita de cea a personajulu vechi (ma refer la partea "daily_routine = Rtn_start_001;"
Acum ca aveti scriptul, trebuie salvat in folderul:
\Gothic\_work\DATA\scripts\content\Story\NPC
odata salvat, deschideti playerkit, bifati "reparse all scripts" in casuta mod development, si rulati gothic. Selectati new game, iar in joc, deschideti "Character Screen" si tastati in el "marvin". Daca ati facut corect ar trebui sa aveti un mesaj "marvin mode on" in partea de sus-stanga a ecranului.
Da?
Bun. Acum apasati F2, pentru a deschide consola. In consola tastati
Code:
INSERT PAT_001_Tutie
. Daca ati modificat instanta si nu este "PAT_001_Tutie" , folositi aceeasi comanda, insert dar cu instanta creata de voi.
O sa vedeti cum personajul insertat o sa se duca in New Camp sa adune orez Smile Daca doriti sa vorbiti cu el, nu o sa puteti. Deoarece nu are fisier de dialog inca. Nu veti avea decat optiunea "end". Despre dialog vom vorbi in urmatorul tutorial.

ATENTIE!
Numele scriptului salvat trebuie sa fie la fel ca si instanta! Astfel dispar si confuziile.

Lectia Nr 2 - Dialogurile
Dialogurile sunt importante la randul lor, deoarece totul se bazeaza pe ele. Story-ul jocului in special. In aceasta lectie, vom discuta despre cum sa facem un fisier de dialog (DIA) personajului nostru, si ii vom atribui si un quest(misiune), in cazul acesta, Hunkor.
Sa luam un exemplu:
Code:
// ************************************************************
//                                  EXIT
// ************************************************************

INSTANCE DIA_Hunkor_EXIT (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 999;
    condition    = DIA_Hunkor_EXIT_Condition;
    information    = DIA_Hunkor_EXIT_Info;
    permanent    = 1;
    description = DIALOG_ENDE;
};                      

FUNC INT DIA_Hunkor_EXIT_Condition()
{
    return 1;
};

FUNC VOID DIA_Hunkor_EXIT_Info()
{    
    AI_StopProcessInfos    (self);
};
Se observa commenturile de sus (am vorbit despre ele acum o lectie). Apoi avem instanta acestei PARTI de dialog.
INSTANCE DIA_Hunkor_EXIT (C_INFO)
Instanta este modificabila dar este bine sa lasati partea cu EXIT asa cum e, si sa modificati doar informatia de la NPC.
Code:
    npc            = PAT_001_Hunkor;
Ce avem aici?
Prin aceasta "linie" se face legatura cu NPC-ul nostru, Hunkor (sau Tutie... ca asa se numea inainte). Astfel, ca prin linia " npc = PAT_001_Hunkor;" se "arata" faptul ca partea aceasta de dialog este atribuita lui Hunkor.
A se observa faptul ca nu am folosit numele lui (Hunkor) ci instanta NPC-ului!
Nr = 999;
Arata "locul" ocupat de linia aceasta de dialog pe ecran. Punem 999 ca sa fim siguri ca e ultima optiune de dialog (end)
Fiecare dialog are o conditie si o informatie. Daca conditiile sunt indeplinite, scriptul "trimite" informatia.
Pentru conditie folosim:
Code:
DIA_Hunkor_EXIT_Condition;
Iar pentru informatie:
Code:
DIA_Hunkor_EXIT_Info;
A se observa faptul ca DIA_Hunkor_EXIT (instanta partii de dialog), face parte din conditie/informatie.
Code:
permanent = 1;
Valoarea unu (1) este "sinonima" cu "pozitiv". Astfel, optiunea aceasta este permanenta (chiar daca este selectata odata poate fi selectata de un numar nelimitat de ori).

Code:
FUNC INT DIA_Hunkor_EXIT_Condition()
{
    return 1;
};

FUNC VOID DIA_Hunkor_EXIT_Info()
{    
    AI_StopProcessInfos    (self);
};
Sunt 2 lucruri ce necesita atentia aici. A se observa ca a fost introdusa conditia. Dupa ea este si informatia.
Dupa conditie exista partea
Code:
{
    return 1;
};
Astfel, ca daca conditia este indeplinita este folosita comanda "return" care "intoarce" o valoare. Valoarea fiind informatia.
In cazul de fata, informatia este terminarea dialogului. Aceasta se face prin comanda " AI_StopProcessInfos (self);".
A se observa "directiva" (asa o sa o numesc de acum in colo) "self" care face legatura cu NPC-ul nostru (hunkor). AI_StopProcessInfos este comanda pentru "sfarsit de dialog" sa zic asa.
Acum o sa trecem la o parte mai... complexa.
Code:
// ************************************************************
//                         Hallo
// ************************************************************

INSTANCE DIA_Hunkor_Hello (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 1;
    condition    = DIA_Hunkor_Hello_Condition;
    information    = DIA_Hunkor_Hello_Info;
    permanent    = 0;
    description = "Ce faci aici?";
};                      

FUNC INT DIA_Hunkor_Hello_Condition()
{
    return 1;
};

FUNC VOID DIA_Hunkor_Hello_Info()
{    
    AI_Output (other, self,"DIA_Hunkor_Hello_02_00"); //Ce faci aici?
    AI_Output (self, other,"DIA_Hunkor_Hello_02_01"); //Stau! Nu se vede? Sau esti la fel de orb ca idiotul ala pe care il considerati lider?!
    AI_Output (other, self,"DIA_Hunkor_Hello_02_02"); //...
    AI_Output (self, other,"DIA_Hunkor_Hello_02_03"); //M-am saturat de lumea asta!
};
Este bine sa folosim commenturi sa separam unele parti de dialog de altele.
Se observa asemanarile cu end script, doar ca sunt diferente enorme!
De data asta dialogul nu mai este permanent! Iar la descriere este o valoare! Conditia este la fel, dar info. Difera! Sa explic mai bine:
Descrierea (Description) arata, in cazul de fata, "numele optiunii de dialog". Astfel, ca odata ce am intrat in dialog cu Hunkor, vom avea optiunea de a vorbi cu el, denumita ca "Ce faci aici?".
La info gasim comanda "AI_Output". Ce inseamna? Practic prin comanda aceasta se introduce text pe ecran! Dialogul propriu-zis.
Mai este o diferenta aici... in paranteze se afla "other, self,". Ce inseamna aceastea? Ca sa explic mai bine, traduc:
other = altcineva/altceva/altul (altul e cel mai bun termen in cazul de fata)
self = sine
deci in ordinea other, self, putem sa zicem ca Altul (eroul nostru) ii spune ceva lui Hunkor (sine, deoarece la el se refera scriptul - a se observa instanta lui la NPC)
Code:
"DIA_Hunkor_Hello_15_00");
aceasta parte arata "instanta" sa o numim asa a dialogului. Sub aceasta instanta va fi introdusa in fisierele sursa.
De asemenea aici este trecut si timingu (ordinea dialogurilor.) Astfel avem:
Code:
"DIA_Hunkor_Hello_02_00"
"DIA_Hunkor_Hello_02_01"
"DIA_Hunkor_Hello_02_02"
A se observa ordinea > 00, 01, 02.
Hmm alte commenturi? Da si nu! Commenturile dupa comenzile AI_Output sunt ignorate de compileru normal, dar le vom compila cu Spacer nu cu GothicMod.exe! O sa revenim la ele mai tarziu.

Acum va voi arata cum sa faceti un Quest!
Code:
// ************************************************************
//                         Kann ich helfen?
// ************************************************************
// ************************************************************

INSTANCE DIA_Hunkor_WannaHelp (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 1;
    condition    = DIA_Hunkor_WannaHelp_Condition;
    information    = DIA_Hunkor_WannaHelp_Info;
    permanent    = 0;
    description = "Te pot ajuta cu ceva?";
};                      

FUNC INT DIA_Hunkor_WannaHelp_Condition()
{
    if (Npc_KnowsInfo(hero, DIA_Hunkor_Hello))
    {
        return 1;
    };
};

FUNC VOID DIA_Hunkor_WannaHelp_Info()
{    
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_15_00"); //Pot sa te ajut cu ceva?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_01"); //Categoric. Vreau sa ies din acest loc.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_02"); //Bariera e magica, nu cred ca se poate distruge asa usor.
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_03"); //Trebuie sa fie o metoda. Nu am venit singur aici. Si ceilalti prieteni ai mei au fost aruncati aici.
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_04"); //Unul din prietenii mei se numea Arcus. Este un mag puternic, si foarte intelept. Sunt sigur ca va gasi o solutie la aceasta problema.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_05"); //Si trebuie sa il gasesc pe acest Arcus?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_06"); //Exact. Gaseste-l si intreaba-l daca stie ceva despre ceilalti cu care am venit. Jorkan si Rhem.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_07"); //Si cam, pe unde l-as putea gasi pe acest Arcus?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_08"); //De unde sa stiu? Pe aici pe undeva...
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_09"); //Asa... O sa incerc sa dau de el... cumva...
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_10"); //Mult noroc.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_11"); //Sper sa nu fie nevoie...
    
    Hunkor_Buttsecs = LOG_RUNNING;
    
    Log_CreateTopic        (PAT_Hunkor, LOG_MISSION);
    Log_SetTopicStatus    (PAT_Hunkor, LOG_RUNNING);
    B_LogEntry            (PAT_Hunkor,"Azi m-am intalnit cu un personaj foarte ciudat... pe nume Hunkor. Mi-a spus ca vine dintr-o lume numita Aeophia, care este in pericol. Mi-a mai zis ca i-a cerut ajutorul lui Rhobar, care pana la urma l-a aruncat in aceasta vale, la un loc cu prietenii lui. M-am oferit sa il ajut sa scape de aici, iar el mi-a spus sa vorbesc cu unul din prietenii lui, pe nume 'Arcus'. Nu a spus unde il pot gasi insa... Va trebui sa il intreb pe acest Arcus daca stie ceva despre ceilalti prieteni al lui Hunkor, 'Jorkan' si 'Rhem' ");

};
Acesta este partea de script care "introduce un quest".
Prima si prima data insa, va trebui sa deschidem alte scripturi. Deschideti scriptul Log_Constants.d din folderul:
Code:
\Gothic\_work\DATA\scripts\content\Story
In el introduceti la sfarsitul documentului urmatoarea linie:
Code:
const string    PAT_Hunkor              =   "Povestea lui Hunkor";
Astfel questul este oficial introdus in joc!
Dar mai e ceva! Deschideti Story_Globals.d din acelasi folder si scrieti, tot la sfarsit:
Code:
var int Hunkor_Quest;
Cu ajutorul VARIABILEI "Hunkor_Quest" vom face legatura la "Povestea lui Hunkor" in alte scripturi. Daca aceasta variabila este trecuta in Story_Globals, atunci va putea fi folosita in ORICE ALT SCRIPT.
Bun... acum sa ne intoarcem la dialogul nostru.
De data asta avem o conditie! Una adevarata!
Sa ne uitam mai atenti la ea!
Code:
    if (Npc_KnowsInfo(hero, DIA_Hunkor_Hello))
Ce are asa special? Hmm... pai particula "if" (tradusa ca si "daca") arata conditia propriu-zisa. In paranteza avem:
Code:
(Npc_KnowsInfo(hero, DIA_Hunkor_Hello))
Npc_KnowsInfo - Arata o functie. Daca un NPC cunoaste informatia X se intampla Y (info. de dupa conditie).
In cazul nostru avem:
(Npc_KnowsInfo (hero <arata faptul ca NPC-ul care trebuie sa cunoasca informatia X este eroul jucatorului), DIA_Hunkor_Hello <aceasta este informatia ce trebuie cunoscuta de erou. Adica, pentru ca dialogul din aceasta parte sa continue, eroul trebuie sa cunoasca deja DIA_Hunkor_Hello. Daca va uitati mai sus vedeti ca DIA_Hunkor_Hello este instanta celui de-al doilea dialog. Astfel, daca eroul "il saluta" pe Hunkor, va avea si optiunea despre care vorbim aici (DIA_Hunkor_WannaHelp). Daca Eroul nu trece prin DIA_Hunkor_Hello (daca nu il saluta pe Hunkor), aceasta optiune nu va aparea ca selectabila!>
A se observa parantezele!
Fiecare paranteza orientata spre dreapta trebuie sa se incheie cu una orientata spre stanga. Astfel, avem seturi de paranteze pentru toate subconditiile (Asa o sa le numesc de acum incolo). Putem adauga mai multe subconditii, de exemplu:
Code:
    if (Npc_KnowsInfo(hero, DIA_Hunkor_Hello)  && (Npc_KnowsInfo(hero, DIA_Hunkor_Hi))
Nu luati codul de mai sus in considerare pentru tutorialul acesta... este doar de "cultura generala". Smile
Ok, sa trecem mai departe!
Avem dialogul normal cu AI_Output, dar la sfarsit avem altceva! Hmmm ia sa ne uitam mai bine!
Code:
    Hunkor_Quest = LOG_RUNNING;
    
    Log_CreateTopic        (PAT_Hunkor, LOG_MISSION);
    Log_SetTopicStatus    (PAT_Hunkor, LOG_RUNNING);
    B_LogEntry            (PAT_Hunkor,"Azi m-am intalnit cu un personaj foarte ciudat... pe nume Hunkor. Mi-a spus ca vine dintr-o lume numita Aeophia, care este in pericol. Mi-a mai zis ca i-a cerut ajutorul lui Rhobar, care pana la urma l-a aruncat in aceasta vale, la un loc cu prietenii lui. M-am oferit sa il ajut sa scape de aici, iar el mi-a spus sa vorbesc cu unul din prietenii lui, pe nume 'Arcus'. Nu a spus unde il pot gasi insa... Va trebui sa il intreb pe acest Arcus daca stie ceva despre ceilalti prieteni al lui Hunkor, 'Jorkan' si 'Rhem' ");
Asa. Ce avem aici?
Pai, avem in primul rand:
Code:
Hunkor_Quest = LOG_RUNNING;

Ce face aceasta? Pai, daca va amintiti, ati introdus variabila Hunkor_Quest in Story_Globals.d! Prin aceasta comanda, starea variabilei Hunkor_Quest este trecuta ca LOG_Running. Adica, in cazul nostru, questul Hunkor_Quest este inceput.
Dar asta nu prea face nimic. De ce? Pai trebuie sa se intample si urmatoarele lucruri:
Code:
        Log_CreateTopic        (PAT_Hunkor, LOG_MISSION);
    Log_SetTopicStatus    (PAT_Hunkor, LOG_RUNNING);
    B_LogEntry            (PAT_Hunkor,"Azi m-am intalnit cu un personaj foarte ciudat... pe nume Hunkor. Mi-a spus ca vine dintr-o lume numita Aeophia, care este in pericol. Mi-a mai zis ca i-a cerut ajutorul lui Rhobar, care pana la urma l-a aruncat in aceasta vale, la un loc cu prietenii lui. M-am oferit sa il ajut sa scape de aici, iar el mi-a spus sa vorbesc cu unul din prietenii lui, pe nume 'Arcus'. Nu a spus unde il pot gasi insa... Va trebui sa il intreb pe acest Arcus daca stie ceva despre ceilalti prieteni al lui Hunkor, 'Jorkan' si 'Rhem' ");
Comanda Log_CreateTopic este comanda prin care se "creeaza" un nou quest in Log. Se observa legatura cu PAT_Hunkor (introdus de tine in Log_Constants.d) si locul in care este introdus!
Astfel, este introdus "Povestea lui Hunkor" (Asa l-am denumit in log_constants) in "locul" special pentru misiuni din log, numit ca instanta "LOG_MISSIONS"!
Log_SetTopicStatus este o comanda similara cu prima, dar aceasta seteaza statutul questului nostru. In cazul nostru il seteaza ca LOG_RUNNING (adica, questul "ruleaza" sa zic asa)
B_LogEntry este partea mea preferata. Cu aceasta comanda "introducem" text in subiectul special creeat pentru 'Povestea lui Hunkor' in log.
Astfel avem comanda (B_LogEntry), legatura cu instanta questului (PAT_Hunkor) si dupa virgula, ca valoare (a se observa ghilimelele) textul care urmeaza sa fie introdus!

Cel mai bine e sa faceti pe viu asta.
Deschideti din nou scriptul lui Tutie. De data asta modificati instanta in:
Code:
PAT_001_Hunkor
Numele puteti sa il lasati la fel... nu e obligatoriu sa il schimbati.Dati save la fisierul lui Tutie, odata ce ati modificat.
Bun, acum, uitati-va pe scriptul de dialog al lui Hunkor. Ar trebui sa arate asa:
Code:
// ************************************************************
//                                  EXIT
// ************************************************************

INSTANCE DIA_Hunkor_EXIT (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 999;
    condition    = DIA_Hunkor_EXIT_Condition;
    information    = DIA_Hunkor_EXIT_Info;
    permanent    = 1;
    description = DIALOG_ENDE;
};                      

FUNC INT DIA_Hunkor_EXIT_Condition()
{
    return 1;
};

FUNC VOID DIA_Hunkor_EXIT_Info()
{    
    AI_StopProcessInfos    (self);
};
// ************************************************************
//                         Hallo
// ************************************************************

INSTANCE DIA_Hunkor_Hello (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 1;
    condition    = DIA_Hunkor_Hello_Condition;
    information    = DIA_Hunkor_Hello_Info;
    permanent    = 0;
    description = "Ce faci aici?";
};                      

FUNC INT DIA_Hunkor_Hello_Condition()
{
    return 1;
};

FUNC VOID DIA_Hunkor_Hello_Info()
{    
    AI_Output (other, self,"DIA_Hunkor_Hello_15_00"); //Ce faci aici?
    AI_Output (self, other,"DIA_Hunkor_Hello_02_01"); //Stau! Nu se vede? Sau esti la fel de orb ca idiotul ala pe care il considerati lider?!
    AI_Output (other, self,"DIA_Hunkor_Hello_02_02"); //...
    AI_Output (self, other,"DIA_Hunkor_Hello_02_03"); //M-am saturat de lumea asta!
};// ************************************************************
//                         Kann ich helfen?
// ************************************************************
// ************************************************************

INSTANCE DIA_Hunkor_WannaHelp (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 1;
    condition    = DIA_Hunkor_WannaHelp_Condition;
    information    = DIA_Hunkor_WannaHelp_Info;
    permanent    = 0;
    description = "Te pot ajuta cu ceva?";
};                      

FUNC INT DIA_Hunkor_WannaHelp_Condition()
{
    if (Npc_KnowsInfo(hero, DIA_Hunkor_Hello))
    {
        return 1;
    };
};

FUNC VOID DIA_Hunkor_WannaHelp_Info()
{    
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_15_00"); //Pot sa te ajut cu ceva?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_01"); //Categoric. Vreau sa ies din acest loc.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_02"); //Bariera e magica, nu cred ca se poate distruge asa usor.
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_03"); //Trebuie sa fie o metoda. Nu am venit singur aici. Si ceilalti prieteni ai mei au fost aruncati aici.
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_04"); //Unul din prietenii mei se numea Arcus. Este un mag puternic, si foarte intelept. Sunt sigur ca va gasi o solutie la aceasta problema.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_05"); //Si trebuie sa il gasesc pe acest Arcus?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_06"); //Exact. Gaseste-l si intreaba-l daca stie ceva despre ceilalti cu care am venit. Jorkan si Rhem.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_07"); //Si cam, pe unde l-as putea gasi pe acest Arcus?
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_08"); //De unde sa stiu? Pe aici pe undeva...
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_09"); //Asa... O sa incerc sa dau de el... cumva...
    AI_Output (self, other,"DIA_Hunkor_WannaHelp_02_10"); //Mult noroc.
    AI_Output (other, self,"DIA_Hunkor_WannaHelp_02_11"); //Sper sa nu fie nevoie...
    
    Hunkor_Quest = LOG_RUNNING;
    
    Log_CreateTopic        (PAT_Hunkor, LOG_MISSION);
    Log_SetTopicStatus    (PAT_Hunkor, LOG_RUNNING);
    B_LogEntry            (PAT_Hunkor,"Azi m-am intalnit cu un personaj foarte ciudat... pe nume Hunkor. Mi-a spus ca vine dintr-o lume numita Aeophia, care este in pericol. Mi-a mai zis ca i-a cerut ajutorul lui Rhobar, care pana la urma l-a aruncat in aceasta vale, la un loc cu prietenii lui. M-am oferit sa il ajut sa scape de aici, iar el mi-a spus sa vorbesc cu unul din prietenii lui, pe nume 'Arcus'. Nu a spus unde il pot gasi insa... Va trebui sa il intreb pe acest Arcus daca stie ceva despre ceilalti prieteni al lui Hunkor, 'Jorkan' si 'Rhem' ");

};
Asa! Acum ca avem scriptul complet, si NPC-ul Hunkor, trebuie salvat si scriptul de dialog. Dialogul il vom salva in folderul:
Code:
\Gothic\_work\DATA\scripts\content\Story\MISSIONS
Sub numele:
Code:
DIA_PAT_001_Hunkor
Mai avem de facut doar un singur lucru!
Mergeti in folderul
Code:
\Gothic\_work\DATA\scripts\content\CUTSCENE
Acolo veti gasi 2 fisiere. Ou.CSL si Ou.BIN . Copiati-le si faceti-le backup in orice alt folder vreti (eu folosesc my documents). Gata? Bun!
Acum ca aveti backupul facut intoarceti-va in
Code:
\Gothic\_work\DATA\scripts\content\CUTSCENE

si stergeti ou.csl si ou.bin de acolo! Gata? Perfect!
Acum, deschideti Gothic Player Kit, si dati click pe MOD Development. Acolo bifati Reparse All scripts, si de asemenea la Application selectati SPACER nu gothic!
Ok? Invatati repede Grin
Acum, daca ati selectat spacer si reparse all scripts, dati click sus, pe Start Spacer! Asteptati sa se incarce.
Odata incarcat o sa apara mai multe ferestre. Voi selectati-o pe cea numita "output units". In acea fereastra dati click pe "UPDATE". Asteptati sa se faca update-ul, iar dupa ce update-ul este complet, dati click pe "SAVE". Gata! Acum iesiti din SPACER. Daca va uitati acum in folderul:
Code:
\Gothic\_work\DATA\scripts\content\CUTSCENE
O sa vedeti ou.csl si ou.bin acolo din nou! Dar de data asta sunt diferite. Contin si dialogul tau!
Tot ce trebuie sa mai faci acum e sa intri in gothic cu reparse all scripts bifat, si sa il introduci pe Hunkor, cum l-ai introdus si pe Tutie... Voila! De data asta ai si dialog, si quest!
In urmatorul capitol vom vorbi despre Texturi, si cum sa terminam un quest!
Lectia Nr 3 - Finisarea unui Quest
Am decis sa fac texture tutorial intr-o lectie separata iar in aceasta lectie sa vorbim doar despre finisarea unui quest!

Ok... o sa trecem la ceva mai complicat de data asta. Insa nu ar trebui sa fie ATAT de greu...
Avem deja un quest inceput, in lectiile anterioare! Mai ai scripturile nu? Perfect! Tot ce trebuie sa facem acum e sa adaugam cateva chestii noi si totul e gata!
Asa... sa incepem. Pentru inceput, o sa facem un nou NPC. Daca inca nu ai inteles pe deplin cum se face un NPC, poti oricand sa te intorci la lectia #1! Asa. Facem un NPC nou, cu numele la alegere (De preferat Arcus), cu instanta PAT_002_Arcus!
Waypointul pentru rutina este din nou, la alegere, dar ar trebui sa stii unde e acel waypoint!
Pentru "Arcus-ul" meu am folosit waypointul:
Code:
OCR_OUTSIDE_HUT_66
De asemenea l-am inlocuit pe un digger de acolo cu Arcus. Dar asta se poate face si fara inlocuire!
Oricum, sa trecem peste asta!
Ai facut fisierul NPC-ului? Perfect! Salveaza-l cum ai facut si cu cel al lui Hunkor, iar apoi o sa continuam cu restul lectiei.
Asa! Bun. Acum... ce facem cu questul din fisierul de dialog al lui Hunkor? O sa vezi acum.
Pentru inceput, facem un nou script (DIA_PAT_002_Arcus) fara continut.
Apoi vom adauga EXIT script (ca la Hunkor), o sa ma scriu odata... nu ma astept sa-l fi retinut din prima...
Code:
// ************************************************************
//                                  EXIT
// ************************************************************

INSTANCE DIA_Arcus_EXIT (C_INFO)
{
    npc            = PAT_002_Arcus;
    nr            = 999;
    condition    = DIA_Arcus_EXIT_Condition;
    information    = DIA_Arcus_EXIT_Info;
    permanent    = 1;
    description = DIALOG_ENDE;
};                      

FUNC INT DIA_Arcus_EXIT_Condition()
{
    return 1;
};

FUNC VOID DIA_Arcus_EXIT_Info()
{    
    AI_StopProcessInfos    (self);
};
Asa ar trebui sa arate. Acum, sa facem si Hello Dialogue (Desi nu e obligatoriu, eu il fac pentru a-l oferi ca si conditie la urmatoarele dialoguri!) O sa il re-scriu si pe acesta!
Code:
// ************************************************************
//                         Hallo
// ************************************************************

INSTANCE DIA_Arcus_Hello (C_INFO)
{
    npc            = PAT_002_Arcus;
    nr            = 1;
    condition    = DIA_Arcus_Hello_Condition;
    information    = DIA_Arcus_Hello_Info;
    permanent    = 0;
    description = "Cine esti?";
};                      

FUNC INT DIA_Arcus_Hello_Condition()
{
    return 1;
};

FUNC VOID DIA_Arcus_Hello_Info()
{    
    AI_Output (other, self,"DIA_Arcus_Hello_15_00"); //Cine esti?
    AI_Output (self, other,"DIA_Arcus_Hello_02_01"); //Ma numesc Arcus.
    AI_Output (other, self,"DIA_Arcus_Hello_02_02"); //Si ce faci aici?
    AI_Output (self, other,"DIA_Arcus_Hello_02_03"); //Nu vad de ce te-ar interesa pe tine acest lucru!
};
Si Hunkor si Arcus sunt foarte aroganti nu? Tongue eh asta e... puteti modifica dialogurile! In fond asta e menirea tutorialului, sa va invete sa modificati Gothic!
Asa. Acum urmeaza partea ce completeaza questul! O sa o scriu de la inceput, dar o sa discut doar unele parti din ea!
Code:
// ************************************************************
//                         Kann ich helfen?
// ************************************************************
// ************************************************************

INSTANCE DIA_Arcus_WannaHelp (C_INFO)
{
    npc            = PAT_002_Arcus;
    nr            = 1;
    condition    = DIA_Arcus_WannaHelp_Condition;
    information    = DIA_Arcus_WannaHelp_Info;
    permanent    = 0;
    description = "Hunkor m-a trimis la tine!";
};                      

FUNC INT DIA_Arcus_WannaHelp_Condition()
{
    if (Npc_KnowsInfo(hero, DIA_Arcus_Hello) && (Hunkor_Quest == LOG_RUNNING))
    {
        return 1;
    };
};

FUNC VOID DIA_Arcus_WannaHelp_Info()
{    
    AI_Output (other, self,"DIA_Arcus_WannaHelp_15_00"); //Hunkor m-a trimis la tine! A spus sa te intreb daca stii ceva despre prietenii lui, Jorkham si Rhem.
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_01"); //Asa deci, el unde e?
    AI_Output (other, self,"DIA_Arcus_WannaHelp_02_02"); //E intr-o pestera langa tabara asta!
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_03"); //Stiu despre Jorkhan si Rhem.
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_04"); //Cred ca stiu si ce plan are Hunkor... vrea sa scape... m-am uitat mai bine la bariera si se pare ca e... indestructibila...
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_05"); //Dar oricum, Jorkhan si Rhem au plecat impreuna in padure, nu au vrut sa vina cu mine in tabara.
    AI_Output (other, self,"DIA_Arcus_WannaHelp_02_06"); //De ce?
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_07"); //Eu de unde sa stiu? Poate ca nu le-ar fi placut sa traiasca aici.
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_08"); //Oricum, i-am vazut plecand spre ceea ce numiti voi, Tabara din Mlastina. Sau Tabara Sectei.
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_09"); //Spune-i lui Rhem sa mearga in pestera lui Hunkor. Deindata ce i-ai spus acest lucru, vino inapoi si spune-mi. O sa continuam atunci. Jorkham NU trebuie sa afle despre acest lucru in NICI UN FEL... nu intreba de ce, am motivele mele.
    AI_Output (other, self,"DIA_Arcus_WannaHelp_02_10"); //Am inteles... O sa incerc sa il gasesc.
    AI_Output (self, other,"DIA_Arcus_WannaHelp_02_11"); //Inca ceva... Ia aceasta carte... e pentru Rhem... zi-i ca e din partea mea... e ziua lui de nastere azi... isi dorea de mult aceasta carte...
    AI_Output (other, self,"DIA_Arcus_WannaHelp_02_12"); //Bun... o sa vad ce pot face...
    
    CreateInvItem   (self, ArcusBook);
    B_GiveInvItems  (self,other, ArcusBook,1);
    
    Hunkor_Quest = LOG_SUCCESS;
    Log_SetTopicStatus            (PAT_Hunkor,    LOG_SUCCESS);
    B_LogEntry            (PAT_Hunkor,"M-am intalnit cu Arcus. Desi acesta mi-a dat alta insarcinare, am decis sa inchei acest 'capitol'");
        B_GiveXP            (200);
};
OK! Ce observam aici?
In primul rand in conditia "mare" (asa o sa o numesc de acum incolo), se afla 2 subconditii! Cunosti deja ce inseamna prima, vreau sa iti atrag atentia asupra celei de-a doua!
Code:
Hunkor_Quest == LOG_RUNNING
Ce poate insemna? Hmm... daca iti mai aduci aminte bine, ai introdu Hunkor_Quest in story_globals.d.
De asemenea, ai setat Hunkor_Quest ca LOG_RUNNING in scriptul de dialog al lui Hunkor! Astfel, daca ai inceput practic questul dat de hunkor, poti vorbi despre acest lucru cu Arcus!
Vreau sa mai vorbesc aici, si despre urmatoarea parte:
Code:
    Hunkor_Quest = LOG_SUCCESS;
    Log_SetTopicStatus            (PAT_Hunkor,    LOG_SUCCESS);
        B_GiveXP            (200);
Sunt foarte asemanatoare cu cele din scriptul lui Hunkor nu?
E cam acelasi lucru. Doar ca in partea ...
Code:
    Hunkor_Quest = LOG_SUCCESS;
... nu se seteaza ca inainte LOG_RUNNING (quest in desfasurare) ci LOG_SUCCESS (Quest terminat), astfel ca dupa ce s-a terminat dialogul de dupa AI_Output command, o sa se execute aceste functii, iar questul o sa se termine dupa dialog!
Code:
    Log_SetTopicStatus            (PAT_Hunkor,    LOG_SUCCESS);
Acum mai trebuie sa folosim comada de mai sus pentru a muta "Povestea lui Hunkor" la locul ei in log, unde stau questurile completate Grin
Code:
B_GiveXP            (200);
Ce face comanda de mai sus? Este noua nu? Smile Nu am mai folosit-o pana acum asa ca o sa o explic:
B_GiveXP este o comanda care, cand este executata "ofera" personajului principal (eroul nostru) un anumit numar de experienta.
Numarul de experienta este acelasi cu numarul valorii, iar valoarea este cea din parantezele rotunde! Astfel, dupa ce Eroul a vorbit cu Arcus si Questul a fost terminat, va primi 200 experienta!
Am ales sa fac un script cu o continuare de quest (dar nu o sa o continui), deoarece contine o parte interesanta!
Code:
    CreateInvItem   (self, ArcusBook);
    B_GiveInvItems  (self,other, ArcusBook,1);
Ce face aceasta?
<pentru a evita confuzii mentionez ca "ArcusBook" este instanta unui obiect din Hunkor's Tale MOD>
Code:
CreateInvItem
Comanda de mai sus, practic creeaza un obiect in inventarul NPC-ului definit de directiva (self). Astfel, obiectul cu instanta ArcusBook este creeat in inventarul lui Arcus (self)!
Aceasta comanda este foarte folositoare pentru a te asigura ca NPC-ul are intr-adevar obiectul de care ai nevoie!
Code:
    B_GiveInvItems  (self,other, ArcusBook,1);
Observati vreo asemanare cu o comanda din script? Da... seamana cu AI_Output! Seamana si la nivel "tehnic".
Daca la AI_Output personajul Arcus (self) ii spunea ceva eroului (other) aici, personajul Arcus (self) ii da ceva eroului (other). In cazul nostru, ii da UN obiect cu instanta ArcusBook.
De ce am scris cu litera mare UN? pentru ca dupa directive si instanta este prezenta si o valoare, si anume:
Code:
    "1"
Valoarea aceea indica cate obiecte din tipul ala o sa ii dea Arcus eroului! In cazul nostru este 1, dar toata tarasenia este perfect modificabila!
S-ar putea sa fiti cam in ceata acum ca am pomenit de obiecte... nu trebuie neaparat sa le cunoasteti la lectia asta. O sa le prezint in lectia urmatoare, dar voiam sa stiti cate ceva de dinainte, e mai simplu asa!
Asa! Acum avem si scriptul de dialog al lui Arcus, si questul completat! Salvati tot scriptul, si faceti la fel cu OU.CSL si OU.BIN si cu SPACER cum am facut la dialogul lui Hunkor.

--OPTIONAL--
Pentru a testa mai repede, puteti inserta amandoua personajele unul dupa celalalt. Dar totusi ar fi mai interesant sa faceti altfel!
Deschideti din folderul:
Code:
\Gothic\_work\DATA\scripts\content\Story
Scriptul "Startup.d" !
In el cautati urmatoarele cuvinte:
Code:
VLK_576_Buddler
Ati gasit? Perfect. Acum inlocuiti "VLK_576_Buddler" cu urmatoarea instanta:
Code:
PAT_002_Arcus
Da, acum cand o sa pornesti gothic, Arcus o sa fie insertat in locul diggerului acela (de obicei nu e bine sa insertezi 2 personaje cu aceeasi rutina, asa ca eu elimin de obicei diggeri fara folos ca sa bag un personaj nou in locul lor..
Hai sa il inseram si pe Hunkor... hmm undeva mai retras... In pestera de langa Cavalorn!
Adauga in startup.d urmatoarea linie:
Code:
    Wld_InsertNpc        (PAT_001_Hunkor,"OW_SAWHUT_MOLERAT_SPAWN01");
Gata? Perfect! Daca vrei, poti sa scoti de la startup si molerats de langa Hunkor, dar oricum ar fi, pana la urma tot Hunkor ramane in pestera Wink Sau poti sa inlocuiesti un molerat cu Hunkor!

--
Cum gasim Waypoints pentru insert si rutine? Hmmm... Pai putem sa le facem noi (dar o sa studiem asta mult mai tarziu), sau sa le folosim pe cele existente.
Putem sa le gasim pe cele existente ruland Gothic, si tastand in consola "Toggle Waynet"
astfel o sa se vada si "cararile de la waypoint la waypoint" si instantele (numele) waypointurilor!

IMPORTANT!!!
Daca l-ai inserat pe Hunkor in startup.d la molerat spawn nu inseamna ca ramane acolo! El acolo e insertat, dar daca iti mai aduci aminte, rutina lui consta in adunatul de orez in NEW CAMP. Asa ca va trebui sa modificam rutina putin!

Code:
    TA_Stay        (07,30,21,30,"OW_SAWHUT_MOLERAT_SPAWN01");
    TA_Stay        (21,30,07,30,"OW_SAWHUT_MOLERAT_SPAWN01");
Cu rutina aceasta va sta in pestera!
Pentru arcus poti folosi rutina mea:
Code:
    TA_Sleep        (22,45,06,30,"OCR_HUT_66");
    TA_StandAround  (07,00,12,00,"OCR_OUTSIDE_HUT_66");
    TA_Smalltalk    (12,00,17,00,"OCR_OUTSIDE_HUT_66");
    TA_SitCampfire    (17,00,22,45,"OCR_OUTSIDE_HUT_66");
Este o rutina mai complexa, dar mai realista!

Dar sa ne intoarcem la ale noastre Grin
Ai terminat scripturile lui Arcus? Salveaza-le, fa "schema" cu OU.CSL/OU.BIN si cu SPACER, fa daca vrei si insert in startup.d, iar apoi ruleaza Gothic cu reparse all scripts bifat. Si o sa poti incepe questul, termina questul! Felicitari! Ai parcurs lectia a 3-a din tutorial!
Urmatoarea lectie se refera la obiecte/arme/armuri.
Lectia #4 - Obiecte
Desigur, nu poti termina orice quest doar prin dialog Grin Ar fi timpul sa facem ceva mai complex. In acest tutorial o sa va explic ce si cum sunt obiectele, cum se insereaza, si cum sunt folosite in diferite scopuri!
Sa luam prima data un obiect simplu, si sa il introducem in questul nostru!
Sa deschidem prima data fisierul DIA_ Al lui Hunkor, cel pe care l-ati editat inainte! Lasam totul cum este, adaugam altceva insa!

Code:
// ************************************************************
//                         tutorial3
// ************************************************************
// ************************************************************

INSTANCE DIA_Hunkor_Obiect (C_INFO)
{
    npc            = PAT_001_Hunkor;
    nr            = 1;
    condition    = DIA_Hunkor_Obiect_Condition;
    information    = DIA_Hunkor_Obiect_Info;
    permanent    = 0;
    description = "Poftim obiectul tau!";
};                      

FUNC INT DIA_Hunkor_Obiect_Condition()
{
    if (Npc_HasItems ( hero, ItKeLockpick ) && (Hunkor_Quest == LOG_RUNNING))
    {
        return 1;
    };
};

FUNC VOID DIA_Hunkor_Obiect_Info()
{    
    AI_Output (other, self,"DIA_Hunkor_Obiect_15_00"); //Ti-am adus obiectul acesta!
    AI_Output (self, other,"DIA_Hunkor_Obiect_02_01"); //Multumesc!

    
    CreateInvItem       (other, ItKeLockpick);
    B_GiveInvItems      (other, self, ItKeLockpick,1);
    Npc_RemoveInvItem    (other,    ItKeLockpick);
    
    B_LogEntry            (PAT_Hunkor,"I-am dat obiectul lui Hunkor");
    B_GiveXP            (500);
};
Sa discutam intai pe scriptul acesta iar apoi o sa trecem la analiza obiectelor.
Ca obiect in acest quest am folosit un LockPick (ItKeLockpick ca instanta). Ca si conditie am folosit
una din subconditii ca jucatorul sa aiba acel lockpick :
Code:
(Npc_HasItems ( hero, ItKeLockpick )
Iar ca a doua conditie, Hunkor_Quest TREBUIE SA RULEZE pentru ca jucatorul sa ii dea obiectul lui Hunkor!
Se observa faptul ca am dat obiecte (nu am primit cum s-a intamplat in tut. trecut), aceasta s-a rezolvat schimband directiva din self/other in other/self Smile
Un log entry pe questlog si XP-ul!
puteti sa scoateti conditia de quest active, dar asa nu prea ar avea hunkor nevoie de lockpick. puteti si modifica PAT_Hunkor, si dialogul de mai inainte ca sa ceara un lockpick!
Sa ne uitam acum mai atent la obiectul Lockpick!
Code:
INSTANCE ItKeLockpick (C_Item)
{
    name                 =    "Picklock";

    mainflag             =    ITEM_KAT_NONE;
    flags                 =    ITEM_MULTI;

    value                 =    Value_Dietrich;

    visual                 =    "ItKe_Lockpick_01.3ds";
    material             =    MAT_METAL;

    description            = name;
    TEXT[4]                = NAME_Value;                    COUNT[4]    = Value_Dietrich;
};
E mai diferit decat celelalte nu? Smile Dar nu e deloc greu sa intelegem ce e cu codul asta!
In primul rand avem instanta, pe care sunt sigur ca ai observat-o deja!
Apoi avem numele (name), la fel ca la un NPC!
'Mainflag' si 'flags' difera de la obiect la obiect! Sa ne imaginam ceva mai ciudat...
Avem 3 gemeni! Nu ii putem deosebi unul de celalalt. Fiecare din ei tine in mana un 'steag', iar fiecare steag este diferit de celalalt.
Ei bine prin mainflag un obiect este atribuit unei anumite "categorii"! Exista multe mainflags dar nu le voi discuta pentru ca nu are rost... o sa va dati seama pana la urma ca astfel de lucruri se trec cu vederea!
Flags sunt proprietati minore (sau categorii minore la care este atribuit obiectul). Din nou lasam asta cum e!
Code:
    value                 =    Value_Dietrich;
Hmmm... De ce nu este introdusa o valoare numerica la 'value'? Aceasta este introdusa deja dar nu aici.
Picklock se afla in scriptul MISC.d din folderul:
Code:
_work/data/scripts/content/items
Deschideti scriptul! Dati scroll putin mai jos.
O sa gasiti picklock acolo, si valoarea "Value_Dietrich". Daca va uitati la inceputul scriptului o sa vedeti:
Code:
const int    Value_Dietrich            =    10;
Valoatea picklockului a fost introdusa ca o constanta in partea de sus a scriptului. Valoarea insa se poate face si numeric direct din scriptul picklockului astfel putem avea:
[/code]value = "10";[/code]

Dar nu uitati ghilimelele!
Apoi avem materialul din care este 'facut' itemul:
Code:
    material             =    MAT_METAL;
Din nou, neavand asa mare importanta, lasam asta doar pentru cei ce vor sa stie mai mult...
Apoi avem:
Code:
    description            = name;
    TEXT[4]                = NAME_Value;                    COUNT[4]    = Value_Dietrich;
Asta e noua! Ce este aici?
Va puteti gandi la 'description' ca la primul slot de text din item view. Daca rulati gothic si examinati din inventar un item, textul de la mijloc sus, in ciuda faptului ca e 'titlul' itemului, este defapt numit 'descriere'
Apoi avem celelalte sloturi de text! Mai jos de description avem:
TEXT[1]
TEXT[2]
etc.
La description vedem " = name;"
Astfel, ca in locul pentru textul descrierii o sa apara numele (titlul...) itemului nostru!
TEXT[4] trece direct foarte jos! prin NAME_Value este introdusa o constanta automat numita 'value'. Iar prin COUNT[4] este atribuita o valoare acelei constante, si anume valoarea picklockului nostru!

Cam atat despre items. Desigur, sunt mult mai multe tiprui de obiecte, dar toate la timpul lor Smile La urmatoarea lectie va dau o tura in Spacer! Plus imagini ^^ Pana atunci o sa va prezint ceva mai multe obiecte diferite:
Show Content
V-am bagat in ceata? ^^ Nu-i nimic, o sa mai fie o lectie cu Items speciale unde vom discuta despre arme, potiuni, artefacte etc!
Items gasiti in:
_work/data/scripts/content/items
In diverse scripturi.
Good luck!
pentru a insera un obiect:
marvin mode > insert <instanta obiectului>
Dar fara <>!

Lectia #5 - Spacer

De data asta vom invata cum sa inseram un NPC in lumea noastra folosind un event "triggered" de un levier.
Vom folosi un .zen existent deja insa. Turnul lui Xardas, spre exemplu! Il gasim in:
Code:
\Gothic\_work\DATA\Worlds\_work

Sub numele de "Demontower.zen". Pentru a lucra mai usor il copiem in:
Code:
\Gothic\_work\DATA\Worlds
Gata? Bun. Porniti Spacer si deschideti .zen-ul din "\Gothic\_work\DATA\Worlds" copiat mai devreme (open .zen)
Navigati pana vedeti ceva de genul:
(intrarea in turn) - click pe spoiler

Show Content
Navigati apoi in camera din dreapta.
Show Content
Aici vom insera un Waypoint nou. In caz ca nu e activata, dat click pe "activate" in objects window. Dati click dupa ce ati activat fereastra, pe zCvobWaypoint! Apoi, in fereastra cu lumea, click dreapta undeva pe pamant si click pe insert zCvobWaypoint. Va trebui sa ii dati un nume. Numiti-l "TutorialWP1", fara ghilimele desigur. Gata? Daca nu e asezat cum trebuie asezati-l cumva mai sus putin de pamant!
Gata? Bun. Trecem mai departe.
Vom insera acum un Levier.
Tot din objects window (daca nu este activata, activati din nou), 'dezvoltati' sectiunea "oCVob (Abstract)" din panou, pana ajungeti sa vedeti oCMOB. Dezvoltati si oCMOB, si dati click (DAR NU DEZVOLTATI) pe oCMobInter.
Click acum pe fereastra cu lumea, cum ati facut si cu Waypoint. Insert oCMobInter! Acum ar trebui sa aveti fereastra Objects schimbata. In ea vedeti proprietatile obiectului. Ce trebuie sa modificati. La "visual", folositi "LEVER_1_OC.MDS". Apasati "apply". Acum ar trebui sa vedeti levierul! Plasati-l undeva PE un zid, iar planul de jos (al "cutiei" rosii ce inconjoara levierul) sa ATINGA pamantul! Gata? Bun. Mai trebuie totusi sa modificam ceva.
Setati tot in acea fereastra cdDyn = TRUE (dati dublu-click pe fals). Astfel coliziunile vor fi "procesate in mod dinamic" sa zic asa. Apasati Apply! Ok... mai este totusi ceva de facut.
Asa... Acum avem nevoie de un script care sa ii arate levierului ce sa faca cand este activat. Pentru asta, "dezvoltati" zCTriggerBase, in urmatoarea ordine:
Code:
zCTriggerBase - zCTrigger - oCTriggerScript
Ar trebui sa se "vada" cam asa:
Show Content
Insertati un oCTriggerScript undeva LANGA levier. Gata? Ok. Modificam acum ceva in proprietatile sale.
Jos de tot in fereastra triggerului se afla "elementul" scriptFunc. Vom introduce in acel loc urmatoarea valoare: MOD_TUTORIAL_01 ! Apasati Apply.
Tot in acea fereastra, sus la VobName, folositi: TUTORIALSCRIPT !
Asa. Apasati apply. Este timpul sa conectam levierul la script. Dati click pe levier, si in fereastra obiectului, introduceti la "TriggerTarget" , VobName-ul scripttriggerului insertat mai devreme. Mai exact, TUTORIALSCRIPT. Daca ati facut totul corect, dupa ce apasati Apply, o linie albastra ar trebui sa fie vizibila intre Levier si ScriptTrigger. Gata. Mai trebuie doar sa introducem un StartPoint (cum ati introdus si waypoint), pe care il plasam undeva langa levier, pe pamant. (startpoint se afla chiar langa zCVobWaypoint)
Plasati-l unde doriti, si gata! Dati click pe File, sus, si Save .Zen in folderul "_WORK/DATA/WORLDS", unde, daca ati copiat, normal faceti overwrite.
Gata. Acum trebuie sa facem nitel scripting. Vom insera ... un ... err... molerat cand levierul este tras!
Deschideti un editor de text (si notepad e bun... eu folosesc Gothic Sourcer tho) si scrieti urmatoarea in el:
Code:
FUNC VOID MOD_TUTORIAL_01 ()
{
Wld_InsertNpc (MOLERAT,"TutorialWP1");
};
Gata? Ok. Acum va trebui sa salvam scriptul in:
Code:
SCRIPTS/CONTENT/STORY/EVENTS
sub numele: TUTORIALMODEVENT.d !
Gata. Deschideti acum folderul:
Gothic/System . In el veti gasi "Defaultmod.ini". Deschideti acel fisier si modificati:
Code:
world=world.ZEN
in
Code:
world=Demontower.zen
Dati save. Deschideti gothic starter, si selectati Vorlage Fur Mod.ini. Rulati Gothic, cu comanda reparse all scripts on. Dati new game si trageti de levier Grin Lupta nu ar trebui sa fie asa grea.
-------------------------------------------------------
Waypoint image:
Show Content

Post: #6
RE: Patrunjelu's Modding Tutorial

Interlude - Trading
Pentru ca am vazut ca un user de aici a dorit sa afle cum sa faca un NPC care sa comercializeze lucruri, o sa fac un mic tutorial care sa fie vazut de TOTI care doresc sa afle acest lucru. Totul este FOARTE simplu. Pentru a adauga obiecte in inventarul NPC-ului vostru adaugati in fisierul lui, obiectele folosind comanda "CreateInvItems" (daca nu stiti la ce ma refer, citit i tutorialul de mai sus... sau uitati-va in scriptul unui personaj existent! gen Wolf). Acum ca i-ati infipt ceva obiecte in inventar, urmeaza sa faceti partea de dialog care sa "inceapa" tradingul. Adaugati:
Code:
//**************************************** // TRADE //**************************************** instance NPC_6996_NPCultau_TRADE (C_INFO) { npc = NPC_6996_NPCultau; nr = 800; condition = NPC_6996_NPCultau_TRADE_Condition; information = NPC_6996_NPCultau_TRADE_Info; permanent = 1; description = DIALOG_TRADE; trade = 1; }; FUNC int NPC_6996_NPCultau_TRADE_Condition() { return 1; }; FUNC void NPC_6996_NPCultau_TRADE_Info() { AI_Output (other, self,"NPC_6996_NPCultau_TRADE_15_00"); //Oh hay! AI_Output (self, other,"NPC_6996_NPCultau_TRADE_09_01"); //Taek a look at my goods mon! };
In fisierul de dialog al npc-ului vostru. Desi e mura in gura, luati din nou aminte ca instanta npc-ului si dialogul sunt modificabile >.>

Why examine me?
[Image: Random_River_Troll.png]
You'd be dead soon anyway.
(This post was last modified: 08-25-2008 04:48 PM by Patrunjelu'.)
Interlude - Tips & Tricks 1


Daca doriti in cazul unui quest ca nameless sa faca diverse lucruri intr-un quest si nu exista conditie de genul "If_NPCHasItems(BLA_001_NPC, ITEM)" puteti sa faceti urmatorul lucru.

In story_globals declarati o variabila la alegere
Code:
var int numevariabila;
(va fi una numerica). Initial va avea valoarea 0. O puteti folosi intr-un DIA file astfel:
(Exemplu.)
"Nameless trebuie sa imparta 5 armuri unor 5 persoane. Cum aflam daca le-a impartit pe toate ACELOR persoane?"
De fiecare data cand ii da armura persoanei potrivite putem sa crestem valoarea variabilei numerice declarate cu o valoare numerica oarecare (eu folosesc unu) astfel:
numevariabila = numevariabila + 1;

Astfel ca dupa ce va imparti cele 5 armuri persoanelor potrivite, variabila noastra (in cazul meu) va avea valoarea 5. Acum putem pune conditia
if (numevariabila == 5) unui dialog pentru a termina questul.

Posibilitatile sunt multiple. Nu ne rezumam doar la "impartit de armuri". Putem sa folosim si pentru a verifica daca nameless a aflat informatii de la diferite npc-uri. Dar v-ati prins deja asa ca lasati-va imaginatia sa zboare ^^
GoMan - Export/Import Files

---------------------------
Show Content

Am decis sa il pun deoarece poate sunt unii care au probleme export/import al fisierelor cu GoMan. Al capone mi-a semnalat una chiar azi.
Va rog nu postati in threadul asta ci la "discutii despre modding".