←  Gotowe funkcje

AMXX.pl: Support AMX Mod X i SourceMod

»

Operacje na obrażeniach

  • +
  • -
Fili:P - zdjęcie Fili:P 18.02.2013

W tym poradniku postaram się opisać kilka operacji związanych z obrażeniami.

Wstęp

  • Opis argumentów funkcji RegisterHam
  • Argument 1 = Ham_TakeDamage: Ten argument oznacza jakie zdarzenie chcemy "złapać". Skoro chcemy przechwycić zadawanie obrażeń, będzie to Ham_TakeDamage.
  • Argument 2 = "player": Drugi argument oznacza klasę u której chcemy "złapać" zdarzenie, w tym wypadku zadanie obrażeń. Ta wartość oznacza gracza.
  • Argument 3 = "fw_TakeDamagePre": Jest to po prostu nazwa funkcji, za pomocą której będziemy "łapać" zdarzenie.
  • Argument 4: W tym wypadku nie podaje jego wartości, ponieważ jego domniemana wartość to 0. 0 oznacza, iż chcemy złapać moment przed zdarzeniem ( daje to możliwość modyfikacji parametrów zdarzenia ). 1 oznacza, że nie mamy już wpływu na przebieg zdarzenia.

2. Opis argumentów zdarzenia Ham_TakeDamage

  • this - identyfikator istoty ( bytu, entu, etc. ), która otrzyma/ła obrażenia
  • idinflictor - identyfikator bytu zadającego obrażenia, zwykle idinflictor == idattacker, jednak kiedy obrażenia zadaje przykładowo granat to w tym argumencie właśnie będzie jego identyfikator.
  • idattacker - identyfikator osoby, istoty, bytu, etc. która atakuje this-a
  • Float:damage - ilość zadawanych obrażeń ( zwróć uwagę iż jest to liczba typu float )
  • damagebits - bit typ/ów zadanych obrażeń ( więcej: http://amxx.pl/topic/28881-damagebits/ )

a) Natychmiastowe zabicie z X broni:
wymagane moduly

hamsandwich
amxmodx

opis
1. Zarejestruj w plugin_init zdarzenie z modułu hamsandwich, Ham_TakeDamage.
RegisterHam( Ham_TakeDamage, "player", "fw_TakeDamagePre" );

2. "Utwórz" funkcję:
public fw_TakeDamagePre( this, idinflictor, idattacker, Float:damage, damagebits )
{
// tu będzie kod
}

3. Wewnątrz funkcji, sprawdź czy this i idattacker żyją:
if( !is_user_alive( idattacker ) || !is_user_alive( this ) )
return HAM_IGNORED;

4. Jeżeli chcesz, aby było natychmiastowe zabicie z granatu dodaj taki warunek:
if( idinflictor == idattacker )
return HAM_IGNORED;
if( !(damagebits & (1<<24) ) )
return HAM_IGNORED;
  • Teraz dodajemy natychmiastowe zabicie z HE:
    SetHamParamFloat( 4, float( get_user_health( this ) + 1) );
    return HAM_HANDLED;
  • Jeżeli chcesz mieć ułamkową szansę na natychmiastowe zabicie przed powyższym kodem dodaj:
    if( !random( X ))
    return HAM_IGNORED;

    Jako x należy podstawić wartość zakresu, np. jeżeli dasz 5, to szansa będzie 1/5.

4. Jeżeli chcesz, aby było natychmiastowe zabicie z innej broni niż granat dodaj warunek:

if( idifnlictor != idattacker )
return HAM_IGNORED;

  • Teraz sprawdźmy z jakiej broni zabito:
    if( get_user_weapon( idattacker ) != Y )
    return HAM_IGNORED;
    Jako Y należy podstawić definicje identyfikatora naszej broni.
  • Teraz dodajemy natychmiastowe zabicie z naszej broni:
    SetHamParamFloat( 4, float( get_user_health( this ) + 1) );
    return HAM_HANDLED;
  • Jeżeli chcesz mieć ułamkową szansę na natychmiastowe zabicie przed powyższym kodem dodaj:
    if( !random( X ))
    return HAM_IGNORED;

    Jako x należy podstawić wartość zakresu, np. jeżeli dasz 5, to szansa będzie 1/5.

B) Odporność ( % ) na obrażenia
opis
1. Zarejestruj w plugin_init zdarzenie z modułu hamsandwich, Ham_TakeDamage.

RegisterHam( Ham_TakeDamage, "player", "fw_TakeDamagePre" );

2. "Utwórz" funkcję:
public fw_TakeDamagePre( this, idinflictor, idattacker, Float:damage, damagebits )
{
// tu będzie kod
}

3. Wewnątrz funkcji, sprawdź czy this i idattacker żyją:
if( !is_user_alive( idattacker ) || !is_user_alive( this ) )
return HAM_IGNORED;

