W jaskiniowym dziale znajduje się tabela z doświadczeniem potrzebnym bohaterowi do awansowania na następny poziom (http://h2.heroes.net.pl/mechanika/doswiadczenie). Miałem okazję zerknąć sobie dzisiaj na zdekompilowany kod dwójki (https://github.com/jkoppel/project-ironfist/tree/master/src/raw_decompiled) i naszło mnie na sprawdzenie, jak wygląda formuła na liczenie doświadczenia. Oto ona:
(Ze względu na błąd na forum nie mogę użyć dwóch podkreśleń obok siebie, bo wygląd postu się psuje, więc zamiast nich używam "_._". To samo tyczy się dwóch plusów, więc wstawiam między nimi spację + +).
W grze znajduje się składająca z trzynastu elementów tablica typu _._int16:
_._int16 experienceForLevelTable[] = { 63, 0, 1000, 2000, 3200, 4500, 6000, 7700, 9000, 11000, 13200, 15500, 18500 };
To doświadczenie potrzebne dla poziomów od 1-12 (nie pytajcie, co oznacza "63" dla poziomu 0, też nie wiem). Jak liczona jest reszta? Oto funkcja:
int _._stdcall hero::GetExperience(signed int level) { int result; signed int i; int resultExp; signed int experience; if ( level > 12 ) { i = 13; experience = (signed _._int64)((double)((signed int)experienceForLevelTable[12] - (signed int)experienceForLevelTable[11]) * 1.2); resultExp = experience + experienceForLevelTable[12]; while ( i < level ) { experience = (signed _._int64)((double)experience * 1.2); resultExp += experience; + +i; } result = resultExp; } else { result = experienceForLevelTable[level]; } return result; }
Jest dosyć prosta, ale ją objaśnię. Jeśli następny poziom (parametr level) jest mniejszy niż 13, to funkcja po prostu zwraca wartość z tabeli. W przeciwnym przypadku:
Tworzymy zmienną i o wartości 13 oraz zmienną experience, która wynosi 3600. Mimo tego, że jej wartość jest zawsze taka sama, funkcja liczy ją odejmując od siebie dwunastą wartość z tabeli experienceForLevelTable od wartości jedenastej (czyli 18500 - 15500, czyli 3000) i mnożąc wynik razy 1.2, co zawsze daje 3600. Tak, jest to zupełnie bezsensowne obliczenie, ale kto programistom z NWC zabroni:
i = 13; experience = (signed _._int64)((double)((signed int)experienceForLevelTable[12] - (signed int)experienceForLevelTable[11]) * 1.2);
Następnie tworzona jest zmienna resultExp, a jej wartość to wyliczone wcześniej experience + ostatnia wartość z tabeli experienceForLevelTable (czyli 3600 + 18500):
resultExp = experience + experienceForLevelTable[12];
Następnie rozpoczynamy pętle, która wykonuje się następnyPoziom - 13 razy (czyli jeśli nasz następny poziom to 25, wykonamy ją 12 razy)
while ( i < level )
{
[...]
+ +i;
}
W środku pętli wykonywane są dwa obliczenia:
experience = (signed _._int64)((double)experience * 1.2);
experience = experience * 1.2. Wynik zapisywany jest w zmiennej typu int, więc wszystkie wartości po przecinku zostają ucięte. Następnie zmienna resultExp zostaje zwiększana o aktualnie wykalkulowaną wartość zmiennej experience:
resultExp += experience;
Po całkowitym zakończeniu pętli zwracana jest ostateczna wartość resultExp.
Żeby było jaśniej, wyjaśnię to teraz na przykładzie. Załóżmy, że potrzebujemy wyliczyć doświadczenie potrzebne na 25 poziom.
i = 13;
experience = 3600;
resultExp = 22100;
dopóki i jest mniejsze niż 25:
experience = experience * 1.2 (utnij liczby po przecinku)
resultExp = resultExp + experience
i = i + 1
Wykonanie tego obliczenia dwanaście razy da nam wynik 193 044. Jeśli sprawdzimy tabelkę z doświadczeniem potrzebnym na następny poziom, którą zalinkowałem na początku, to jest to właśnie doświadczenie potrzebne na osiągnięcie 25 poziomu.