[G2NK] Kradzież kieszonkowa z Gothic 1 3235 0

O temacie

Autor bogu9821

Zaczęty 24.06.2019 roku

Wyświetleń 3235

Odpowiedzi 0

bogu9821

bogu9821

Użytkownicy
posty407
Propsy185
ProfesjaProgramista
  • Użytkownicy
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 ->  Klik
Otwieramy plik odpowiednim programem i ukazuje się wam takie okno:
Spoiler
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  :ok:) -> 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:
Spoiler

Dziękuję za uwagę i mam nadzieję, że ktoś coś zrozumiał z moich prób tłumaczenia.  :D
 
while(false) Do();


0 użytkowników i 1 Gość przegląda ten wątek.
0 użytkowników
Do góry