4. Teraz dodajemy już ostatnie 2 linie:
SetHamParamFloat( 4, damage * ( 1.00 - ( O_ILE_PROCENT_ZMNIEJSZAMY_OBRAZENIA / 100 ) ) );
return HAM_HANDLED;

Chyba nie muszę tłumaczyć co podstawiamy za O_ILE_PROCENT_ZMNIEJSZAMY_OBRAZENIA.

c) Obrażenia większe o %
opis
1. Zarejestruj w plugin_init zdarzenie z modułu hamsandwich, Ham_TakeDamage.
RegisterHam( Ham_TakeDamage, "player", "fw_TakeDamagePre" );

2. "Utwórz" funkcję:
public fw_TakeDamagePre( this, idinflictor, idattacker, Float:damage, damagebits )
{
// tu będzie kod
}

3. Wewnątrz funkcji, sprawdź czy this i idattacker żyją:
if( !is_user_alive( idattacker ) || !is_user_alive( this ) )
return HAM_IGNORED;

4. Teraz dodajemy już ostatnie 2 linie:
SetHamParamFloat( 4, damage * ( 1.00 + ( O_ILE_PROCENT_ZWIĘKSZAMY_OBRAZENIA / 100 ) ) );
return HAM_HANDLED;

Chyba nie muszę tłumaczyć co podstawiamy za O_ILE_PROCENT_ZWIĘKSZAMY_OBRAZENIA.

d) Szansa na pełną absorpcję obrażeń
opis
1. Zarejestruj w plugin_init zdarzenie z modułu hamsandwich, Ham_TakeDamage.
RegisterHam( Ham_TakeDamage, "player", "fw_TakeDamagePre" );

2. "Utwórz" funkcję:
public fw_TakeDamagePre( this, idinflictor, idattacker, Float:damage, damagebits )
{
// tu będzie kod
}

3. Wewnątrz funkcji, sprawdź czy this i idattacker żyją:
if( !is_user_alive( this ) )
return HAM_IGNORED;

4. W poniższym kodzie jako X wstaw szansę na absorpcję ( 1/X ):
if( !random( X ))
return HAM_IGNORED;

5. Teraz dodajemy już ostatnie 2 linie:
return HAM_SUPERCEDE;


Jeżeli macie jeszcze jakieś pomysły na nowe podpunkty dotyczące operacji na obrażeniach, proszę pisać śmiało w komentarzach :)

Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 19.02.2013

SetHamParamFloat( 4, float( get_user_health( this ) + 1) );

To nie zabija natychmiast, a przynajmniej nie w każdym przypadku.
Odpowiedz

  • +
  • -
Fili:P - zdjęcie Fili:P 20.02.2013

SetHamParamFloat( 4, float( get_user_health( this ) + 1) );

To nie zabija natychmiast, a przynajmniej nie w każdym przypadku.

Masz na myśli brak wliczonego współczynniku pancerza ?
Odpowiedz

  • +
  • -
Jak się nazwać - zdjęcie Jak się nazwać 20.02.2013

Wszędzie:
random_num( 1, x ) != 1
-->
!random(x)

SetHamParamFloat( 4, 0.0 );
return HAM_HANDLED;
-->
return HAM_SUPERCODE
Odpowiedz

  • +
  • -
Fili:P - zdjęcie Fili:P 20.02.2013

Po operacjach zwracamy HAM_HANDLED, aby zatwierdzić zmiany.

Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 20.02.2013

Ktoś się chyba pomylił i źle podpowiedział

if( !random( X ))
return HAM_IGNORED;

na
if(random( X ))
return HAM_IGNORED;

dopiero teraz będzie 1/x szansy na brak obrażeń.

Po operacjach zwracamy HAM_HANDLED, aby zatwierdzić zmiany.

Jeśli zmieniasz obrażenia, czy coś tam jeszcze, to wtedy piszesz HAM_HANDLED, ale skoro ma blokować całe obrażenia, to należy zwrócić HAM_SUPERCODE, bez żadnego zmieniania obrażeń na 0.

SetHamParamFloat( 4, float( get_user_health( this ) + 1) );

To nie zabija natychmiast, a przynajmniej nie w każdym przypadku.

Masz na myśli brak wliczonego współczynniku pancerza ?

Tak.
Odpowiedz

  • +
  • -
Jak się nazwać - zdjęcie Jak się nazwać 20.02.2013

Po operacjach zwracamy HAM_HANDLED, aby zatwierdzić zmiany.

Niby tak napisał, ale z moich testów wynika że ham_ignored też modyfikuje obrażenia, co do supercode to właśnie o to mi chodziło, żeby nie zmieniać obrażeń na 0.0 tylko po prostu zablokować
Odpowiedz

  • +
  • -
