Niewiele osób wie o tym, że kradzież kieszonkowa z pierwszej części egzystuje także w zmodyfikowanym silniku, na którym "chodzi" druga część. (potrzebny Ikarus & Lego lub Union, jednakże tutaj pokazuję jak to osiągnąć tym pierwszym.)
Oczywiście nie liczcie na gotowca. Będziecie musieli trochę pogłówkować i przenieść skrypty AI z G1 (w stylu ostrzegania przez innych npc'tów przed kradzieżą itp.), lub napisać swoje własne. ;)
Na początku musimy mieć możliwość wywołania z silnika metody
oCNpc::OpenSteal(), bo ona robi za nas całą robotę.
Konwencja wywołania tejże metody to __thiscall, więc do jej wywołania będziemy musieli użyć funkcji z ikarusa
CALL__thiscall(var int this, var int adr);
Teraz pokażę wam przykład wywołania funkcji w konwencji wywołania __thiscall, abyście skumali jak się to robi.
Wywołanie w c++ wygląda tak:
player->OpenSteal();
Jak można zauważyć, metoda taka potrzebuje jakiegoś obiektu, by zostać wywołana i właśnie wskaźnik this wskazuje na ten obiekt podczas wywołania.
Tutaj można zobaczyć jak takie wywołanie przykładowej metody (
void __thiscall oCNpc::IdentifyAllMushrooms()) wygląda w ikarusie (wywołanie różni się trochę od tego, czy metoda zwraca wartość i czy ma jakieś argumenty):
Spoiler
//adres do metody
const int oCNpc__IdentifyAllMushrooms = 7639008;
//adres do obiektu gracza
const int oCNpc__player_G2 = 11216516;
//funkcja MEM_ReadInt(var int adr) zamienia adres na wskaźnik.
var int player; player = MEM_ReadInt(oCNpc__player_G2);
CALL__thiscall(player,oCNpc__IdentifyAllMushrooms);
I to ta cała "czarna magia". Możemy wreszcie wracać do sedna.
Wiedzcie, że będziecie musieli sami wywołać metodę
oCNpc::OpenSteal() po następnym kroku.
Następnym krokiem będzie zhookowanie metody
oCAIHuman::StandActions (aby nie musieć wywoływać tego w nieodpowiednim czasie. Metoda ta wykonuje się podczas interakcji gracza.). Bez wchodzenia w szczegóły pozwala nam to na wywołanie naszego kod chwilę przed/po/w trakcie wywoływania kodu z silnika.
Potrzebujemy do tego adresu metody, przynajmniej 5 bajtów do których możemy się "zahaczyć" (tak zwany hook len) i nowej funkcji.
Na szczęście nie musimy szukać sami adresów i lenów.
Znajdziecie je tutaj ->
KlikOtwieramy plik odpowiednim programem i ukazuje się wam takie okno:
Najważniejsze w tej chwili elementy tabeli to: Address, HookLen i name
Szukamy więc (ctrl+f) metody
oCAIHuman::StandActions.Zapamiętujemy hooklen i kopiujemy adres, po czym konwertujemy go z szesnastkowego na system dziesiętny na przykład przy użyciu tej strony (chyba, że ktoś jest taki dobrym matematykiem i to sam obliczy w kilka sekund

) ->
Klik Dobra, mamy już wszystko, więc możemy wkraczać do kolejnego etapu - zhookowanie metody
oCAIHuman::StandActions i wywołanie
oCNpc::OpenSteal().
Deklarujemy sobie kolejne stałe z adresami:
const int oCAIHuman__Walkmode_offset = 356;
const int oCNpc__focusvob_offset = 2476;
Następnie tworzymy sobie nową funkcje, np.
oCAIHuman_StandActions_Hook.
W funkcji tej tworzymy zmienną typu
int i musimy przypisać do niej wartość z klasy
oCAIHuman ze zmiennej, przechowywującej wartość, która mówi jaki styl chodzenia ma
dany npc.Właściwie zmienna ta pełni zdanie
boola, bo przyjmuje tylko wartości
0 lub 1, bo zastosowaliśmy
operator== var int b_IsSneaking; b_IsSneaking = (MEM_ReadInt(ECX+oCAIHuman__Walkmode_offset /*wyliczony rozmiar bajtów do pola (zmiennej) z klasy oCAIHuman*/) == 2);
W konwencji wywołania __thiscall, this znajduje się w rejestrze komputera o nazwie ECX, więc stąd to ECX.
Później sprawdzamy, czy b_IsSneaking wynosi 1 i w klamrach deklarujemy kolejne zmienne, które właściwie są wskaźnikami do gracza i na npc, na którego patrzy.
var int player; player = MEM_ReadInt(oCNpc__player_G2);
var int fcsVob; fcsVob = MEM_ReadInt(player+oCNpc__focusvob_offset/*wyliczony rozmiar bajtów do pola (zmiennej) z klasy oCAIHuman*/);
I teraz trzeba sprawdzić, czy fcsVob nie równa 0, bo jeżeli nie mamy npc zfocusowanego, to wartość wynosi on 0.
Od razu sprawdzimy też, czy jest on npctem, a nie jakimś kufrem czy czymś w tym stylu.
Pozwala nam na to funkcja z ikarusa
Hlp_Is_oCNpc(var int obj). I tutaj w ciele ifa musicie właśnie wywołać metodę
oCNpc::OpenSteal, tak jak pokazałem u góry. Pomocniczno napiszę: this = player.
if(Hlp_Is_oCNpc(fcsVob)) {
//tutaj wywołujesz oCNpc::OpenSteal
};
Na koniec najlepiej stworzyć nową funkcję, która będzie służyła
do inicjowania hooków, którą wywołujemy w init_global po zainicjowania LeGo!.
Ma mieć ona taką strukturę:
func void InitHooks()
{
const int init = 0;
if(!init)
{
//hooki
init = true; // przeciwdziała to tworzeniu nadmiernych obiektów z hookami
};
};
No to hookujemy metodę
oCAIHuman::StandActions z funkcją przez nas stworzoną w ten sposób (oczywiście dajemy to do
InitHooks):
HookEngineF(adresMetody,HookLen,NaszaFunkcja);
UWAGA! Aby kradzież nie kończyła się ciągłą porażką, musisz dać graczowi talent 6 w przedziale od 0 do 100.
Szansa na kradzież polega na sprawdzaniu, czy (pseudo)losowa liczba z przedziału od 0 do 99 jest mniejsza od talentu gracza!Warto jeszcze zaznaczyć, że istnieje taka funkcja jak
G_CanSteal w \Content\Story\G_Functions\G_CanSteal.d, dzięki której możemy dodać dodatkowe warunki potrzebne do kradzieży.
Efekty możemy zauważyć tutaj:
Dziękuję za uwagę i mam nadzieję, że ktoś coś zrozumiał z moich prób tłumaczenia.
