Befehle mit Übergabewerten

Wichtiger Hinweis: Bitte ändert nicht manuell die Schriftfarbe auf schwarz sondern belasst es bei der Standardeinstellung. Somit tragt ihr dazu bei dass euer Text auch bei Verwendung unseren dunklen Forenstils noch lesbar ist!

Tipp: Ihr wollt längere Codeausschnitte oder Logfiles bereitstellen? Benutzt unseren eigenen PasteBin-Dienst Link
  • nimm ocmd ist schneller wie dcmd ,


    ocmd:straftat(playerid,params[]) // den code nicht in OnPlayerCommandText , sondern z.B über den public
    {
    new string1[256];
    new string2[256];
    new string3[256];
    new
    pID,
    anzahl,
    grund[128];
    if(sscanf(params, "uds",pID,anzahl,grund)) return SendClientMessage(playerid,COLOR_RED,"USAGE: /st [playerid][Anzahl an WantedPunkten][grund]"); // in einer zeile schaut es gleich besser aus

    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }


    WantedPoints[pID] += anzahl;
    SetPlayerCriminal(pID,playerid, grund);
    SendClientMessage(playerid,COLOR_RED,"Erfolgreich!");
    format(string1,128,"Du hast ID:%d %d WantedPunkte gegeben![Grund:%s]",pID,anzahl,grund);
    SendClientMessage(playerid,COLOR_RED,string1);
    format(string2,128,"ID:%d %d hat dir %d WantedPunkte gegeben![Grund:%s]",playerid,anzahl,grund);
    SendClientMessage(playerid,COLOR_RED,string2);
    format(string3,128,"HQ:%d hat %d %d WantedPunkte gegeben, weil %s",playerid,pID,anzahl,grund);
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(IsACop(i))
    {
    SendClientMessage(i,COLOR_RED,string3);



    }
    }
    return 1;
    }


    hier die include http://www.kleiner-opa.de/files/ocmd.inc
    und hier eine schöne grafik http://www.kleiner-opa.de/pic/test.png

  • So ist es richtig als DCMD Killer_Kater, trotzdem solltest du auch darauf achten wieviele Strings du erstellst.
    new string1[256];
    new string2[256];
    new string3[256];

    Es würde auch reichen

    new str[128];

    Danach
    format(str,sizeof(str),"Du hast ID:%d %d WantedPunkte gegeben![Grund:%s]",pID,anzahl,grund);
    SendClientMessage(playerid,COLOR_RED,str);
    format(str,sizeof(str),"ID:%d %d hat dir %d WantedPunkte gegeben![Grund:%s]",playerid,anzahl,grund);
    SendClientMessage(playerid,COLOR_RED,str);
    format(str,sizeof(str),"HQ:%d hat %d %d WantedPunkte gegeben, weil %s",playerid,pID,anzahl,grund);
    for(new i = 0; i < MAX_PLAYERS; i++)
    {
    if(IsACop(i))
    {
    SendClientMessage(i,COLOR_RED,str);
    }
    }



    [iP]Zabus,diese Grafik ist ja mal ein Witz.
    http://www.kleiner-opa.de/pic/test.png
    Mich würde ja mal dazu der Code interessieren, mit dem es getestet wurde oder die genauen Timings.Find ich persöhnlich immernoch lustig zu sehen wie jmd da betreitet es gäbe ZCMD nicht :rolleyes: .

  • << Zurück zu Teil 1 des Tutorials



    Im ersten Teil des Tutorials haben wir bereits gelernt,wie wir mit DCMD umgehen.Durch DCMD ist der Command nun
    bereits leichter gestaltet,doch das einfache durchsuchen des Befehls nach zusätzlichen Informationen wurde noch nicht
    großartig erleichtert.
    Dazu jetzt mehr in Teil 2.


    SSCANF
    sscanf wurde von Alex "Y_Less" C. erstellt.Eine sehr bekannte Person für jeden der sich des öfteren im SA:MP Forum rumtreibt.
    Zunächst einmal braucht ihr den sscanf - Code
    sscanf code - SA-MP
    Es gibt bereits ein SSCANF Plugin, womit die Performence gesteigert wurde sowie viele neue Verbesserung eingeführt wurden.
    SSCANF Plugin
    Ich rate jedoch sich nicht sofort mit dem Plugin zu beschäftigen, da es teilweise für Anfänger schwieriger sein kann,die neuen Platzhalter zu verstehen.


    Nun aber weiter im eigentlichem Tutorial
    Viel umschreiben muss man jetzt nicht mehr vom Befehl wie wir in ihn Teil 1 geschrieben haben.
    dcmd_kick(playerid,params[]) {
    new
    pID;
    if(sscanf(params, "u",pID)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid]");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    Kick(pID);
    SendClientMessage(playerid,COLOR_YELLOW,"Erfolgreich Spieler vom Server gekickt!");
    return 1;
    }
    Jetzt müsste euch die Zeile

    if(sscanf(params, "u",pID)) {


    auffallen.Diese Zeile spart euch nun das ganze getue mit strtok,strget oder Sonstigem.
    sscanf brauch zwei Parameter.Der erste Paramter ist der String in dem gesucht werden soll nach etwas.Da wir dcmd benutzten ist es params.Der zweite Paramter ( "u" ) ist der Typ der Information.Es kann so ziemlich alles sein,was man brauch.String,Integer,Float,Hex,Char oder sogar ein Spieler.Dabei steht der Platzhalter "u" für einen Spieler,es kann sowohl der Name als auch die SpielerID angeben werden.Beides ist möglich.


    Alle Datentypes mit ihren Charakterkürzeln gibt es hier:
    Fast Commands - SA-MP - Data types


    Jetzt gibt es aber noch weitere Paramter.Die Anzahl der weiteren Paramter sollte gleich zu der Anzahl sein wie die Anzahl an Datentypes die ihr im String sucht.
    In unserem Beispiel handelt es sich um "u".Demnach brauchen wir einen weiteren Parameter, dabei handelt es sich um den Spieler an dem wir die Operation durchführen wollen.


    sscanf(params, "udd",pID,WeaponID,Ammo) // /giveweapon [playerid] [weaponid] [ammo]
    sscanf(params, "s",sAnnounce) // /announce [text]
    sscanf(params, "f",fGrav) // /gravity [Float:Anziehungskraft]


    Wird ein Spieler ( Name oder ID ) gefunden in params,wird er pID zugewiesen.Sehr praktisch wenn ihr mich fragt.Sollte jetzt gültiger Wert für den Spielrnamen oder die SpielerID im String params sein,wird sscanf irgendwas returnen nur nicht 0.


    Nun wollen wir den Command etwas ausbreiten.Wir wollen einen Grund des Kicks als Grund mit angeben.Da der Grund für einen kick normalerweise ein Text ist,müssen wir jetzt als Datentype "s" oder "z".Da "z" ein Optionaler String ist,lassen wir den erst aus.Zu dem komm ich gleich ;).
    Wir brauchen natürlich noch eine neue Variable (ein String) ,in der wir den Grund speichern.Demnach sieht es so aus mit einer kleinen Extra Nachricht:
    dcmd_kick(playerid,params[]) {
    new
    sGrund[128],
    pID;
    if(sscanf(params, "us",pID,sGrund)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid] [grund]");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,ThePlayer,sizeof(ThePlayer));
    format(string,sizeof(string),"%s (ID %d) wurde vom Server gekickt,Grund: %s",ThePlayer,pID,sGrund);
    Kick(pID);
    return 1;
    }
    Wenn das nicht einfach war.Keine große Veränderung der eigentlichen sscanfs-Zeile und schon ein neuer Parameter.
    Wir können den Grund auch optional angeben,also:Es muss kein Grund als Kick angegeben werden,nur wenn man möchte.
    Dafür gibt es einen extra Datentype,das ist "z".


    Achtung: Dies gilt nur für die PAWN Version SSCANF
    In der Plugin Version von SSCANF sind die Platzhalter für einen Datentyp Anders!
    http://forum.sa-mp.com/showthread.php?t=120356


    Also ändern wir erst einmal die sscanf Zeile:
    if(sscanf(params, "uz",pID,sGrund)) {
    Das tolle daran ist,wird kein Grund angegeben returnt sscanf nicht 0
    dcmd_kick(playerid,params[]) {
    new
    sGrund[128],
    pID;
    if(sscanf(params, "uz",pID,sGrund)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid] ([grund],optional)");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,ThePlayer,sizeof(ThePlayer));
    format(string,sizeof(string),"%s (ID %d) wurde vom Server gekickt,Grund: %s",ThePlayer,pID,sGrund[0] ? sGrund : "<Kein Grund>");
    SendClientMessageToAll(COLOR_YELLOW,string);
    Kick(pID);
    return 1;
    }
    Was es mit dem sGrund[0] ? sGrund : "<Kein Grund>" auf sich hat möchte ich nicht genau erklären.Kurz gesagt,wenn das was vor dem "?" true ist (sGrund[0]) ,wird das nach dem "?" eingesetzt ( sGrund ),ansonsten das nach dem ":" ( "<Kein Grund>" ).Demnach,falls sGrund ein leerer string ist,also false,wird "<Kein Grund>" eingesetzt anstatt sGrund.


    Nun solltet ihr verstanden haben wie dcmd in Verbindung mit sscanf funktioniert.


    Hier noch ein weiterer Beispiele:
    public OnPlayerCommandText(playerid, cmdtext[])
    {
    dcmd(kick,4,cmdtext);
    dcmd(sethealth,9,cmdtext);
    dcmd(announce,8,cmdtext);
    return 0;
    }
    dcmd_sethealth(playerid,params[])
    {
    new
    pID,
    Float:fHP;
    if(sscanf(params, "uf",pID,fHP)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /sethealth [playerid] [healthpoints]");


    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");


    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    GivePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,GivePlayer,sizeof(GivePlayer));
    GetPlayerName(playerid,ThePlayer,sizeof(ThePlayer));
    SetPlayerHealth(pID,fHP);
    format(string,sizeof(string),"%s (ID %d) Healtpoints wurden von %s (ID %d) auf %.1f gesetzt!",GivePlayer,pID,ThePlayer,playerid,fHP);
    SendClientMessageToAll(COLOR_YELLOW,string);
    return 1;
    }
    dcmd_announce(playerid,params[]) {
    new
    sAnnounce[128];
    if(sscanf(params, "s",sAnnounce)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /announce [text]");
    }
    GameTextForAll(sAnnounce,3*1000,3);
    return 1;
    }
    dcmd_kick(playerid,params[]) {
    new
    sGrund[128],
    pID;
    if(sscanf(params, "uz",pID,sGrund)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid] ([grund],optional)");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,ThePlayer,sizeof(ThePlayer));
    format(string,sizeof(string),"%s (ID %d) wurde vom Server gekickt,Grund: %s",ThePlayer,pID,sGrund[0] ? sGrund : "<Kein Grund>");
    SendClientMessageToAll(COLOR_YELLOW,string);
    Kick(pID);
    return 1;
    }

    Wer sich trotzdem nochmal zu sscanf schlau machen möchte,sollte diesen Link hier anklicken:
    Fast Commands - SA:MP - sscanf


    Ich hoffe mal das ganze ist verständlich erklärt und von nun an werden einige mehr in diesem Forum auf dcmd + sscanf umsteigen.


    Goldkiller


    >> Teil 3 des Tutorials


    //Edit ( 3.10.2010 ) : Parameter u wird nun für Spieler genutzt anstatt d

    5 Mal editiert, zuletzt von Goldkiller ()

  • Zabus, stoppst du das mal, es ist sowas von unnützlich und wiederlich überall hinzuschreiben das OCMD besser ist,OCMD ist meiner meinung nach scheiße, außerdem ist es sowas von Blöd dass man nix unter OnPlayerCommandText schreibt, denn so ist es noch besser denn man kann immer nachsehen welche CMDS man hat, und außerdem:
    DracoBlue ist %100 besserer Scripter als kleinerOpa, wenn der es wollte könnte er es auch ohne OnPlayerCommandText machen.


    Das NERVT langsam überall das wort "ocmd" zu sehen, welches eh nur unnütz ist, also hört endlich auf damit, überall für OCMD zu werben, nur weil OCMD net gemocht wird.... PFF :thumbdown:


    @Topic://
    Ist es auch möglich, einen CMD so zu machen per sscanf ?:
    dcmd_blo(playerid,params[])
    {
    new ypsilon, pupsilon, rock;
    if(sscanf(params,"d",rock)) { return SendClientMessage(playerid,COLOR_GREY,"Syntax: /blo [ypsilon (OPTiONAL)] [pupsilon (OPTiONAL)] [rock]"); }
    if(sscanf(params,"d",ypsilon)) { ypsilon=5; }
    if(sscanf(params,"d",pupsilon)) { pupsilon=7; }
    return 1;
    }


    Ich denke schon, oder ? ^^

  • Meiner meinung nach kann man ihn nicht 2 mal splitten das heißt so wirds nicht funktionieren wäre auch unnötig



    if(sscanf(params,"ddd",rock, ypsilon, pupsilon=7;)) { return SendClientMessage(playerid,COLOR_GREY,"Syntax: /blo [ypsilon (OPTiONAL)] [pupsilon (OPTiONAL)] [rock]"); }



    RFT

  • Ließ dir den Thread durch, da steht alles.

    Zitat

    weil wenn z.B. pupsilon nicht angegeben wird, wird es zu 7 gemacht aber der Spieler wird's trotzdem angeben müssen.


    Ganz klar Nein.
    Der Spieler muss es nicht angeben,alle Platzhalter die Groß ( D ) geschrieben werden,sind Optional.Heisst also, sie brauchen nicht angegeben zu werden.


    Zitat

    Aber ich denke meins wird funken, ich werds mal testen...


    Tu dir keinen Zwang an :p

  • Ließ dir den Thread durch, da steht alles.


    Ganz klar Nein.
    Der Spieler muss es nicht angeben,alle Platzhalter die Groß ( D ) geschrieben werden,sind Optional.Heisst also, sie brauchen nicht angegeben zu werden.



    Tu dir keinen Zwang an :p


    Der Post war zu RFT xD
    Goldkiller: Achso,sorry :p
    Aber ich werd's wirklich testen, ich hab soein gefühl das es funken wird. XD


    €dit://
    HAHA Funktionieren beide nicht richtig :D


    @Goldkiller://
    Ich kenne mich mit Plugins net aus, deswegen will ich sie auch irgendwie nicht benutzen x)
    Naja wenn ich sie mal brauche benutze ich es mal :P


    Goldkiller: Du müsstest auch das Plugin verwenden,damit es funktioniert ;)

    3 Mal editiert, zuletzt von Goldkiller ()

  • Ich wollte schon seit längerem mal das Tutorial erweitern um ZCMD, hab jetzt endlich mal Zeit dafür gefunden.


    zcmd
    ZCMD ist ein weiterer Weg seine Commands einfach und schnell zu scripten.Die Methode funktioniert um einiges anders als dcmd.Hier wird der eingegebene Text, der bei OnPlayerCommandText() übergeben wird nicht wie bei DCMD mit allen Befehlen verglichen die ihr unter OnPlayerCommandText() habt, sondern es wird versucht ein public Funktion aufzurufen,die den Namen des Commands hat.


    Diese Technik wurde zum ersten mal von Y_Less vorgestellt.Allerdings wurde sie kaum benutzt,lag wohl darans,dass
    Y_Less diese Technik mehr oder weniger versteckt hatte in seinem YSI Include,welches relativ wenig genutzt wurde.
    Viel später kam dann eine Russe Namens ZeeX auf die Idee diese Technik zu nutzen und stellte diese
    im offiziellen SA:MP Forum vor.Seit der ersten Version wurde sie um einiges verändert und ist nun
    neben DCMD die wohl meist genutzte Command-Technik.


    Alle darauf folgenden XYZ-Commands sind nur basierten auf ZCMD und auch nicht schneller / besser,deshalb empfehle
    Ich beim Original zu bleiben.


    Die Version von ZeeX ist hier zu finden: [INC] zcmd 0.3.1 | Fast & Simple Command Processor (updated 30/10/2009)



    Es ist generell kein großer Aufwand,die Commands von dcmd in zcmd umzuschreiben.Nehmen wir mal an,unser Befehl sieht so aus ( Bereits im Tutorial erstellt ).
    public OnPlayerCommandText(playerid, cmdtext[])
    {
    dcmd(kick,4,cmdtext);
    return 0;
    }
    dcmd_kick(playerid,params[]) {
    new
    sGrund[128],
    pID;
    if(sscanf(params, "dz",pID,sGrund)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid] ([grund],optional)");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,ThePlayer,sizeof(ThePlayer));
    format(string,sizeof(string),"%s (ID %d) wurde vom Server gekickt,Grund: %s",ThePlayer,pID,sGrund[0] ? sGrund : "<Kein Grund>");
    SendClientMessageToAll(COLOR_YELLOW,string);
    Kick(pID);
    return 1;
    }
    Wer dcmd verstanden hat, dem sollte klar sein, dass /kick der eigentliche Befehl ist.
    Um es nun in ZCMD umzuschreiben,sollte man zuerst den Teil unter OnPlayerCommandText() löschen ( dcmd(kick,4,cmdtext); )


    Den DCMD selber muss man natürlich auch verändern.Sind aber auch nur kleine Veränderungen.
    Aus dem
    dcmd_kick(playerid,params[]) {
    wird dabei :
    COMMAND:kick(playerid,params[]) {
    Viel muss man also nicht ändern.Man ersetz das dcmd_ aus dcmd_kick() durch COMMAND:.
    Daraus wird dann also COMMAND:kick()


    Deshalb erhält man nun das folgende Script:
    public OnPlayerCommandText(playerid, cmdtext[])
    {
    // dcmd(kick,4,cmdtext); - Brauchen wir nicht mehr
    return 0;
    }
    COMMAND:kick(playerid,params[]) {
    new
    sGrund[128],
    pID;
    if(sscanf(params, "uz",pID,sGrund)) {
    return SendClientMessage(playerid,COLOR_RED,"USAGE: /kick [playerid] ([grund],optional)");
    }
    if(!IsPlayerConnected(pID)) {
    return SendClientMessage(playerid,COLOR_RED,"Kein Spieler mit angegebener ID Online");
    }
    new
    ThePlayer[MAX_PLAYER_NAME],
    string[128];
    GetPlayerName(pID,ThePlayer,sizeof(ThePlayer));
    format(string,sizeof(string),"%s (ID %d) wurde vom Server gekickt,Grund: %s",ThePlayer,pID,sGrund[0] ? sGrund : "<Kein Grund>");
    SendClientMessageToAll(COLOR_YELLOW,string);
    Kick(pID);
    return 1;
    }



    Man muss aber auch auf Kleinigkeiten im Script achten.Solltet ihr ZCMD in einem Filterscript benutzen,
    solltet ihr sicher stellen,dass ihr im Kopf eures Scriptes, das Script als Filterscript definiert.
    #define FILTERSCRIPT
    Das ist deshalb nötig,da bei ZCMD einige Callbacks wichtig sind für das funktionieren des Technik.Wie das Ganze im Detail funktioniert sollte für die meisten unwichtig sein.


    Auch sollte man jetzt darauf achten,dass man nur mit isnull() überprüft ob der übergebene Text in
    params[] leer ist.Die Funktion isnull() ist übrigens im ZCMD Include enthalten.
    Bedeutet im Klartext,keine Abfragen mehr wie:


    if(!params[0]) { }
    if(!strlen(params)) { }


    Stattdessen müsst ihr es jetzt so machen
    if(isnull(params)) { }


    Falls ihr mehrere Commandhandler habt für eine Funktion, müsst ihr ebenfalls kleine Änderungen in eurem Script vor nehmen.
    Nehmen wir an /test ist der eigentliche Command den wir auch über /testaufruf aufrufen sollen,sieht es so aus.


    COMMAND:testaufruf(playerid,params[]) {
    cmd_test(playerid,params);
    }
    Dabei ist zu beachten,dass der zusätzliche Command-Handler ( testaufruf ) nach dem
    Command im Script kommt, zu dem es weiterleiten soll.Heisst also:

    COMMAND:test(playerid,params[]) {
    printf("COMMAND:test(%d,%s)",playerid,params);
    return 1;
    }
    COMMAND:testaufruf(playerid,params[]) {
    return cmd_test(playerid,params);
    }




    Ich selber benutze auch nur noch die ZCMD Methode.Im Endeffekt muss aber jeder für sich selber entscheiden welche Methode ihm am besten gefällt.

    2 Mal editiert, zuletzt von Goldkiller ()

  • mach SSCANF raus^^


    PS: "#pragma unused params"


    //Edit:


    Das musst du überigens mit einer FOR Schleife machen ;>


    //Edit2:


    dcmd_admins(playerid,params[])
    {
    #pragma unused params
    SendClientMessage(playerid, COLOR_BLUE, "|______Supporter / Administratoren Online:______|");


    new i = 0, aname[256], string[256];
    for(; i < MAX_PLAYERS; i++)
    {
    if(IsPlayerConnected(i))
    {
    if(Player[i][admin] == 1)
    {
    GetPlayerName(i, aname, sizeof(aname));
    format(string, 256, " Supporter: %s", aname);
    SendClientMessage(playerid, COLOR_LIGHTBLUE, string);
    }
    else if(Player[i][admin] == 2)
    {
    GetPlayerName(i, aname, sizeof(aname));
    format(string, 256, " Super Supporter: %s", aname);
    SendClientMessage(playerid, COLOR_LIGHTBLUE, aname);
    }
    else if(Player[i][admin] == 3)
    {
    GetPlayerName(i, aname, sizeof(aname));
    format(string, 256, " Admin: %s", aname);
    SendClientMessage(playerid, COLOR_LIGHTBLUE, string);
    }
    else if(Player[i][admin] == 4)
    {
    GetPlayerName(i, aname, sizeof(aname));
    format(string, 256, " Server Besitzer: %s", aname);
    SendClientMessage(playerid, COLOR_LIGHTBLUE, string);
    }
    }
    }
    return 1;
    }

    Die deutsche Rechtschreibung ist Freeware, sprich du darfst sie kostenlos nutzen...
    Sie ist aber nicht Open Source, somit darfst du sie nicht verändern oder in veränderter Form veröffentlichen!


  • oke und den ResT? xD


    danke dir :D



    eddit


    habs ;) danke


    aber die anderen Befehle muss ich sie auch mit for schleife machen= ``??




    Wow


    ich habe mir die Tutorials noch nie genau durchgelesen aber nun oO


    Respekt


    jetzt hab ich alles gecheckt ^^


    danke Goldkiller ;)

    Einmal editiert, zuletzt von TeamSpirit ()