dasiek - zdjęcie dasiek 20.02.2013

Automatyczna wiadomość


Ten temat został przeniesiony z forum

Scripting AMXXTutoriale

do

Scripting AMXXGotowe funkcje
Odpowiedz

Gość_21977_* 20.02.2013

Dokładnie, w momencie, gdy chcemy absorbować całkowicie obrażenia, to blokujemy wywołanie funkcji poprzez zwrócenie HAM_SUPERCEDE.
Zmiana obrażeń na 0, a następnie zwrócenie HAM_HANDLED, nie zada, co prawda, obrażeń, ale nie zablokuje eventu obrażeń.
Innymi słowy, gracz dalej będzie odpychany (pomimo pozornej absorpcji obrażeń), a event TakeDamage Post zostanie wykonany.
Jeśli będę dodawał np. amunicję za każde trafienie wroga, zostanie ona dodana pomimo właśnie pozornej absorpcji obrażeń.
Dlatego też, w przypadku chęci absorpcji obrażeń, należy zwrócić HAM_SUPERCEDE, by całkowicie zablokować event.

!random(x) ma faktycznie 1/x procent szans na wywołanie, jednak w użytej przez autora tematu formie, ma to być
szansa na zablokowanie wydarzenia, z powodu stojącej za funkcją pseudo-losową, instrukcji return HAM_IGNORED.

Jeśli chodzi zaś o natychmiastowe zabicie gracza, to zdejmijmy mu kamizelkę przed wywołaniem operacji, by mieć pewność, że nie uratuje ona mu życia:
cs_set_user_armor(id, 0, CS_ARMOR_NONE);

Użytkownik Benio101 edytował ten post 20.02.2013 21:18
lit., drobne stylistyczne
Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 20.02.2013

Jeśli chodzi zaś o natychmiastowe zabicie gracza, to zdejmijmy mu armor przed wywołaniem operacji, by mieć pewność, że nie uratuje ona mu życia:

cs_set_user_armor(id, 0, CS_ARMOR_NONE);

To też nie zawsze może pomóc, najlepiej jest gdzieś po zadaniu obrażeń sprawdzać, czy dany gracz miał być od razu zabity i jeśli tak, to wtedy gdy jest dalej żywy, zadać jeszcze raz takie same obrażenia jak wcześniej (lub jakiekolwiek obrażenia) i tak oczywiście aż dany gracz nie zostanie zabity.

Jeszcze co do zmieniania obrażeń przed ich zadaniem, to niestety, ale takie coś może powodować tzw. bug z 0hp, bo gdy gracz będzie miał hp pomiędzy 0, a 1 (czyli wartość zmiennoprzecinkową 0 < hp < 1), to wtedy ekran przekręca się na bok i wyświetla 0hp. Chyba najlepszym sposobem jest po prostu sprawdzanie po zadaniu obrażeń ile hp ma gracz oraz czy żyje, jeśli hp ma mniej od 1 i żyje, to zadać mu jakieś tam obrażenia lub po prostu zabić go za pomocą "kanapki".
Odpowiedz

Gość_21977_* 20.02.2013

W przypadku stosowania systemu żyć bądź natychmiastowego autoodradzania (typu DM) dla rozgrywki na rundy, gracz natychmiast straci wszystkie swoje życia, zamiast tylko jednego.
btw. Teraz mnie jeszcze zaciekawiłeś, w jakim wypadku zdjęcie kamizelki będzie niewystarczające (uzględniając to +1 dodawane do liczby odejmowanych punktów życia gracza)?
Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 20.02.2013

W przypadku stosowania systemu żyć bądź natychmiastowego autoodradzania (typu DM) dla rozgrywki na rundy, gracz natychmiast straci wszystkie swoje życia, zamiast tylko jednego.

Do czego to się odnosi?

btw. Teraz mnie jeszcze zaciekawiłeś, w jakim wypadku zdjęcie kamizelki będzie niewystarczające (uzględniając to +1 dodawane do liczby odejmowanych punktów życia gracza)?

Z moich testów wynika (czyli na podstawie logów), że nie zawsze pomaga zabranie kamizelki, czemu tak jest, nie wiem, ale po prostu w logach miałem coś takiego, że graczowi zostawało trochę hp (liczę sytuację, gdy gracz miał hp >= 1) i był żywy. Testy robiłem właśnie dlatego, bo szukałem przyczyny bugu z 0hp.
sebul (20.02.2013 23:18):
Choć co do kamizelki, to pewny nie jestem, nie używałem "cs_set_user_armor", do tego nie jestem pewien, kiedy dokładnie wywaliłem z kodu usuwanie kamizelki, na pewno już po tym jak zacząłem zapisywać do logów natychmiastowe zabicie.
Odpowiedz

