[G1][GFA] Kolizja strzał z NPC 4213 4

O temacie

Autor DerDrache

Zaczęty 1.12.2021 roku

Wyświetleń 4213

Odpowiedzi 4

DerDrache

DerDrache

Użytkownicy
posty18
Propsy2
ProfesjaProgramista
  • Użytkownicy
Hej, to znowu ja.
Jak pewnie wiecie, strzała/pocisk magiczny wystrzelona/y przez NPC ignoruje wszystkich NPC po co drodze do celu, chyba że ten NPC to potwór lub ork. Potrzebuję to zmienić, a dokladnie, kiedy NPC który strzela jest orkiem i ten NPC po drodze jest rownież orkiem, to strzała przez niego przelatuje :)

Szukajac rozwiązan, trafiłem na teoretycznie rozwiązanie niemal gotowe, idealne w Gothic Free Aim. Otóż hookują oni tam oCAIArrow::CanThisCollideWith(zCVob*) przed samym koncem funkcji, sprawdzając kilka warunków i co najważniejsze dla mnie, pobierając strzelającego z oCAIArrow, a "ofiarę" oczywiście z parametru zCVob.
(https://github.com/szapp/GothicFreeAim/blob/master/_work/data/Scripts/Content/GFA/_intern/collision.d #554, GFA_ExtendCollisionCheck())

Niestety próby zhookowania tego u mnie całkowicie nie wypalają. Po zhookowaniu tego w miejscu w którym zrobili to tworcy GFA, nic sie nie dzieje, funkcja sie nie wywołuje nigdy, po prostu nic się nie dzieje. Po shookowaniu tego na początku(co ogólnie nie ma raczej sensu, bo chce zmodyfikowac wartosc zwracana) nastepuje crash w momencie próby edycji voblisty i wpisywania czegoś do rejestru.
Wygląda to mniej więcej tak (wersja z hookiem jak w GFA):

func void EVT_oCAIArrow_CanThisCollideWith()
{
var int collidingVobPointer; collidingVobPointer = MEM_ReadInt(ESP + 8);
var int arrowAI; arrowAI = MEMINT_SwitchG1G2(ESI, EDI);

if ((collidingVobPointer == 0) ||
(arrowAI == 0) ||
(!Hlp_Is_oCNpc(collidingVobPointer)))
{
return;
};

var C_Npc shooter; shooter = _^(MEM_ReadInt(arrowAI + oCAIArrow_origin_offset));
var C_NPC collidingNpc; collidingNpc = MEM_PtrToInst(collidingVobPointer);

if(C_NpcIsOrc(shooter) &&
   C_NpcIsOrc(collidingNpc))
{
// var int ignoreVobList; ignoreVobList = MEM_ReadInt(arrowAI + oCAIArrowBase_ignoreVobList_offset);
        // List_AddFront(ignoreVobList, collidingVobPointer);

        // // // Increase reference counter, otherwise NPC/vob will be deleted on list destruction!
        // var zCVob vob; vob = _^(collidingVobPointer);
        // vob._zCObject_refCtr += 1;

        // // Set return value of collision check to false
        //ECX = 0;
} else
{
PrintScreen("Something differrent than orc hit", 15,54,"FONT_OLD_10_WHITE.TGA",_TIME_MESSAGE_GIVEN);
};
};

func void Hook_oCAIArrow_CanThisCollideWith()
{
HookEngineF(oCAIArrow_CanThisCollideWithAddr_positive, 6, EVT_oCAIArrow_CanThisCollideWith);
};

Wszystko rozbija się o pobranie z odpowiednich miejsc shootera i victima. Jak rozumiem, ten vob który przelatuje jest na ESP + 8, poniewaz jedno słowo juz zostalo dolozone na stos, i to by sie zgadzało z liczbą pushy:


(call zdejmuje tych 5 pushy/argumetow koło siebie)

Natomiast nie do konca rozumiem dlaczego oCAIArrow jest odczytywane z ESI zamiast ESP + 4(wszak jest to ukryty argument).

Ogółem cały koncept reprezentowania rejestrów w LeGo/Ikarusie jest dość niezrozumiały dla mnie.
Np. tutaj (całkiem inna funkcja, ale pewnie niedługo się za to zabiorę)

EDI = DMG_OnDmg(EBP, MEM_ReadInt(dmgDesc + 8), EDI, +dmgDesc, +dmg_IsHit);
powyzszy kawałek kodu powstał na kanwie implementacji onDmg, która będzie mi potrzeban do czego innego: EDI jako deadalusowa zmienna jest traktowany jako wartość, ale z drugiej strony w przypisaniu collidingVobPointera juz ESP traktujemy jako adres? wuteef?
 
GOTHIC I: AMPLIFIED


bogu9821

bogu9821

Użytkownicy
posty406
Propsy185
ProfesjaProgramista
  • Użytkownicy
"Natomiast nie do konca rozumiem dlaczego oCAIArrow jest odczytywane z ESI zamiast ESP + 4(wszak jest to ukryty argument). "

Kompilator MSVC daje this do ECX, a jak widać, jest ten rejestr potrzebny do wywołania innej funkcji, więc do ESI trafia wartość z ECX.

"EDI jako deadalusowa zmienna jest traktowany jako wartość, ale z drugiej strony w przypisaniu collidingVobPointera juz ESP traktujemy jako adres? wuteef? "

To już kwestia tego jak działa assembler. ESP zawiera adres, a np. ESI, ECX może już zawierać pointer.
Ogólnie polecam trochę assemblera liznąć, jeżeli chcesz rozumieć co jak działa i z czego wynika w hookach w LeGo. One nic nie wymyślają, tylko korzystają znanych rozwiazań, zainplementowanych w opraciu o dziury w pamięci w daedalusie.

Kodu nie sprawdzałem w grze, ale tylko chociaż napiszę.
A jeżeli w GFA to działa dobrze, to pewnie jakiś błąd jednak leży po twojej stronie.
 
while(false) Do();

DerDrache

DerDrache

Użytkownicy
posty18
Propsy2
ProfesjaProgramista
  • Użytkownicy
Cytuj
To już kwestia tego jak działa assembler. ESP zawiera adres, a np. ESI, ECX może już zawierać pointer.
Stack POINTER, więc co za różnica? :D chyba że pomyliłeś się
 
GOTHIC I: AMPLIFIED


DerDrache

DerDrache

Użytkownicy
posty18
Propsy2
ProfesjaProgramista
  • Użytkownicy


Popróbowałem w innych miejscach funkcji OcAIArrow::CanCollideWith(zCVob*) i trafiłem wreszcie w opdowiednie miejsce. Zhookowałem funkcję na 0x6195A4 z przy oldInstr równym 7. I działa, podmieniam wartosc zwracaną w momencie gdy obaj NPC spełniają pewne warunki, cacy, strzała przenika. Jednak gdy sam odpalał łuk i strzelał, nastepuje insta crash gry. Oczywiście top funkcja w backtrace to CanCollideWith. Hook nadpisuje oprócz zwracania wartości, także pierwszą instrukcje z nastepnej etykiety(fioletowy kolor na screenie), więc strzelam, ze przy skoku do tej podemnienionej piewszej instrukcji etykiety(w której miejscu jest juz cos innego) dzieje się cos niespodziewanego.

Jest jakaś opcja żeby to jakoś ominąć, inaczej zrobić? Chyba jest coś takiego jak ReplaceFunc w LeGo, ale nie wiem czy dało by się wsadzić funkcję większą, która poprzesuwałaby offsety dalszego kodu.

Dodam, że problem nie lezy w funkcji dedalusowej, hookowanej. Nawet gdy zhookowałem pustą funkcje, to byl crash przy strzelaniu bezim z łuku.
 
GOTHIC I: AMPLIFIED


DerDrache

DerDrache

Użytkownicy
posty18
Propsy2
ProfesjaProgramista
  • Użytkownicy
Temat do zamknięcia, bardzo łatwo udało się rozwiazać problem Unionem
Dla zainteresowanych kod:

int __fastcall oCAIArrow_CanThisCollideWith(oCAIArrow*, void*, zCVob*);
CInvoke<int(__thiscall*)(oCAIArrow*, zCVob*)> Ivk_oCAIArrow_CanThisCollideWith(oCAIArrow_CanThisCollideWithAddr, oCAIArrow_CanThisCollideWith);

int __fastcall oCAIArrow_CanThisCollideWith(oCAIArrow* _this, void* vtable, zCVob * vob)
{
auto retval = Ivk_oCAIArrow_CanThisCollideWith(_this, vob);
// nasze warunki modyfikujące retval
                return retval;
}
 
GOTHIC I: AMPLIFIED



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