Obydwa systemy przeniesiemy wprost ze skryptów G2NK, lekko je dostosowując tak by działały pod Gothic 1.
Dobrze jest mieć skrypty z G2nk gdzieś pod ręką, aczkolwiek nie jest to konieczne, jeśli chce się korzystać tylko z tego samouczka.
1. Część pierwsza - walka bronią
Zacznijmy od porównania jak wyglądają obydwa systemy nauki w g1 i g2nk.
System walki broniami w G1 jest trzystopniowy. Gracz zaczyna od zera z poziomem "brak" i może awansować na poziom "adept" oraz "mistrz" poprzez bezpośrednie wyuczenie się tych poziomów u NPC. Każdy z poziomów posiada odmienne animacje ataku oraz sztywno przydzieloną szansę na uderzenie krytyczne.
W G2NK wygląda to odmiennie. Gracz nie uczy się samych poziomów, lecz podnosi swą biegłość w używaniu danego rodzaju broni wyrażonej w procentowej szansie na obrażenia krytyczne. Po osiągnięciu odpowiednio wysokiej wartości gra automatycznie ustawia bohaterowi kolejny poziom. Tych podobnie jak w G1 jest trzy i również odpowiadają za animacje ataku, noszą jednak lekko odmienne nazwy (np. zielony, wojownik, mistrz). Gracz startuje z 10% szansą na trafienie krytyczne w każdym rodzaju broni, a gra automatycznie awansuje nas na drugi poziom po osiągnięciu 30% biegłości, oraz na trzeci - przy progu 60%. Progi to kolejna znacząca różnica pomiędzy G1, a G2nk. Przechodząc do rzeczy, istnieją 3 progi, od których zależny jest koszt Punktów Nauki (PN) jaki wydamy na 1% w biegłości danej broni. Do 30% biegłości: 1% = 1PN, od 30% do 60%: 1% = 2PN, od 60% do 90%: 1% = 3PN, od 90%: 1% = 4PN. Można by pociągnąć to jeszcze wyżej jednak osiągnięcie 100% biegłości jest zazwyczaj limitem.
Część teoretyczna za nami, a przed nami zadanie by przenieść to co wyżej zostało opisane do G1.
Interesuje nas kilka funkcji, pierwsza z nich nosi nazwę B_TeachFightTalentPercent. Otwieramy plik ze skryptów G2NK: Story\B_Story\B_TeachFighTalentPercent.d i kopiujemy naszą funkcję.
func int B_TeachFightTalentPercent(var C_Npc slf,var C_Npc oth,var int talent,var int percent,var int teacherMAX)
{
var string concatText;
var int kosten;
var int realHitChance;
kosten = B_GetLearnCostTalent(oth,talent,1) * percent;
if((talent != NPC_TALENT_1H) && (talent != NPC_TALENT_2H) && (talent != NPC_TALENT_BOW) && (talent != NPC_TALENT_CROSSBOW))
{
Print("*** Błąd: Zły parametr ***");
return FALSE;
};
if(talent == NPC_TALENT_1H)
{
realHitChance = oth.HitChance[NPC_TALENT_1H];
}
else if(talent == NPC_TALENT_2H)
{
realHitChance = oth.HitChance[NPC_TALENT_2H];
}
else if(talent == NPC_TALENT_BOW)
{
realHitChance = oth.HitChance[NPC_TALENT_BOW];
}
else if(talent == NPC_TALENT_CROSSBOW)
{
realHitChance = oth.HitChance[NPC_TALENT_CROSSBOW];
};
if(realHitChance >= teacherMAX)
{
concatText = ConcatStrings(PRINT_NoLearnOverPersonalMAX,IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNYOUREBETTER");
return FALSE;
};
if((realHitChance + percent) > teacherMAX)
{
concatText = ConcatStrings(PRINT_NoLearnOverPersonalMAX,IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNOVERPERSONALMAX");
return FALSE;
};
if(oth.lp < kosten)
{
PrintScreen(PRINT_NotEnoughLP,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNNOPOINTS");
return FALSE;
};
oth.lp = oth.lp - kosten;
if(talent == NPC_TALENT_1H)
{
B_RaiseFightTalent(oth,NPC_TALENT_1H,percent);
if(oth.aivar[REAL_TALENT_1H] >= (oth.aivar[REAL_TALENT_2H] + 30))
{
B_RaiseFightTalent(oth,NPC_TALENT_2H,percent);
PrintScreen(PRINT_Learn1H_and_2H,-1,-1,FONT_Screen,2);
}
else
{
PrintScreen(PRINT_Learn1H,-1,-1,FONT_Screen,2);
};
return TRUE;
};
if(talent == NPC_TALENT_2H)
{
B_RaiseFightTalent(oth,NPC_TALENT_2H,percent);
if(oth.aivar[REAL_TALENT_2H] >= (oth.aivar[REAL_TALENT_1H] + 30))
{
B_RaiseFightTalent(oth,NPC_TALENT_1H,percent);
PrintScreen(PRINT_Learn2H_and_1H,-1,-1,FONT_Screen,2);
}
else
{
PrintScreen(PRINT_Learn2H,-1,-1,FONT_Screen,2);
};
return TRUE;
};
if(talent == NPC_TALENT_BOW)
{
B_RaiseFightTalent(oth,NPC_TALENT_BOW,percent);
if(oth.aivar[REAL_TALENT_BOW] >= (oth.aivar[REAL_TALENT_CROSSBOW] + 30))
{
B_RaiseFightTalent(oth,NPC_TALENT_CROSSBOW,percent);
PrintScreen(PRINT_LearnBow_and_Crossbow,-1,-1,FONT_Screen,2);
}
else
{
PrintScreen(PRINT_LearnBow,-1,-1,FONT_Screen,2);
};
return TRUE;
};
if(talent == NPC_TALENT_CROSSBOW)
{
B_RaiseFightTalent(oth,NPC_TALENT_CROSSBOW,percent);
if(oth.aivar[REAL_TALENT_CROSSBOW] >= (oth.aivar[REAL_TALENT_BOW] + 30))
{
B_RaiseFightTalent(oth,NPC_TALENT_BOW,percent);
PrintScreen(PRINT_LearnCrossbow_and_Bow,-1,-1,FONT_Screen,2);
}
else
{
PrintScreen(PRINT_LearnCrossbow,-1,-1,FONT_Screen,2);
};
return TRUE;
};
};
Wklejamy ją do skryptów G1 (ja umieściłem ją w pliku Story\B\B_GiveSkill.d, ale jeśli chcemy można utworzyć osobny plik na tą funkcję).
Pierwszym co edytujemy są linijki:
realHitChance = oth.HitChance[NPC_TALENT_1H];
realHitChance = oth.HitChance[NPC_TALENT_2H];
realHitChance = oth.HitChance[NPC_TALENT_BOW];
realHitChance = oth.HitChance[NPC_TALENT_CROSSBOW];
w G1 Hitchance nie istnieje, zmieniamy je więc na Npc_GetTalentValue (funkcja, która pobiera "wysokość skilla" danej umiejętności):
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_1H);
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_2H);
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_BOW);
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW);
Następna rzecz, którą zmieniamy są linijki:
concatText = ConcatStrings(PRINT_NoLearnOverPersonalMAX,IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
na:
concatText = ConcatStrings("Maksimum dla tego nauczyciela wynosi ",IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",2);
Stała z tekstem PRINT_NoLearnOverPersonalMAX występuje tylko w G2, dlatego musimy albo dodać ją sami albo użyć w jej miejscu podobnej stałej z G1. Można też wpisać po prostu tekst w cudzysłowiu (ten sposób jest zaprezentowany na przykładzie).
Podobnie ma się sprawa ze stałą definiującą czcionkę. FONT_Screen nie występuje w G1 dlatego, od razu użyłem bezpośredniej nazwy czcionki "FONT_OLD_20_WHITE.TGA".
Z resztą przeniesionych stałych postępujemy podobnie.
Przejdźmy teraz do części funkcji odpowiedzialnej za podnoszenie umiejętności broni. Za przykład weźmy broń jednoręczną, pozostałe rodzaje broni zedytujemy analogicznie.
if(talent == NPC_TALENT_1H)
{
B_RaiseFightTalent(oth,NPC_TALENT_1H,percent);
if(oth.aivar[REAL_TALENT_1H] >= (oth.aivar[REAL_TALENT_2H] + 30))
{
B_RaiseFightTalent(oth,NPC_TALENT_2H,percent);
PrintScreen(PRINT_Learn1H_and_2H,-1,-1,FONT_Screen,2);
}
else
{
PrintScreen(PRINT_Learn1H,-1,-1,FONT_Screen,2);
};
return TRUE;
};
Widzimy, że do procentowego podnoszenia biegłości w danej broni używana jest kolejna funkcja: B_RaiseFightTalent. Oczywiście funkcja nie istnieje w G1, a przeniesienie jej było by trudne gdyż bazuje na nieobecnych w g1 aivarach. Dlatego posłużymy się obecną w G1 funkcją Npc_SetTalentValue.
Zastępujemy linijkę:
B_RaiseFightTalent(oth,NPC_TALENT_1H,percent);
na:
Npc_SetTalentValue(hero, NPC_TALENT_1H, Npc_GetTalentValue(hero, NPC_TALENT_1H)+percent);
Teraz musimy edytować warunek, odpowiedzialny za automatyczny trening dwóch broni naraz. Zastępujemy linijkę:
if(oth.aivar[REAL_TALENT_1H] >= (oth.aivar[REAL_TALENT_2H] + 30))
na:
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= (Npc_GetTalentValue(hero,NPC_TALENT_2H) +30))
Potem analogicznie jak przedtem:
B_RaiseFightTalent(oth,NPC_TALENT_2H,percent);
na:
Npc_SetTalentValue(hero, NPC_TALENT_2H, Npc_GetTalentValue(hero, NPC_TALENT_2H)+percent);
Znów dodajemy stałą z tekstem lub dopisujemy tekst sami, zmieniając:
PrintScreen(PRINT_Learn1H_and_2H,-1,-1,FONT_Screen,2);
na:
PrintScreen("Trening: posługiwanie się bronią jedno- i dwuręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
oraz
PrintScreen(PRINT_Learn1H,-1,-1,FONT_Screen,2);
na:
PrintScreen("Trening: posługiwanie się bronią jednoręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
To jednak jeszcze nie wszystko. Teraz musimy zadbać o to, aby zależnie od wartości procentowej w umiejętności, gra odpowiednio zwiększała jej poziom (zielony, wojownik, adept). Do tego celu dodamy nową funkcję B_SetFightSkill (wzorowana na podobnej funkcji z g2notr B_AddFightSkill).
func void B_SetFightSkill(var C_Npc slf,var int talent,var int percent)
{
if(talent == NPC_TALENT_1H)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 0)
{
Npc_SetTalentSkill(slf,NPC_TALENT_1H,0);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 30)
{
Npc_SetTalentSkill(slf,NPC_TALENT_1H,1);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 60)
{
Npc_SetTalentSkill(slf,NPC_TALENT_1H,2);
};
};
if(talent == NPC_TALENT_2H)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 0)
{
Npc_SetTalentSkill(slf,NPC_TALENT_2H,0);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 30)
{
Npc_SetTalentSkill(slf,NPC_TALENT_2H,1);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 60)
{
Npc_SetTalentSkill(slf,NPC_TALENT_2H,2);
};
};
if(talent == NPC_TALENT_BOW)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 0)
{
Npc_SetTalentSkill(slf,NPC_TALENT_BOW,0);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 30)
{
Npc_SetTalentSkill(slf,NPC_TALENT_BOW,1);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 60)
{
Npc_SetTalentSkill(slf,NPC_TALENT_BOW,2);
};
};
if(talent == NPC_TALENT_CROSSBOW)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 0)
{
Npc_SetTalentSkill(slf,NPC_TALENT_CROSSBOW,0);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 30)
{
Npc_SetTalentSkill(slf,NPC_TALENT_CROSSBOW,1);
};
if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 60)
{
Npc_SetTalentSkill(slf,NPC_TALENT_CROSSBOW,2);
};
};
};
Jak widać, funkcja sprawdza obecną wartość umiejętności i w zależności pod jaki próg się kwalifikuje, ustawia skill od 0, 1, 2 (czyli dla broni jednoręcznej będzie to odpowiednio: zielony, wojownik, mistrz).
Wracamy więc do naszej pierwszej funkcji (B_TeachFightTalentPercent) i do warunku odpowiedzialnego za broń jednoręczną dodajemy na końcu:
B_SetFightSkill(hero,talent,percent);
Teraz po osiągnięciu 30% automatycznie powinniśmy uzyskać wojownika wraz ze zmienionymi animacjami ataku.
Pamiętajmy jednak, że w systemie nauki z nocy kruka istnieje możliwość nauki dwóch broni równocześnie, dlatego musimy wywołać funkcję jeszcze w jednym miejscu, tym razem definiując jaki talent ma podnieść (automatycznie wykrywany jest jedynie talent glowny, którego się uczymy):
B_SetFightSkill(hero,NPC_TALENT_2H,percent);
Całość odpowiedzialna za naukę broni jednoręcznej powinna wyglądać mniej więcej tak:
if(talent == NPC_TALENT_1H)
{
Npc_SetTalentValue(hero, NPC_TALENT_1H, Npc_GetTalentValue(hero, NPC_TALENT_1H)+percent);
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= (Npc_GetTalentValue(hero,NPC_TALENT_2H) +30))
{
Npc_SetTalentValue(hero, NPC_TALENT_2H, Npc_GetTalentValue(hero, NPC_TALENT_2H)+percent);
B_SetFightSkill(hero,NPC_TALENT_2H,percent);
PrintScreen ("Trening: posługiwanie się bronią jedno- i dwuręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
}
else
{
PrintScreen ("Trening: posługiwanie się bronią jednoręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
};
B_SetFightSkill(hero,talent,percent);
return TRUE;
};
Analogicznie postępujemy z pozostałymi rodzajami broni. W efekcie cała funkcja powinna wyglądać tak:
func int B_TeachFightTalentPercent(var C_Npc slf,var C_Npc oth,var int talent,var int percent,var int teacherMAX)
{
var string concatText;
var int kosten;
var int realHitChance;
kosten = B_GetLearnCostTalent(oth,talent,1) * percent;
if((talent != NPC_TALENT_1H) && (talent != NPC_TALENT_2H) && (talent != NPC_TALENT_BOW) && (talent != NPC_TALENT_CROSSBOW))
{
Print("*** Błąd: Zły parametr ***");
return FALSE;
};
if(talent == NPC_TALENT_1H)
{
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_1H);
}
else if(talent == NPC_TALENT_2H)
{
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_2H);
}
else if(talent == NPC_TALENT_BOW)
{
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_BOW);
}
else if(talent == NPC_TALENT_CROSSBOW)
{
realHitChance = Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW);
};
if(realHitChance >= teacherMAX)
{
concatText = ConcatStrings("Maksimum dla tego nauczyciela wynosi ",IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNYOUREBETTER");
return FALSE;
};
if((realHitChance + percent) > teacherMAX)
{
concatText = ConcatStrings("Maksimum dla tego nauczyciela wynosi ",IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNOVERPERSONALMAX");
return FALSE;
};
if(oth.lp < kosten)
{
PrintScreen("Za mało punktów umiejętności!",-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNNOPOINTS");
return FALSE;
};
oth.lp = oth.lp - kosten;
if(talent == NPC_TALENT_1H)
{
Npc_SetTalentValue(hero, NPC_TALENT_1H, Npc_GetTalentValue(hero, NPC_TALENT_1H)+percent);
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= (Npc_GetTalentValue(hero,NPC_TALENT_2H) +30))
{
Npc_SetTalentValue(hero, NPC_TALENT_2H, Npc_GetTalentValue(hero, NPC_TALENT_2H)+percent);
PrintScreen("Trening: posługiwanie się bronią jedno- i dwuręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
B_SetFightSkill(hero,NPC_TALENT_2H,percent);
}
else
{
PrintScreen("Trening: posługiwanie się bronią jednoręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
};
B_SetFightSkill(hero,talent,percent);
return TRUE;
};
if(talent == NPC_TALENT_2H)
{
Npc_SetTalentValue(hero, NPC_TALENT_2H, Npc_GetTalentValue(hero, NPC_TALENT_2H)+percent);
if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= (Npc_GetTalentValue(hero,NPC_TALENT_1H) +30))
{
Npc_SetTalentValue(hero, NPC_TALENT_1H, Npc_GetTalentValue(hero, NPC_TALENT_1H)+percent);
B_SetFightSkill(hero,NPC_TALENT_1H,percent);
PrintScreen ("Trening: posługiwanie się bronią dwu- i jednoręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
}
else
{
PrintScreen ("Trening: posługiwanie się bronią dwuręczną", -1,10,"FONT_OLD_20_WHITE.TGA",2);
};
B_SetFightSkill(hero,talent,percent);
return TRUE;
};
if(talent == NPC_TALENT_BOW)
{
Npc_SetTalentValue(hero, NPC_TALENT_BOW, Npc_GetTalentValue(hero, NPC_TALENT_BOW)+percent);
if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= (Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) +30))
{
Npc_SetTalentValue(hero, NPC_TALENT_CROSSBOW, Npc_GetTalentValue(hero, NPC_TALENT_CROSSBOW)+percent);
PrintScreen ("Trening: posługiwanie się łukiem i kuszą", -1,10,"FONT_OLD_20_WHITE.TGA",2);
B_SetFightSkill(hero,NPC_TALENT_CROSSBOW,percent);
}
else
{
PrintScreen ("Trening: posługiwanie się łukiem", -1,10,"FONT_OLD_20_WHITE.TGA",2);
};
B_SetFightSkill(hero,talent,percent);
return TRUE;
};
if(talent == NPC_TALENT_CROSSBOW)
{
Npc_SetTalentValue(hero, NPC_TALENT_CROSSBOW, Npc_GetTalentValue(hero, NPC_TALENT_CROSSBOW)+percent);
if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= (Npc_GetTalentValue(hero,NPC_TALENT_BOW) +30))
{
Npc_SetTalentValue(hero, NPC_TALENT_BOW, Npc_GetTalentValue(hero, NPC_TALENT_BOW)+percent);
B_SetFightSkill(hero,NPC_TALENT_BOW,percent);
PrintScreen ("Trening: posługiwanie się kuszą i łukiem", -1,10,"FONT_OLD_20_WHITE.TGA",2);
}
else
{
PrintScreen ("Trening: posługiwanie się kuszą", -1,10,"FONT_OLD_20_WHITE.TGA",2);
};
B_SetFightSkill(hero,talent,percent);
return TRUE;
};
};
Ostatnią funkcję, którą przeniesiemy z Nocy Kruka będzie funkcja odpowiedzialna za obliczanie kosztu punktów nauki. Funkcja ta nosi nazwę: B_GetLearnCostTalent
W Nocy Kruka jest ona bardzo rozbudowana, gdyż oblicza koszt wszystkich umiejętności. Nas interesuje jedynie fragment odpowiedzialny za bronie. Oryginalna funkcja korzysta z hitchance i aivarów, więc tak jak poprzednio używamy zamiast tego funkcji z g1, Npc_GetTalentValue
Nie ma sensu tłumaczyć drugi raz tego samego, zaprezentuje więc od razu całość:
func int B_GetLearnCostTalent(var C_Npc oth,var int talent,var int skill)
{
var int kosten;
kosten = 0;
if(talent == NPC_TALENT_1H)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 90)
{
kosten = 4;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 60)
{
kosten = 3;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_1H) >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
kosten = kosten * skill;
};
if(talent == NPC_TALENT_2H)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 90)
{
kosten = 4;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 60)
{
kosten = 3;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_2H) >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
kosten = kosten * skill;
};
if(talent == NPC_TALENT_BOW)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 90)
{
kosten = 4;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 60)
{
kosten = 3;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_BOW) >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
kosten = kosten * skill;
};
if(talent == NPC_TALENT_CROSSBOW)
{
if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 90)
{
kosten = 4;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 60)
{
kosten = 3;
}
else if(Npc_GetTalentValue(hero,NPC_TALENT_CROSSBOW) >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
kosten = kosten * skill;
};
return kosten;
};
Wszystko powinno być jasne. Największa część pracy za nami. Teraz pozostało tylko parę szczegołów.
Musimy zmienić nazwy stopni władania bronią. Gothic 1 miał domyślnie ustawione dla wszystkich czterech rodzaji broni takie same poziomy brak, adept, mistrz. W g2 wygląda to inaczej. Otwieramy więc plik story\text.d i odnajdujemy linijkę: const string TXT_TALENTS_SKILLS[12]
Poniżej powinniśmy zobaczyć:
"",
"brak|adept|mistrz",
"brak|adept|mistrz",
"brak|adept|mistrz",
"brak|adept|mistrz",
Pierwszą pozycję zostawiamy pustą, a kolejne zmieniamy na:
"",
"Zielony|Wojownik|Mistrz",
"Zielony|Wojownik|Mistrz",
"Zielony|Łucznik|Mistrz",
"Zielony|Kusznik|Mistrz",
Pozostaje nam jeszcze ustawić poziom startowy umiejętności na 10% wzorem G2NK. W tym celu otwieramy plik Story\NPC\PC_Hero.d
Do instancji naszego bohatera (pierwsza z góry) dodajemy następujące linijki:
Npc_SetTalentValue(self, NPC_TALENT_1H,10);
Npc_SetTalentValue(self, NPC_TALENT_2H,10);
Npc_SetTalentValue(self, NPC_TALENT_BOW,10);
Npc_SetTalentValue(self, NPC_TALENT_CROSSBOW,10);
To właściwie tyle. Podam jeszcze przykład jak powinien wyglądać skrypt dialogu u trenera, wykorzystujący system nauki jaki zaimplementowaliśmy. Polecam wykorzystać do tego character helpera, by szybko przekonać się czy wszystko gra.
Otwieramy plik AI\Test_Scripts\CharacterHelper.d i wklejamy poniższy kod.
instance CH_Training_NotRCombat(C_Info)
{
npc = ch;
condition = CH_Training_NotRCombat_Condition;
information = CH_Training_NotRCombat_Info;
important = 0;
permanent = 1;
description = "TALENT: Walka Notr";
};
func int CH_Training_NotRCombat_Condition()
{
return TRUE;
};
func void CH_Training_NotRCombat_Info()
{
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,"Wróć",CH_Training_NotRCombat_BACK);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_1H_1()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_1H,1,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_1H_5()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_1H,5,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_2H_1()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_2H,1,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_2H_5()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_2H,5,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_Bow_1()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_BOW,1,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_Bow_5()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_BOW,5,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_Crossbow_1()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_CROSSBOW,1,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_Crossbow_5()
{
B_TeachFightTalentPercent(self,other,NPC_TALENT_CROSSBOW,5,100);
Info_ClearChoices(CH_Training_NotRCombat);
Info_AddChoice(CH_Training_NotRCombat,Dialog_Back,CH_Training_NotRCombat_Back);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_1H,1),0),CH_Training_NotRCombat_1H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń jednoręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_1H,5),0),CH_Training_NotRCombat_1H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +1",B_GetLearnCostTalent(other,NPC_TALENT_2H,1),0),CH_Training_NotRCombat_2H_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Broń dwuręczna +5",B_GetLearnCostTalent(other,NPC_TALENT_2H,5),0),CH_Training_NotRCombat_2H_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +1",B_GetLearnCostTalent(other,NPC_TALENT_BOW,1),0),CH_Training_NotRCombat_Bow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Łuki +5",B_GetLearnCostTalent(other,NPC_TALENT_BOW,5),0),CH_Training_NotRCombat_Bow_5);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +1",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,1),0),CH_Training_NotRCombat_Crossbow_1);
Info_AddChoice(CH_Training_NotRCombat,B_BuildLearnString("Kusze +5",B_GetLearnCostTalent(other,NPC_TALENT_CROSSBOW,5),0),CH_Training_NotRCombat_Crossbow_5);
};
func void CH_Training_NotRCombat_BACK()
{
Info_ClearChoices(CH_Training_NotRCombat);
};
Następnie kompilujemy skrypty, odpalamy nową grę, włączamy konsole i przyzywamy character helpera komendą: "insert CH".
Ustawiamy sobie jeszcze Punkty Nauki (wpisujemy edit abilities w konsoli, a potem lp = 60), po czym rozmawiamy z przyzwaną postacią. Wybieramy dialog "TALENT: Walka Notr" i sprawdzamy jak działa system walki, który zaimplementowaliśmy.
2. Część druga - atrybuty
Część pierwsza za nami, teraz pora na część drugą, czyli przeniesienie systemu nauki atrybutów. Interesuje nas funkcja B_TeachAttributePoints, w nocy kruka wygląda ona tak:
func int B_TeachAttributePoints(var C_Npc slf,var C_Npc oth,var int attrib,var int points,var int teacherMAX)
{
var string concatText;
var int kosten;
var int realAttribute;
kosten = B_GetLearnCostAttribute(oth,attrib) * points;
if((attrib != ATR_STRENGTH) && (attrib != ATR_DEXTERITY) && (attrib != ATR_MANA_MAX))
{
Print("*** Błąd: Zły parametr ***");
return FALSE;
};
if(attrib == ATR_STRENGTH)
{
realAttribute = oth.attribute[ATR_STRENGTH];
}
else if(attrib == ATR_DEXTERITY)
{
realAttribute = oth.attribute[ATR_DEXTERITY];
}
else if(attrib == ATR_MANA_MAX)
{
realAttribute = oth.attribute[ATR_MANA_MAX];
};
if(realAttribute >= teacherMAX)
{
concatText = ConcatStrings(PRINT_NoLearnOverPersonalMAX,IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNYOUREBETTER");
return FALSE;
};
if((realAttribute + points) > teacherMAX)
{
concatText = ConcatStrings(PRINT_NoLearnOverPersonalMAX,IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNOVERPERSONALMAX");
return FALSE;
};
if(oth.lp < kosten)
{
PrintScreen(PRINT_NotEnoughLP,-1,-1,FONT_Screen,2);
B_Say(slf,oth,"$NOLEARNNOPOINTS");
return FALSE;
};
oth.lp = oth.lp - kosten;
B_RaiseAttribute(oth,attrib,points);
return TRUE;
};
Kopiujemy ją i umieszczamy np. w story\b\b_buyattributepoints.d
Nie musimy tutaj za dużo zmieniać, interesują nas stałe, odpowiadające za wyswietlany tekst oraz wywołanie funkcji B_RaiseAttribute.
Ta co prawda wystepuje już w G1, jednak rózni się nieco od tej z g2, dlatego najlepiej będzie jesli zamiast ją zastępowac, bądź edytować dodamy drugą o lekko zmienionej nazwie, powiedzmy B_RaiseAttribute_Gothic2
Zmieńmy więc jej nazwę w funkcji b_buyattributepoints:
func int B_TeachAttributePoints(var C_Npc slf,var C_Npc oth,var int attrib,var int points,var int teacherMAX)
{
var string concatText;
var int kosten;
var int realAttribute;
kosten = B_GetLearnCostAttribute(oth,attrib) * points;
if((attrib != ATR_STRENGTH) && (attrib != ATR_DEXTERITY) && (attrib != ATR_MANA_MAX))
{
Print("*** Błąd: Zły parametr ***");
return FALSE;
};
if(attrib == ATR_STRENGTH)
{
realAttribute = oth.attribute[ATR_STRENGTH];
}
else if(attrib == ATR_DEXTERITY)
{
realAttribute = oth.attribute[ATR_DEXTERITY];
}
else if(attrib == ATR_MANA_MAX)
{
realAttribute = oth.attribute[ATR_MANA_MAX];
};
if(realAttribute >= teacherMAX)
{
concatText = ConcatStrings("Maksimum dla tego nauczyciela wynosi ",IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNYOUREBETTER");
return FALSE;
};
if((realAttribute + points) > teacherMAX)
{
concatText = ConcatStrings("Maksimum dla tego nauczyciela wynosi ",IntToString(teacherMAX));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNOVERPERSONALMAX");
return FALSE;
};
if(oth.lp < kosten)
{
PrintScreen("Za mało Punktów Nauki!",-1,-1,"FONT_OLD_20_WHITE.TGA",2);
B_Say(slf,oth,"$NOLEARNNOPOINTS");
return FALSE;
};
oth.lp = oth.lp - kosten;
B_RaiseAttribute_Gothic2(oth,attrib,points);
return TRUE;
};
I przejdzmy do funkcji B_RaiseAttribute.
func void B_RaiseAttribute(var C_Npc oth,var int attrib,var int points)
{
var string concatText;
if(attrib == ATR_STRENGTH)
{
oth.attribute[ATR_STRENGTH] = oth.attribute[ATR_STRENGTH] + points;
concatText = ConcatStrings(PRINT_LearnSTR,IntToString(points));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
};
if(attrib == ATR_DEXTERITY)
{
oth.attribute[ATR_DEXTERITY] = oth.attribute[ATR_DEXTERITY] + points;
if((oth.attribute[ATR_DEXTERITY] >= 90) && (Npc_GetTalentSkill(oth,NPC_TALENT_ACROBAT) == 0))
{
Npc_SetTalentSkill(oth,NPC_TALENT_ACROBAT,1);
PrintScreen(PRINT_Addon_AcrobatBonus,-1,55,FONT_Screen,2);
};
concatText = ConcatStrings(PRINT_LearnDEX,IntToString(points));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
};
if(attrib == ATR_MANA_MAX)
{
oth.attribute[ATR_MANA_MAX] = oth.attribute[ATR_MANA_MAX] + points;
concatText = ConcatStrings(PRINT_LearnMANA_MAX,IntToString(points));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
};
if(attrib == ATR_HITPOINTS_MAX)
{
oth.attribute[ATR_HITPOINTS_MAX] = oth.attribute[ATR_HITPOINTS_MAX] + points;
concatText = ConcatStrings(PRINT_Learnhitpoints_MAX,IntToString(points));
PrintScreen(concatText,-1,-1,FONT_Screen,2);
};
B_RaiseRealAttributeLearnCounter(oth,attrib,points);
};
Tu również zmieniamy jej nazwę na B_RaiseAttribute_Gothic2. Widzimy też, że wywoluje ona kolejna funkcje B_raiserealattributelearncounter.
Służy ona do liczenia prawdziwych wartości atrybutów (bez bonusów), jedak z racji tego, że opiera się ona na aivarach z g2, my z niej zrezygnujemy i będziemy opierać się po prostu na obecnych statystykach gracza.
Niestety rozwiązanie ten ma tę wadę, że w przypadku, gdy gracz założony będzie mieć przedmiot podnoszący atrybut, gra nie będzie w stanie rozróżnić, jaką wartość ma tenże atrybut "na czysto" (bez dodatkowego bonusu), co będzie skutkować w obliczaniu progów wliczając w nie dodatkowy bonus. Jeśli bardzo chcemy problem ten można oczywiście ominąć, np. wprowadzając własne zmienne i licząc na nich osobno aytrubuty przy nauce, ale to już zostawiam chętnym do własnej realizacji.
My nie będziemy sobie tutaj tego komplikować i z poprawiania tej funkcji po prostu zrezygnujemy.
Usuwamy więc (bądź zakomentowujemy) wywolanie funkcji B_RaiseRealAttributeLearnCounter. Tradycyjnie znowu też poprawiamy zmienne odpowiedzialne za tekst.
Usuwamy też warunek odpowiedzialny za automatyczną naukę akrobatyki po osiągnięciu 90 zręczności (chyba, że chcemy by tak to zostało) i otrzymujemy:
func void B_RaiseAttribute_Gothic2(var C_Npc oth,var int attrib,var int points)
{
var string concatText;
if(attrib == ATR_STRENGTH)
{
oth.attribute[ATR_STRENGTH] = oth.attribute[ATR_STRENGTH] + points;
concatText = ConcatStrings(NAME_RaiseStrength,IntToString(points));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",4);
};
if(attrib == ATR_DEXTERITY)
{
oth.attribute[ATR_DEXTERITY] = oth.attribute[ATR_DEXTERITY] + points;
concatText = ConcatStrings(NAME_RaiseDexterity,IntToString(points));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",4);
};
if(attrib == ATR_MANA_MAX)
{
oth.attribute[ATR_MANA_MAX] = oth.attribute[ATR_MANA_MAX] + points;
concatText = ConcatStrings(NAME_RaiseManaMax,IntToString(points));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",4);
};
if(attrib == ATR_HITPOINTS_MAX)
{
oth.attribute[ATR_HITPOINTS_MAX] = oth.attribute[ATR_HITPOINTS_MAX] + points;
concatText = ConcatStrings(NAME_RaiseHealthMax,IntToString(points));
PrintScreen(concatText,-1,-1,"FONT_OLD_20_WHITE.TGA",4);
};
//B_RaiseRealAttributeLearnCounter(oth,attrib,points);
};
Następnie dodajemy funkcję liczącą koszt Pn atrybutów: B_GetLearnCostAttribute
func int B_GetLearnCostAttribute(var C_Npc oth,var int attribut)
{
var int kosten;
kosten = 0;
if(attribut == ATR_STRENGTH)
{
if(oth.aivar[REAL_STRENGTH] >= 120)
{
kosten = 5;
}
else if(oth.aivar[REAL_STRENGTH] >= 90)
{
kosten = 4;
}
else if(oth.aivar[REAL_STRENGTH] >= 60)
{
kosten = 3;
}
else if(oth.aivar[REAL_STRENGTH] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
if(attribut == ATR_DEXTERITY)
{
if(oth.aivar[REAL_DEXTERITY] >= 120)
{
kosten = 5;
}
else if(oth.aivar[REAL_DEXTERITY] >= 90)
{
kosten = 4;
}
else if(oth.aivar[REAL_DEXTERITY] >= 60)
{
kosten = 3;
}
else if(oth.aivar[REAL_DEXTERITY] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
if(attribut == ATR_MANA_MAX)
{
if(oth.aivar[REAL_MANA_MAX] >= 120)
{
kosten = 5;
}
else if(oth.aivar[REAL_MANA_MAX] >= 90)
{
kosten = 4;
}
else if(oth.aivar[REAL_MANA_MAX] >= 60)
{
kosten = 3;
}
else if(oth.aivar[REAL_MANA_MAX] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
return kosten;
};
W funkcji tej musimy pozmieniac wszystkie linijki zawierające odniesienia do aivarów określających prawdziwą wartość atrybutu, czyli np.
if(oth.aivar[REAL_STRENGTH] >= 120)
zmieniamy na :
if(oth.attribute[ATR_STRENGTH] >= 120)
Po poprawieniu wszystkich linijek powinniśmy mieć taki fragment kodu:
func int B_GetLearnCostAttribute(var C_Npc oth,var int attribut)
{
var int kosten;
kosten = 0;
if(attribut == ATR_STRENGTH)
{
if(oth.attribute[ATR_STRENGTH] >= 120)
{
kosten = 5;
}
else if(oth.attribute[ATR_STRENGTH] >= 90)
{
kosten = 4;
}
else if(oth.attribute[ATR_STRENGTH] >= 60)
{
kosten = 3;
}
else if(oth.attribute[ATR_STRENGTH] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
if(attribut == ATR_DEXTERITY)
{
if(oth.attribute[ATR_DEXTERITY] >= 120)
{
kosten = 5;
}
else if(oth.attribute[ATR_DEXTERITY] >= 90)
{
kosten = 4;
}
else if(oth.attribute[ATR_DEXTERITY] >= 60)
{
kosten = 3;
}
else if(oth.attribute[ATR_DEXTERITY] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
if(attribut == ATR_MANA_MAX)
{
if(oth.attribute[ATR_MANA_MAX] >= 120)
{
kosten = 5;
}
else if(oth.attribute[ATR_MANA_MAX] >= 90)
{
kosten = 4;
}
else if(oth.attribute[ATR_MANA_MAX] >= 60)
{
kosten = 3;
}
else if(oth.attribute[ATR_MANA_MAX] >= 30)
{
kosten = 2;
}
else
{
kosten = 1;
};
};
return kosten;
};
I to w zasadzie wszystko. Podobnie jak poprzednio podam jeszcze przyklad wykorzystanie systemu nauki w dialogu trenera. Znów posłużymy się do tego celu character helperem.
instance CH_Training_NotRAttributes(C_Info)
{
npc = ch;
condition = CH_Training_NotRAttributes_Condition;
information = CH_Training_NotRAttributes_Info;
important = 0;
permanent = 1;
description = "ATRYBUTY: NotR";
};
func int CH_Training_NotRAttributes_Condition()
{
return TRUE;
};
func void CH_Training_NotRAttributes_Info()
{
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_STR_1()
{
B_TeachAttributePoints(self,other,ATR_STRENGTH,1,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_STR_5()
{
B_TeachAttributePoints(self,other,ATR_STRENGTH,5,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_DEX_1()
{
B_TeachAttributePoints(self,other,ATR_DEXTERITY,1,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_DEX_5()
{
B_TeachAttributePoints(self,other,ATR_DEXTERITY,5,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_MAN_1()
{
B_TeachAttributePoints(self,other,ATR_MANA_MAX,1,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_MAN_5()
{
B_TeachAttributePoints(self,other,ATR_MANA_MAX,5,100);
Info_ClearChoices(CH_Training_NotRAttributes);
Info_AddChoice(CH_Training_NotRAttributes,"Wróć",CH_Training_NotRAttributes_BACK);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +1",B_GetLearnCostAttribute(other,ATR_STRENGTH),0),CH_Training_NotRAttributes_STR_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Siła +5",B_GetLearnCostAttribute(other,ATR_STRENGTH) * 5,0),CH_Training_NotRAttributes_STR_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +1",B_GetLearnCostAttribute(other,ATR_DEXTERITY),0),CH_Training_NotRAttributes_DEX_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Zręczność +5",B_GetLearnCostAttribute(other,ATR_DEXTERITY) * 5,0),CH_Training_NotRAttributes_DEX_5);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +1",B_GetLearnCostAttribute(other,ATR_MANA_MAX),0),CH_Training_NotRAttributes_MAN_1);
Info_AddChoice(CH_Training_NotRAttributes,B_BuildLearnString("Mana +5",B_GetLearnCostAttribute(other,ATR_MANA_MAX) * 5,0),CH_Training_NotRAttributes_MAN_5);
};
func void CH_Training_NotRAttributes_BACK()
{
Info_ClearChoices(CH_Training_NotRAttributes);
};