Gość_21977_* 20.02.2013

Do czego to się odnosi?

Do tego:

najlepiej jest gdzieś po zadaniu obrażeń sprawdzać, czy dany gracz miał być od razu zabity i jeśli tak, to wtedy gdy jest dalej żywy, zadać jeszcze raz takie same obrażenia jak wcześniej (lub jakiekolwiek obrażenia) i tak oczywiście aż dany gracz nie zostanie zabity.

Za to dobicie na to kilka HP, będzie dobrym pomysłem (nie sądziłem, że właśnie może gracz takie "zabicie" przeżyć), szczerze powiedziawszy, nigdy z tej opcji jeszcze nie korzystałem.
Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 20.02.2013

Do czego to się odnosi?

Do tego:

najlepiej jest gdzieś po zadaniu obrażeń sprawdzać, czy dany gracz miał być od razu zabity i jeśli tak, to wtedy gdy jest dalej żywy, zadać jeszcze raz takie same obrażenia jak wcześniej (lub jakiekolwiek obrażenia) i tak oczywiście aż dany gracz nie zostanie zabity.

Za to dobicie na to kilka HP, będzie dobrym pomysłem (nie sądziłem, że właśnie może gracz takie "zabicie" przeżyć), szczerze powiedziawszy, nigdy z tej opcji jeszcze nie korzystałem.

Skoro gracz jest żywy (nawet mając mniej niż 1 hp, ale więcej niż 0), to raczej nie jest to liczone jako zabójstwo i nic nie będzie zabierane kilka razy, nawet zadając jeszcze raz 100 (lub więcej) dmg. W każdym razie, natychmiastowe zabijanie można też robić bez usuwania kamizelki, wystarczy dobrze to napisać.
Odpowiedz

  • +
  • -
MarWit - zdjęcie MarWit 21.02.2013

Jeśli chodzi zaś o natychmiastowe zabicie gracza, to zdejmijmy mu kamizelkę przed wywołaniem operacji, by mieć pewność, że nie uratuje ona mu życia:
cs_set_user_armor(id, 0, CS_ARMOR_NONE);


Nie szybciej użyć po prostu ( z zablokowaniem obrażeń przez HAM_SUPERCEDE ) ?:
ExecuteHam( Ham_Killed, ... );
Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 21.02.2013

Jeśli chodzi zaś o natychmiastowe zabicie gracza, to zdejmijmy mu kamizelkę przed wywołaniem operacji, by mieć pewność, że nie uratuje ona mu życia:
cs_set_user_armor(id, 0, CS_ARMOR_NONE);


Nie szybciej użyć po prostu ( z zablokowaniem obrażeń przez HAM_SUPERCEDE ) ?:
ExecuteHam( Ham_Killed, ... );

Też można (ale lepiej używać "ExecuteHamB" w tym przypadku, przynajmniej tak mi się wydaję), zresztą sam zastanawiałem się nad tym, żeby z tego korzystać, ale tutaj nie da się ustawić inflictora (tak jak to w przypadku Ham_TakeDamage), czyli bytu co zabija (zadaje obrażenia), chyba że ja o czymś nie wiem.
Odpowiedz

Gość_21977_* 21.02.2013

ExecuteHam nie będzie wykryte przez silnik, dlatego wersja z B będzie lepsza, do tego trzeba sprawdzić, czy gracz nie ma godmoda.
Odpowiedz

  • +
  • -
Jak się nazwać - zdjęcie Jak się nazwać 21.02.2013

do tego trzeba sprawdzić, czy gracz nie ma godmoda.

Z tego co wiem, to przy godmode TakeDamagePre nie zostanie wykonane (tak mi się wydaje) więc to trzeba byłoby sprawdzić (chyba że już sprawdzałeś, jak tak to zwracam honor)
Odpowiedz

  • +
  • -
MarWit - zdjęcie MarWit 21.02.2013

ale tutaj nie da się ustawić inflictora

"inflictorem" jest broń aktualnie trzymana przez gracza, lub jeżeli byt nie jest graczem to "inflictorem" jest właśnie byt atakujący.
Odpowiedz

  • +
  • -
sebul - zdjęcie sebul 21.02.2013

ale tutaj nie da się ustawić inflictora

"inflictorem" jest broń aktualnie trzymana przez gracza, lub jeżeli byt nie jest graczem to "inflictorem" jest właśnie byt atakujący.

O tym to ja wiem, chodzi mi po prostu o ustawienie "inflictora" bez względu na to, kto jest atakującym, co zresztą pisałem, że chodzi mi o coś takiego jak można ustawić przy "Ham_TakeDamage", czyli jeszcze dokładniej, w "Ham_Killed" nie da się ustawić gracza jako atakującego, a jakiś byt jako to coś, co zadaje obrażenia.
Odpowiedz