Heute möchte ich euch eine kleine Einleitung in die Python Programmierung mit Hilfe des Metin2 Clienten ein wenig näher bringen.
Aber erstmal ein paar kleinere Informationen zu mir:
Ich studiere aktuell Softwaretechnik an der Universität Stuttgart wobei sich dies mehr auf Java beläuft.
Aber der wichtigste Part der Informatik ist grundlegend nicht eine Sprache perfekt zu beherrschen, sondern sie zu verstehen.
Python scripte ich nun seit knapp 2,5 Jahren und habe auch schon verschiedene, bisher eher kleinere Projekte auf die Beine gestellt.
Ich bin auch noch nicht sehr lange im Metin2 Bereich aktiv, habe mich auch erst neulich auf Epvp registriert.
Nichts desto trotz habe ich mir bereits die Python Scripte im Clienten durchgeschaut, wodurch ich zumindest das Prinzip des Clienten verstanden habe.
Weiter geht es mit dem Tutorial:
Was ist Python überhaupt?
Python ist eine bisher einigermaßen unbekannte Programmiersprache, die 1991 von Guido von Rossum in seinen Ferien entwickelt wurde.
Sie gewann schnell an Beliebtheit und wurde stetig weiterentwickelt, sodass sie heute auch mit anderen großen Sprachen mithalten kann.
Warum benutzt Metin2 überhaupt Python als Schnittstelle?
Python ist eine recht performante Sprache, welche auch ab Python 2.3 effizient in Low-Level Interface Bereichen dank ctypes recht interessant geworden ist.
Auch aufgrund seines stabilen Aufbaus und den vielfältigen Möglichkeiten wird es nicht nur in Metin2, sondern auch oft in anderen größeren Spielen verwendet.
Metin2 benutzt Python 2.2.1, die aktuellste Version ist aber 2.7.3 in der 2.x Version und 3.3.0 in der 3.x Version.
Diese wurde bereits seit 2003 nicht mehr weiterentwickelt und erhält selten irgendwelche neuen Module, wobei diese für den Clienten zum Großteil sowieso uninteressant sind.
Wenn die verwendete Python Version über 9 Jahre alt ist, wäre es dann nicht sinnvoll zu updaten?
Ein wichtiger Punkt ist die Performance, worauf jeder gute Programmierer zu achten hat.
Metin2 ruft selber in den Scripts zwei Funktionen auf, die OnUpdate und OnRender heißen, diese werden in der Binary gecalled und sind somit nicht standardmäßig in Python enthalten.
Diese Funktions werden 20x(OnUpdate) und 60x(OnRender) pro Sekunde aufgerufen.
Dies entspricht einer Delay von 0.05 Sekunden und ~0.017 Sekunden.
D.h. die Performance wird sich nicht sichtbar verbessern, wenn ihr Python auf eine neuere Version updated.
Die Funktionen werden zwar schneller ablaufen, allerdings wird die menschliche Wahrnehmung das nicht merken, da diese bei 16 Bildern pro Sekunde liegt, wovon nichtmal alle verarbeitet werden.
Falls ihr die Python Version updaten wollt, könnt ihr zwar neue Module hinzufügen, die Performance wird davon aber nicht sichtbar beeinträchtigt.
Python hat in den neueren Versionen mit unter anderem mit ctypes starke Low-Level Module hinzugefügt, diese werden dadurch auch für "Hacker" nutzbar.
Auch ist die WSAIoctl Funktion ab Python 2.5 verfügbar, welche recht interessant fürs Packet Editing ist, diese wird bei z.B. bei einem Update ebenfalls ermöglicht.
Solange ihr selber also nicht neue Module braucht und Python updaten möchtet ohne dass ihr überhaupt einen gewissen Zweck verfolgt würde ich euch davon abraten.
An Modulen, die man in Metin2 brauchen könnte kamen bis auf z.B. sqlite3 nicht sonderlich viele Module hinzu.
Falls ihr diese Module trotzdem vermisst, gibt es meist noch von der damaligen Zeit Ersatzlösungen oder von neuerer Zeit Backports der demensprechenden Module.
Die Grundlagen von Python:
Ich setze hier bestimmte Grundlagen von Python vorraus, ihr müsst demnach zumindest den groben Syntax verstehen.
indented block(falsche Einrückung von den Tabs bzw. Leerzeichen), invalid syntax wenn jemand z.B. einen Doppelpunkt vergisst werde ich hier nicht erläutern, diese findet ihr bereits beim "Hello World", also dem absoluten Basic bei jedem Python HowTo.
Ich werde hier Metin2 spezifische Module erläutern und die Logik bzw. diskrete Funktionen(so lautet auch eine der wichtigsten Vorlesungen bei meinem Studium).
Eines der wichtigsten Punkte hierbei sind die Vids.
Dies sind virtuelle IDs welche jedem Spieler/Item/Mob zugewiesen werden um das ganze Spiel überhaupt erst in seiner Form zu ermöglichen.
Diese beinhalten alle möglichen Informationen, die der Spieler über die entsprechende Vid erhalten soll, teilweise sogar mehr als geplant war wie ich selber mitbekommen habe.
Gehen wir einmal davon aus, dass die Vid des gewünschten Monsters 1 sei:
Nun haben wir verschiedene Möglichkeiten um an die Informationen des Monsters zu kommen.
Falls man mit dem chr Modul arbeitet ist es erstmal wichtig, die gewünschte Instanz überhaupt erst einmal anzuwählen.
Dies passiert mit dem Command "chr.SelectInstance(vid)", vid ist eine Variable mit dem Datentyp Integrer, d.h. in unserem Beispiel könnten wir direkt über dem Command vid = 1 definieren.
Damit wir unterscheiden können ob es sich bei der erhaltenen Vid um einen Gegner, Npc, Monster, Spieler oder sonstiges unterscheidet überprüfen wir den Instance Type.
Gliedern wir das ganze etwas auf um etwas Ordnung in unsere Vid-Liste zu bekommen.
Dazu erstellen wir kurzerhand ein assoziatives Datenfeld, das folgendermaßen aussieht:
Wir haben hier einen bestimmten Key, der demnach die Vids in dem Array einem bestimmten Unterarray zuweist.
Diesen finden wir mit dem Command "chr.GetInstanceType(vid)" heraus.
Hierfür müssen wir die Instanz dieses mal nicht selecten.
Schauen wir einmal an was wir bisher so haben:
Okay so weit haben wir unsere Vid bereits ihren dementsprechenden Kategorien zugeordnet.
Solange wir aber nicht die Vids der restlichen Instanzen um uns herum herausfinden nutzt und das ganze aber leider recht wenig.
Leider gibt es unter Python 2.2 noch keine Möglichkeit die OnRegisterVID in der Binary zu hooken, demnach müssen wir uns auf die alte Methode Bruteforce(Raten) verlassen.
Hierbei haben die meisten Coder Probleme mit der Performance.
Um eben diese zu gewährleisten greifen wir auf das thread Modul zurück.
Multi-threading bedeutet, dass die Prozesse im selben Arbeitsspeicher ausgeführt werden, aber komplett unabhängig von seinem Parallel Prozess laufen.
Dadurch gewährleisten wir, dass es Ingame kein bisschen laggt, obwohl eine recht große Menge an Vids in einem kleinen Zeitraum abgefragt werden.
Ein Thread erstellt man recht einfach:
Das Beispiel klappt in dem Fall zwar nur auf Windows und das auch nur in der Kommandozeile, soll aber auch nicht die function eine Rolle spielen sondern wie der Thread gestartet wird.
Kleine Ausname was den Thread betrifft:
Sofern nur 1 Parameter mitgeliefert wird ändert sich der 2. Parameter des Calls zu einem None, also z.b. so ein Aufruf
start_new_thread(cookies, (1, ))
Das letzte "," ist wichtig, sonst returned euch die function einen Syntax Fehler.
Genug zu der Funktionsweise der Threads, setzen wir das eben gelernte direkt um.
Zu allererst legen wir eine Range fest, in welcher die ganzen Vids gecheckt werden sollen.
In meinem Fall nehme ich einmal eine Range von 0 bis 2.700.000, da selbst bei Metin2 De auf den einigermaßen vollen Servern die Vid selten oder garnicht diesen Wert überschreitet.
Privat Server überlasten in diesem Fall gerne einmal ihre Server, aber das ist ja deren Sache.
Standardmäßig würden wir das ganze in 1 for loop abfragen, da es dann aber laggen würde, führen wir das ganze einfach in einem externen Thread aus.
Nun haben wir bereits unsere Aufgliederung und die umliegenden Vids mit denen wir arbeiten müssen, damit unser Bot laufen kann.
Zunächst wollen wir abfragen welches Monster überhaupt am nächsten zu uns liegt.
Dazu arbeiten wir mit dem Command "chr.GetPixelPosition()".
Natürlich wollen wir die Distanz zu unserem Charakter ermitteln, d.h. wir brauchen auch unsere eigenen Koordinaten, welche wir mit "player.GetMainCharacterPosition()" erhalten.
Damit wir auch die Koordinaten des gewünschten Monsters erhalten, visieren wir demnach unsere Ziel Vid erneut an und nutzen den oben geschriebenen Command.
Wir können diese 3 Variablen mit diesem einen Command nur nutzen, da GetPixelPosition ein Tuple der Länge 3 returned, also bereits (x, y, z).
Python unpacked das Tuple dann direkt, wenn die gewünschte Anzahl der Variablen der returnten Anzahl der Variablen entspricht.
Kommen wir ein bisschen zur Mathematik, der Vektorrechnung.
Diese brauchen wir um von unserem im 2D Raum betrachteten Aufstellung der Vid-Positionen die Distanz ermitteln zu können.
Die 3. Dimension der Vektorgeometrie benötigen wir nicht, da der Charakter nicht langsamer wird wenn er z.B. einen Berg hinaufläuft.
Hierzu stelle ich direkt die Funktion bereit, da dies nicht sonderlich viel mit der Sprache oder der Logik zu tun hat, sondern einfach eine mathematische Gegebenheit ist, die jeder im Laufe seiner Schulzeit mind. 2-3x lernt.
Wir können das ganze sortieren indem wir die praktische Eigenschaft von Python nutzen ein Array nach der Value zu sortieren.
D.h. wir fügen unsere ganzen Vids in ein assotiatives Datenfeld und schreiben die Distanz als Value dahinter.
Damit erhalten wir jetzt zwei dicts, welche in etwa so aussehen dürften:
distance_dict = {34: [1], 25: [14, 2]}
compare_array = [34, 25]
Jetzt sortieren wir das compare_array und erhalten den kleinsten Wert an 1. Stelle, in unserem Beispiel die 25.
Dazu schreiben wir:
shortest_distance = sorted(compare_array)[0]
Damit wir jetzt die Vid erhalten nutzen wir die Distanz, die wir als Key genommen haben im distance_dict.
D.h. wir nehmen:
distance_dict[shortest_distance]
Dies returned jetzt die Werte 14 und 2, also nehmen wir den 1. indem wir ein [0] an unserem Wert oben anhängen.
Nun haben wir also unser Monster, welches uns am nächsten liegt.
Dann laufen wir einmal zu unserem Monster, indem wir den Befehl chr.MoveToDestPosition nehmen.
Übersetzt heißt der Command Bewege dich zur festgelegten Position, dementsprechend brauchen wir hierbei auch noch eine Position.
Desweiteren wird benötigt WER sich dorthin bewegen soll, d.h. wir brauchen auch noch eine Vid.
D.h. der Command ist so aufgebaut:
chr.MoveToDestPosition(vid, x, y)
die Höhen-Achse brauchen wir ja nicht, da diese automatisch angepasst wird vom Spiel.
Okay damit läuft der Charakter schon einmal zum nächst gelegenen Monster.
Mit der oben verwendeten Distance Berechnung könnt ihr jetzt auch ständig die Distanz zum Monster berechnen und somit bemerken, wann ihr festhängt oder wann ihr in Schlagreichweite seit.
Hierzu eignet sich die OnUpdate Funktion wunderbar, da damit das Monster perfekt verfolgt werden kann.
Die Schlagdistanz beläuft sich auf eine Distanz von 120-180, kommt auf die Größe der Monster an.
Diese könnt ihr auch mit "chr.GetBoundBoxXY(vid)" perfekt berechnen, das wird aber zu umfangreich um das auch noch alles zu erklären.
Angekommen beim Monster setzen wir den Attack Key State auf true, damit der Charakter auch das Monster schlägt.
Dies passiert mit dem Command player.SetAttackKeyState(TRUE).
Nebenbei bemerkt: Metin2 parsed das True aus Python anderst und verlangt ein TRUE anstatt ein True, sodass ein normales True keine Auswirkung hat.
The rest is up to you^^
Von mir werdet ihr übrigens weniger Releases sehen sondern mehr Tutorials, dies liegt an manchen Aktionen in der Community, die mir nicht allzu gut gefallen, wo einige probieren andere Mitglieder der Community zu "veräppeln".
Ich sehe nicht ein, warum ich Releases machen soll für User, welche meinen, dass sie ihren Spaß mit den anderen Usern haben können und dann noch alles leechen können.
Aufgefallen sind mir diese Aktionen in diesem Thread:
[Only registered and activated users can see links. Click Here To Register...]
Demnächst wird noch ein kleineres Tutorial für einen Auto Answer Bot kommen, welcher sich auf cleverbot bezieht und einige kleinere Tricks wie man seinen Bot unauffälliger gestalten kann, sodass er aus dem Pattern der Standard Bots fällt.
Desweiteren ist es wesentlich sinnvoller seinen Bot selber zu schreiben, da er damit nicht public ist und die verschiedenen Varianten sich differenzieren, was es sehr viel schwerer gestaltet Bots zu erkennen und einzugruppieren.
So läuft mein eigener Bot den ich letzte Woche geschrieben habe bereits seit über 92 Stunden nonstop durch.
Durch den Auto-Answer Bot hat der Bot auch eine Frage eines GameMasters überstanden, welcher zwischendurch eine PN geschrieben hat.
Fragen & Verbesserungsvorschläge einfach in den Thread posten.
Einen guten Tag noch
Aber erstmal ein paar kleinere Informationen zu mir:
Ich studiere aktuell Softwaretechnik an der Universität Stuttgart wobei sich dies mehr auf Java beläuft.
Aber der wichtigste Part der Informatik ist grundlegend nicht eine Sprache perfekt zu beherrschen, sondern sie zu verstehen.
Python scripte ich nun seit knapp 2,5 Jahren und habe auch schon verschiedene, bisher eher kleinere Projekte auf die Beine gestellt.
Ich bin auch noch nicht sehr lange im Metin2 Bereich aktiv, habe mich auch erst neulich auf Epvp registriert.
Nichts desto trotz habe ich mir bereits die Python Scripte im Clienten durchgeschaut, wodurch ich zumindest das Prinzip des Clienten verstanden habe.
Weiter geht es mit dem Tutorial:
Was ist Python überhaupt?
Python ist eine bisher einigermaßen unbekannte Programmiersprache, die 1991 von Guido von Rossum in seinen Ferien entwickelt wurde.
Sie gewann schnell an Beliebtheit und wurde stetig weiterentwickelt, sodass sie heute auch mit anderen großen Sprachen mithalten kann.
Warum benutzt Metin2 überhaupt Python als Schnittstelle?
Python ist eine recht performante Sprache, welche auch ab Python 2.3 effizient in Low-Level Interface Bereichen dank ctypes recht interessant geworden ist.
Auch aufgrund seines stabilen Aufbaus und den vielfältigen Möglichkeiten wird es nicht nur in Metin2, sondern auch oft in anderen größeren Spielen verwendet.
Metin2 benutzt Python 2.2.1, die aktuellste Version ist aber 2.7.3 in der 2.x Version und 3.3.0 in der 3.x Version.
Diese wurde bereits seit 2003 nicht mehr weiterentwickelt und erhält selten irgendwelche neuen Module, wobei diese für den Clienten zum Großteil sowieso uninteressant sind.
Wenn die verwendete Python Version über 9 Jahre alt ist, wäre es dann nicht sinnvoll zu updaten?
Ein wichtiger Punkt ist die Performance, worauf jeder gute Programmierer zu achten hat.
Metin2 ruft selber in den Scripts zwei Funktionen auf, die OnUpdate und OnRender heißen, diese werden in der Binary gecalled und sind somit nicht standardmäßig in Python enthalten.
Diese Funktions werden 20x(OnUpdate) und 60x(OnRender) pro Sekunde aufgerufen.
Dies entspricht einer Delay von 0.05 Sekunden und ~0.017 Sekunden.
D.h. die Performance wird sich nicht sichtbar verbessern, wenn ihr Python auf eine neuere Version updated.
Die Funktionen werden zwar schneller ablaufen, allerdings wird die menschliche Wahrnehmung das nicht merken, da diese bei 16 Bildern pro Sekunde liegt, wovon nichtmal alle verarbeitet werden.
Falls ihr die Python Version updaten wollt, könnt ihr zwar neue Module hinzufügen, die Performance wird davon aber nicht sichtbar beeinträchtigt.
Python hat in den neueren Versionen mit unter anderem mit ctypes starke Low-Level Module hinzugefügt, diese werden dadurch auch für "Hacker" nutzbar.
Auch ist die WSAIoctl Funktion ab Python 2.5 verfügbar, welche recht interessant fürs Packet Editing ist, diese wird bei z.B. bei einem Update ebenfalls ermöglicht.
Solange ihr selber also nicht neue Module braucht und Python updaten möchtet ohne dass ihr überhaupt einen gewissen Zweck verfolgt würde ich euch davon abraten.
An Modulen, die man in Metin2 brauchen könnte kamen bis auf z.B. sqlite3 nicht sonderlich viele Module hinzu.
Falls ihr diese Module trotzdem vermisst, gibt es meist noch von der damaligen Zeit Ersatzlösungen oder von neuerer Zeit Backports der demensprechenden Module.
Die Grundlagen von Python:
Ich setze hier bestimmte Grundlagen von Python vorraus, ihr müsst demnach zumindest den groben Syntax verstehen.
indented block(falsche Einrückung von den Tabs bzw. Leerzeichen), invalid syntax wenn jemand z.B. einen Doppelpunkt vergisst werde ich hier nicht erläutern, diese findet ihr bereits beim "Hello World", also dem absoluten Basic bei jedem Python HowTo.
Ich werde hier Metin2 spezifische Module erläutern und die Logik bzw. diskrete Funktionen(so lautet auch eine der wichtigsten Vorlesungen bei meinem Studium).
Eines der wichtigsten Punkte hierbei sind die Vids.
Dies sind virtuelle IDs welche jedem Spieler/Item/Mob zugewiesen werden um das ganze Spiel überhaupt erst in seiner Form zu ermöglichen.
Diese beinhalten alle möglichen Informationen, die der Spieler über die entsprechende Vid erhalten soll, teilweise sogar mehr als geplant war wie ich selber mitbekommen habe.
Gehen wir einmal davon aus, dass die Vid des gewünschten Monsters 1 sei:
Nun haben wir verschiedene Möglichkeiten um an die Informationen des Monsters zu kommen.
Falls man mit dem chr Modul arbeitet ist es erstmal wichtig, die gewünschte Instanz überhaupt erst einmal anzuwählen.
Dies passiert mit dem Command "chr.SelectInstance(vid)", vid ist eine Variable mit dem Datentyp Integrer, d.h. in unserem Beispiel könnten wir direkt über dem Command vid = 1 definieren.
Damit wir unterscheiden können ob es sich bei der erhaltenen Vid um einen Gegner, Npc, Monster, Spieler oder sonstiges unterscheidet überprüfen wir den Instance Type.
Gliedern wir das ganze etwas auf um etwas Ordnung in unsere Vid-Liste zu bekommen.
Dazu erstellen wir kurzerhand ein assoziatives Datenfeld, das folgendermaßen aussieht:
PHP Code:
#create an array with the possible instance types as keys
vid_array = {
chr.INSTANCE_TYPE_BUILDING: [],
chr.INSTANCE_TYPE_ENEMY: [],
chr.INSTANCE_TYPE_NPC: [],
chr.INSTANCE_TYPE_OBJECT: [],
chr.INSTANCE_TYPE_PLAYER: [],
6: [],
}
Diesen finden wir mit dem Command "chr.GetInstanceType(vid)" heraus.
Hierfür müssen wir die Instanz dieses mal nicht selecten.
Schauen wir einmal an was wir bisher so haben:
PHP Code:
import chr, player
from chat import AppendChat
#create an array with the possible instance types as keys
vid_array = {
chr.INSTANCE_TYPE_BUILDING: [],
chr.INSTANCE_TYPE_ENEMY: [],
chr.INSTANCE_TYPE_NPC: [],
chr.INSTANCE_TYPE_OBJECT: [],
chr.INSTANCE_TYPE_PLAYER: [],
6: [],
}
#get main character index
vid = player.GetMainCharacterIndex()
#get the instance type from the selected vid
instance_type = chr.GetInstanceType(vid)
#check if vid is already in the array, else append it to the array
if not vid in vid_array[instance_type]:
vid_array[instance_type].append(vid)
Solange wir aber nicht die Vids der restlichen Instanzen um uns herum herausfinden nutzt und das ganze aber leider recht wenig.
Leider gibt es unter Python 2.2 noch keine Möglichkeit die OnRegisterVID in der Binary zu hooken, demnach müssen wir uns auf die alte Methode Bruteforce(Raten) verlassen.
Hierbei haben die meisten Coder Probleme mit der Performance.
Um eben diese zu gewährleisten greifen wir auf das thread Modul zurück.
Multi-threading bedeutet, dass die Prozesse im selben Arbeitsspeicher ausgeführt werden, aber komplett unabhängig von seinem Parallel Prozess laufen.
Dadurch gewährleisten wir, dass es Ingame kein bisschen laggt, obwohl eine recht große Menge an Vids in einem kleinen Zeitraum abgefragt werden.
Ein Thread erstellt man recht einfach:
PHP Code:
#importing from the modules only the functions I need in my script
from thread import start_new_thread
from os import system
#we still need a function we can call, so I defined a new one
clear = lambda: system("cls")
#finally start the new thread
start_new_thread(clear, ())
Kleine Ausname was den Thread betrifft:
Sofern nur 1 Parameter mitgeliefert wird ändert sich der 2. Parameter des Calls zu einem None, also z.b. so ein Aufruf
start_new_thread(cookies, (1, ))
Das letzte "," ist wichtig, sonst returned euch die function einen Syntax Fehler.
Genug zu der Funktionsweise der Threads, setzen wir das eben gelernte direkt um.
Zu allererst legen wir eine Range fest, in welcher die ganzen Vids gecheckt werden sollen.
In meinem Fall nehme ich einmal eine Range von 0 bis 2.700.000, da selbst bei Metin2 De auf den einigermaßen vollen Servern die Vid selten oder garnicht diesen Wert überschreitet.
Privat Server überlasten in diesem Fall gerne einmal ihre Server, aber das ist ja deren Sache.
Standardmäßig würden wir das ganze in 1 for loop abfragen, da es dann aber laggen würde, führen wir das ganze einfach in einem externen Thread aus.
PHP Code:
import chr, player
from chat import AppendChat
from thread import start_new_thread
vid_array = {
chr.INSTANCE_TYPE_BUILDING: [],
chr.INSTANCE_TYPE_ENEMY: [],
chr.INSTANCE_TYPE_NPC: [],
chr.INSTANCE_TYPE_OBJECT: [],
chr.INSTANCE_TYPE_PLAYER: [],
6 : [],
}
final_range = 36
def check_vid(check_range):
for vid in range(check_range * 75000):
#get the instance type from the selected vid
instance_type = chr.GetInstanceType(vid)
#check if vid is already in the array, else append it to the array
if not vid in vid_array[instance_type]:
vid_array[instance_type].append(vid)
if check_range == final_range:
##here you can continue with your script^^
print("Scan finished...")
for check_range in range(final_range):
start_new_thread(check_vid, (check_range, ))
Zunächst wollen wir abfragen welches Monster überhaupt am nächsten zu uns liegt.
Dazu arbeiten wir mit dem Command "chr.GetPixelPosition()".
Natürlich wollen wir die Distanz zu unserem Charakter ermitteln, d.h. wir brauchen auch unsere eigenen Koordinaten, welche wir mit "player.GetMainCharacterPosition()" erhalten.
Damit wir auch die Koordinaten des gewünschten Monsters erhalten, visieren wir demnach unsere Ziel Vid erneut an und nutzen den oben geschriebenen Command.
PHP Code:
import player, chr
x, y, z = player.GetMainCharacterPosition()
chr.SelectInstance(vid)
x, y, z = chr.GetPixelPosition()
Python unpacked das Tuple dann direkt, wenn die gewünschte Anzahl der Variablen der returnten Anzahl der Variablen entspricht.
Kommen wir ein bisschen zur Mathematik, der Vektorrechnung.
Diese brauchen wir um von unserem im 2D Raum betrachteten Aufstellung der Vid-Positionen die Distanz ermitteln zu können.
Die 3. Dimension der Vektorgeometrie benötigen wir nicht, da der Charakter nicht langsamer wird wenn er z.B. einen Berg hinaufläuft.
Hierzu stelle ich direkt die Funktion bereit, da dies nicht sonderlich viel mit der Sprache oder der Logik zu tun hat, sondern einfach eine mathematische Gegebenheit ist, die jeder im Laufe seiner Schulzeit mind. 2-3x lernt.
PHP Code:
import player, math
def get_distance(mob_x, mob_y):
#get main character position
player_x, player_y, player_z = player.GetMainCharacterPosition()
#distance calculation
tmp_x, tmp_y = (((player_x - mob_x) ** 2/2), ((player_y - mob_y) ** 2/2))
return(math.sqrt(tmp_x + tmp_y))
D.h. wir fügen unsere ganzen Vids in ein assotiatives Datenfeld und schreiben die Distanz als Value dahinter.
PHP Code:
import chr
distance_dict = {}
compare_array = []
#create a loop with the vids as variables
for vid in vid_list[chr.INSTANCE_TYPE_ENEMY]:
#calculate the distance
distance = get_distance(vid)
#if distance isn't already in the dict create it
if not distance in distance_dict:
distance_dict[distance] = []
#append the values to the arrays
distance_dict.append(vid)
compare_array.append(distance)
distance_dict = {34: [1], 25: [14, 2]}
compare_array = [34, 25]
Jetzt sortieren wir das compare_array und erhalten den kleinsten Wert an 1. Stelle, in unserem Beispiel die 25.
Dazu schreiben wir:
shortest_distance = sorted(compare_array)[0]
Damit wir jetzt die Vid erhalten nutzen wir die Distanz, die wir als Key genommen haben im distance_dict.
D.h. wir nehmen:
distance_dict[shortest_distance]
Dies returned jetzt die Werte 14 und 2, also nehmen wir den 1. indem wir ein [0] an unserem Wert oben anhängen.
Nun haben wir also unser Monster, welches uns am nächsten liegt.
Dann laufen wir einmal zu unserem Monster, indem wir den Befehl chr.MoveToDestPosition nehmen.
Übersetzt heißt der Command Bewege dich zur festgelegten Position, dementsprechend brauchen wir hierbei auch noch eine Position.
Desweiteren wird benötigt WER sich dorthin bewegen soll, d.h. wir brauchen auch noch eine Vid.
D.h. der Command ist so aufgebaut:
chr.MoveToDestPosition(vid, x, y)
die Höhen-Achse brauchen wir ja nicht, da diese automatisch angepasst wird vom Spiel.
Okay damit läuft der Charakter schon einmal zum nächst gelegenen Monster.
Mit der oben verwendeten Distance Berechnung könnt ihr jetzt auch ständig die Distanz zum Monster berechnen und somit bemerken, wann ihr festhängt oder wann ihr in Schlagreichweite seit.
Hierzu eignet sich die OnUpdate Funktion wunderbar, da damit das Monster perfekt verfolgt werden kann.
Die Schlagdistanz beläuft sich auf eine Distanz von 120-180, kommt auf die Größe der Monster an.
Diese könnt ihr auch mit "chr.GetBoundBoxXY(vid)" perfekt berechnen, das wird aber zu umfangreich um das auch noch alles zu erklären.
Angekommen beim Monster setzen wir den Attack Key State auf true, damit der Charakter auch das Monster schlägt.
Dies passiert mit dem Command player.SetAttackKeyState(TRUE).
Nebenbei bemerkt: Metin2 parsed das True aus Python anderst und verlangt ein TRUE anstatt ein True, sodass ein normales True keine Auswirkung hat.
The rest is up to you^^
Von mir werdet ihr übrigens weniger Releases sehen sondern mehr Tutorials, dies liegt an manchen Aktionen in der Community, die mir nicht allzu gut gefallen, wo einige probieren andere Mitglieder der Community zu "veräppeln".
Ich sehe nicht ein, warum ich Releases machen soll für User, welche meinen, dass sie ihren Spaß mit den anderen Usern haben können und dann noch alles leechen können.
Aufgefallen sind mir diese Aktionen in diesem Thread:
[Only registered and activated users can see links. Click Here To Register...]
Demnächst wird noch ein kleineres Tutorial für einen Auto Answer Bot kommen, welcher sich auf cleverbot bezieht und einige kleinere Tricks wie man seinen Bot unauffälliger gestalten kann, sodass er aus dem Pattern der Standard Bots fällt.
Desweiteren ist es wesentlich sinnvoller seinen Bot selber zu schreiben, da er damit nicht public ist und die verschiedenen Varianten sich differenzieren, was es sehr viel schwerer gestaltet Bots zu erkennen und einzugruppieren.
So läuft mein eigener Bot den ich letzte Woche geschrieben habe bereits seit über 92 Stunden nonstop durch.
Durch den Auto-Answer Bot hat der Bot auch eine Frage eines GameMasters überstanden, welcher zwischendurch eine PN geschrieben hat.
Fragen & Verbesserungsvorschläge einfach in den Thread posten.
Einen guten Tag noch