Beiträge von |-|auke

    Du startest den Code unter OnPlayerPickUpPickup, also wenn ein Spieler ein Pickup aufsammelt. Allerdings führst du den code mit der Schleife 500 mal aus.
    Nimm:

    for(new i = 0; i < MAX_PLAYERS; i++)
    {

    und unten
    } raus.

    Nun erstellen wir uns eine Funktion, mitder wir die Häuser auslesen und erstellen können:

    LoadHouses ( )
    {
    new File:houses = fopen ( HOUSEFILE, io_read ) , content[ 512 ];
    if ( !houses )
    return 0;
    new house; // Diese Variable wird bei jedem Durchlauf um 1 erhöht.
    while ( fread ( houses , content ) ) // Jede Zeile = ein Schleifdurchlauf
    {
    // Parser © by me :P. Das erkläre ich nicht alles, denn das wäre 1. zu viel und 2. nicht das Thema.
    new v[ 23 ][ 64 ] , bool:stop = false , s;
    while ( stop != true )
    {
    if ( strfind ( content , "|" ) != -1 )
    {
    for ( new x; x < strfind ( content , "|" ); x++ )
    v[ s ][ x ] = content[ x ];
    strdel ( content , 0 , strfind ( content , "|" ) + 1 );
    }
    else
    {
    for ( new x; x < strlen ( content ); x++ )
    v[ s ][ x ] = content[ x ];
    stop = true;
    }
    if ( !strcmp ( v[ s ] , "NULL" , true ) )
    format ( v[ s ] , 16 , "%d" , NULL );
    if ( !strcmp ( v[ s ] , "YES" , true ) )
    format ( v[ s ] , 16 , "%d" , YES );
    if ( !strcmp ( v[ s ] , "NO" , true ) )
    format ( v[ s ] , 16 , "%d" , NO );
    s++;
    } // Ab hier wird es erst interessant:
    House[ house ][ sID ] = stv ( v[ 0 ] ); // ID aus Datei wird in sID gespeichert.
    House[ house ][ InteriorID ] = AddInterior ( v[ 1 ] , stv ( v[ 2 ] ) , stv ( v[ 3 ] ) , stf ( v[ 4 ] ) , stf ( v[ 5 ] ) , stf ( v[ 6 ] ) , stf ( v[ 7 ] ) , stv ( v[ 8 ] ) , stv ( v[ 9 ] ) , stv ( v[ 10 ] ) , stv ( v[ 11 ] ) , v[ 12 ] , stv ( v[ 13 ] ) , stv ( v[ 14 ] ) , stf (v[ 15 ] ) , stf ( v[ 16 ] ) , stf ( v[ 17 ] ) , stf ( v[ 18 ] ) , stv ( v[ 19 ] ) , stv ( v[ 20 ] ) , stv ( v[ 21 ] ) , stv ( v[ 22 ] ) ); // Das Haus wird erstellt, und die interior id in InteriorID gespeichert
    house++;
    CreatedHouses++;
    }
    fclose ( houses );
    return 1;
    }


    Nun das selbe nochmal, aber diesmal für die andere Datei:
    LoadHouseData ( )
    {
    new File:houses = fopen ( HOUSEDATAFILE , io_read ) , content[ 512 ];
    if ( !houses )
    return 0;
    new house;
    while ( fread ( houses , content ) )
    {
    // Parser © by me :P. Das erkläre ich nicht alles, denn das wäre 1. zu viel und 2. nicht das Thema.
    new v[ 7 ][ 64 ] , bool:stop = false , s;
    while ( stop != true )
    {
    if ( strfind ( content , "|" ) != -1 )
    {
    for ( new x; x < strfind ( content , "|" ); x++ )
    v[ s ][ x ] = content[ x ];
    strdel ( content , 0 , strfind ( content , "|" ) + 1 );
    }
    else
    {
    for ( new x; x < strlen ( content ); x++ )
    v[ s ][ x ] = content[ x ];
    stop = true;
    }
    s++;
    }
    HouseData[ house ][ sID ] = stv ( v[ 0 ] ); // Hier werden die HausData Werte beschrieben.
    HouseData[ house ][ Prize ] = stv ( v[ 1 ] ); // Hier werden die HausData Werte beschrieben.
    HouseData[ house ][ AllowRent ] = stv ( v[ 2 ] ); // Hier werden die HausData Werte beschrieben.
    format ( HouseData[ house ][ Owner ] , 64 , v[ 3 ] ); // Hier werden die HausData Werte beschrieben.
    format ( HouseData[ house ][ Tenant ] , 64 , v[ 4 ] ); // Hier werden die HausData Werte beschrieben.
    HouseData[ house ][ Rent ] = stv ( v[ 5 ] ); // Hier werden die HausData Werte beschrieben.
    HouseData[ house ][ Lockstate ] = stv ( v[ 6 ] ); // Hier werden die HausData Werte beschrieben.
    house++;
    }
    fclose ( houses );
    if ( CreatedHouses != house ) // Wenn die erzeugten Häuser mehr sind, als die Haus dazu Informationen ( id Verschiebung ect.. )
    printf ( "The Housefiles may are incorrect. Please fix it to prevent Bugs!" );
    return 1;
    }


    Und nun noch eine kleine Funktion zum Speichern:
    SaveHouseData ( )
    {
    new File:houses = fopen ( HOUSEDATAFILE , io_write ); // datei löschen
    fclose ( houses );
    houses = fopen ( HOUSEDATAFILE , io_append );
    new Line[ 128 ];
    if ( !houses )
    return 0;
    for ( new i; i < CreatedHouses + 1; i++ ) // Alle Häuser durchschleifen
    {
    format ( Line , 128 , "%d|%d|%d|%s|%s|%d|%d\r\n" , HouseData [ i ][ sID ] , HouseData[ i ][ Prize ] , HouseData[ i ][ AllowRent ] , HouseData[ i ][ Owner ] , HouseData[ i ][ Tenant ] , HouseData[ i ][ Rent ] , HouseData[ i ][ Lockstate ] ); // Neue Zeile zusammensetzen
    fwrite ( houses , Line ); // Schreiben
    }
    fclose ( houses );
    return 1;
    }


    Damit wir gleich schnell und fröhlich mit den Befehlen ect. anfangen können, erstellen wir uns ein paar Funktionen, die uns das Leben leichter machen:
    GetHouseIndex ( interior ) // Gibt den Index vom House Array, beidem InterioID das angegebende Interior ist
    {
    for ( new i; i < CreatedHouses + 1; i++ ) // Alle erzeugten Häuser durchschleifen
    if ( House[ i ][ InteriorID ] == interior ) // Wenn InteriorID gleich interior ist, gieb den Index zurück
    return i;
    return -1;
    }
    GetHouseDataIndex ( interior ) // Gibt den Index vom HouseData Array. Gleiche funktionsweise wie oben
    {
    new saveID = House[ GetHouseIndex ( interior ) ][ sID ];
    for ( new i; i < CreatedHouses + 1; i++ )
    if ( HouseData[ i ][ sID ] == saveID )
    return i;
    return -1;
    }
    GetPlayerHouse ( playerid ) // Gibt den Index vom HouseData Array zurück, wenn dem Spieler ein Haus gehört.
    {
    new PlayerName[ MAX_PLAYER_NAME ];
    GetPlayerName ( playerid , PlayerName , MAX_PLAYER_NAME );
    for ( new i; i < CreatedHouses; i++ ) // Alle Häuser durchschleifen
    if ( !strcmp ( HouseData[ i ][ Owner ] , PlayerName ) || !strcmp ( HouseData[ i ][ Tenant ] , PlayerName ) ) // Wenn der Spieler Mieter oder Besitzer ist, gib den index zurücl
    return i;
    return -1;
    }


    Nun kommen wir zu dem spannenden Teil des Tutorials. Folgende 4 Callbacks müssen in´s Script kopiert werden:
    public OnPlayerEnterInterior(playerid ,interior ,method)
    {
    return 1;
    }


    public OnPlayerEnteredInterior(playerid ,interior ,method)
    {
    return 1;
    }


    public OnPlayerLeaveInterior(playerid, interior, method)
    {
    return 1;
    }


    public OnPlayerLeavedInterior(playerid, interior, method)
    {
    return 1;
    }


    Damit die Variable ActualHouse auch einen Sinn hat, und nützlich wird, müssen wir sie beim Hineinkommen in ein Haus auf dessen InteriorID setzen. Beim verlassen wieder auf -1. Das geht mit den Callbacks OnPlayerEnteredIntrerior und OnPlayerLeavedInterior. Beide werden aufgerufen, wenn ein Spieler ein Haus betritt oder verlässt.
    public OnPlayerEnteredInterior(playerid ,interior ,method)
    {
    ActualHouse[ playerid ] = interior;
    return 1;
    }
    public OnPlayerLeavedInterior(playerid, interior, method)
    {
    ActualHouse[ playerid ] = -1;
    return 1;
    }
    Nun kommen wir zu dem Teil, beidem der Spieler versucht ein Haus zu betreten. Dort müssen wir überprüfen ob...

    • ... das Haus eines von unseren erstellten ist
    • ... es verschlossen ist
    • ... der Spieler der Besitzer ist
    • ... der Spieler der Mieter ist
    • ... das Haus verkauft ist
    • ... das Haus einen Mieter hat
    • ... Ob das Haus zu vermieten ist


    Das alles geschieht so:
    public OnPlayerEnterInterior(playerid ,interior ,method) // Wenn der Spieler versucht ein Haus zu betreten
    {
    new HouseIndex = GetHouseIndex ( interior ) , HouseDataIndex = GetHouseDataIndex ( interior ) , PlayerName[ MAX_PLAYER_NAME ]; // Alle Variablen erstellen, und einige vordefinieren
    GetPlayerName ( playerid , PlayerName , MAX_PLAYER_NAME );
    if ( HouseIndex != -1 && HouseDataIndex != -1 ) // Wenn das Haus kein Haus aus diesem System ist
    if ( HouseData[ HouseDataIndex ][ Lockstate ] == 0 ) // Prüfen ob das Haus abgeschlossen ist
    {
    if ( strcmp ( PlayerName , HouseData[ HouseDataIndex ][ Owner ] ) && strcmp ( PlayerName , HouseData[ HouseDataIndex ][ Tenant ] ) ) // Wenn die Person nicht Mieter oder Besitzer ist
    {
    new string[ 128 ];
    if ( strcmp ( UNALLOWED_NAME , HouseData[ HouseDataIndex ][ Owner ] ) ) // Wenn das Haus verkauft ist
    if ( !strcmp ( UNALLOWED_NAME , HouseData[ HouseDataIndex ][ Tenant ] ) ) // Wenn das Haus nicht vermietet ist
    if ( HouseData[ HouseDataIndex ][ AllowRent ] == 0 ) // Wenn man nicht einmieten kann
    format ( string , 128 , "Dieses Haus gehört %s." , HouseData[ HouseDataIndex ][ Owner ] );
    else // Wenn man einmieten kann
    format ( string , 128 , "Dieses Haus gehört %s. Hier kannst du für %d$ einmieten! Benutze \"renthouse\"" , HouseData[ HouseDataIndex ][ Owner ] , HouseData[ HouseDataIndex ][ Rent ] );
    else // Wenn es vermietet und verkauft ist
    format ( string , 128 , "Dieses Haus gehört %s. Mieter ist %s." , HouseData[ HouseDataIndex ][ Owner ] , HouseData[ HouseDataIndex ][ Tenant ] );
    else // Wenn das Haus zu verkaufen ist
    format ( string , 128 , "Dieses Haus ist für %d$ zu verkaufen. Zum Kaufen benutze \"/buyhouse\"" , HouseData[ HouseDataIndex ][ Prize ] );
    SendClientMessage ( playerid , 0x666666AA , string );
    }
    }
    else // Wenn das Haus abgeschlossen ist
    {
    SendClientMessage ( playerid , 0x666666AA , "Dieses Haus ist abgeschlossen!" );
    return 0; // Spieler nicht hinein lassen
    }
    return 1;
    }


    Nun müssen wir bei OnPlayerCommandText nur noch die Befehle für das Auf- und Abschließen, um das Haus zu kaufen, und um sich einzumieten erstellen.
    Haus auf- und abschließen

    if ( !strcmp ( cmdtext , "/lockhouse" ) )
    {
    new HouseDataIndex = GetPlayerHouse ( playerid );
    if ( HouseDataIndex != -1 ) // Wenn der Index gültig ist
    if ( HouseData[ HouseDataIndex ][ Lockstate ] == 0 ) // Wenn das Haus aufgeschlossen ist
    {
    HouseData[ HouseDataIndex ][ Lockstate ] = 1; // Türstatus auf "geschlossen"
    SendClientMessage ( playerid , 0x666666AA , "Du hast das Haus abgeschlossen!" );
    }
    else // Wenn das Haus abgeschlossen ist
    {
    HouseData[ HouseDataIndex ][ Lockstate ] = 0; // Türstatus auf "offen"
    SendClientMessage ( playerid , 0x666666AA , "Du hast das Haus aufgeschlossen!" );
    }
    else // Wenn der Index ungültig ist. -> Spieler besitzt kein Haus
    SendClientMessage ( playerid , 0x666666AA , "Du besitzt kein Haus!" );
    }
    Haus kaufen
    if ( !strcmp ( cmdtext , "/buyhouse" ) )
    {
    new HouseDataIndex = GetHouseDataIndex ( ActualHouse[ playerid ] ) , PlayerName[ MAX_PLAYER_NAME ] , string[ 128 ];
    GetPlayerName ( playerid , PlayerName , MAX_PLAYER_NAME );
    if ( ActualHouse[ playerid ] != -1 ) // Wenn sich der Spieler in einem Haus befindet
    {
    if ( GetPlayerHouse ( playerid ) != -1 ) // Wenn der Spieler ein eigenes Haus besitzt
    return SendClientMessage ( playerid , 0x666666AA , "Du besitzt schon ein Haus!" );
    if ( !strcmp ( HouseData[ HouseDataIndex ][ Owner ] , UNALLOWED_NAME ) ) // Wenn das Haus verkaufbar ist
    {
    if ( HouseData[ HouseDataIndex ][ Prize ] < _GetPlayerMoney ( playerid ) ) // Prüfen ob der Spieler genug Geld besitzt
    {
    _GivePlayerMoney ( playerid , 0 - HouseData[ HouseDataIndex ][ Prize ] ); // Wert negativieren, und Geld abziehen
    format ( HouseData[ HouseDataIndex ][ Owner ] , MAX_PLAYER_NAME , PlayerName ); // Neuen Besitzer eintragen
    format ( string , 128 , "Du hast die dieses Haus für %d$ gekauft!" , HouseData[ HouseDataIndex ][ Prize ] );
    }
    else // Wenn der Spieler nicht genug Geld besitzt
    format ( string , 128 , "Du hast nicht genug Geld für dieses Haus. Du benötigst %d$!" , HouseData[ HouseDataIndex ][ Prize ] );
    return SendClientMessage ( playerid , 0x666666AA , string );
    }
    else // Wenn das Haus nicht zu verkaufen ist
    return SendClientMessage ( playerid , 0x666666AA , "Dieses Haus ist nicht zu verkaufen!" );
    }
    else // Wenn sich der Spieler in keinem Haus befindet
    return SendClientMessage ( playerid , 0x666666AA , "Du befindest dich in keinem Haus!" );
    }
    Haus mieten
    if ( !strcmp ( cmdtext , "/renthouse" ) )
    {
    new HouseDataIndex = GetHouseDataIndex ( ActualHouse[ playerid ] ) , PlayerName[ MAX_PLAYER_NAME ] , string[ 128 ];
    GetPlayerName ( playerid , PlayerName , MAX_PLAYER_NAME );
    if ( ActualHouse[ playerid ] != -1 )
    {
    if ( GetPlayerHouse ( playerid ) != -1 ) // Wenn der Spieler ein Haus besitzt
    return SendClientMessage ( playerid , 0x666666AA , "Du wohnst schon in einem Haus!" );
    if ( !strcmp ( HouseData[ HouseDataIndex ][ Tenant ] , UNALLOWED_NAME ) && HouseData[ HouseDataIndex ][ AllowRent ] && strcmp ( HouseData[ HouseDataIndex ][ Owner ] , PlayerName ) ) // Wenn das Haus einen Besitzer hat, und keinen Mieter hat
    {
    format ( HouseData[ HouseDataIndex ][ Tenant ] , MAX_PLAYER_NAME , PlayerName ); // Spielername als neuen Mieter eintragen
    format ( string , 128 , "Du bist nun Untermieter dieses Haus für %d$!" , HouseData[ HouseDataIndex ][ Rent ] );
    }
    else // Wenn das Haus nicht verkauft, oder schon vermietet ist
    return SendClientMessage ( playerid , 0x666666AA , "Du kannst hier nicht einmieten." );
    }
    else // Wenn sich der Spieler in keinem Haus befindet
    return SendClientMessage ( playerid , 0x666666AA , "Du befindest dich in keinem Haus!" );
    }
    Nun haben wir so einfach und schnell schon alle nützlichen Befehle erstellt! Weitere Befehle wie /unrenthouse oder /sellhouse können so ganz leicht nachgemacht werden.


    Damit das ganze System auch lädt und speichert, müssen wir nun nur noch unter OnGameModeInit laden und unter OnGameModeExit speichern lassen.
    public OnGameModeInit()
    {
    LoadHouses ( );
    LoadHouseData ( );
    return 1;
    }


    public OnGameModeExit()
    {
    SaveHouseData ( );
    return 1;
    }


    Nun fehlt allerdings noch der Punkt, andem der Spieler die Miete bezahlen muss.
    Ich habe nun keinen Payday oder sonstiges gecodet, weil es nicht zum Tutorial dazu gehört.
    Allerdings habe ich schon eine Funktion angefangen, mitder ein Payday oder derartiges einfach möglich ist zu erstellen.
    stock HandleAllRents ( )
    {
    for ( new i; i < CreatedHouses + 1; i++ )
    {
    if ( HouseData[ GetHouseDataIndex ( i ) ][ RentAllowed ] && strcmp ( HouseData[ GetHouseDataIndex ( i ) ][ Tenant ] , UNALLOWED_NAME ) )
    {
    new Houseowner = GetPlayeridByName ( HouseData[ GetHouseDataIndex ( i ) ][ Owner ] ); // Hausbesitzer
    new Tenant = GetPlayeridByName ( HouseData[ GetHouseDataIndex ( i ) ][ Tenant ] ); // Hausmieter
    }
    }
    }
    Diese Funktion benötigt noch eine weitere sehr simple Funktion, die ich nicht weiter erklären werde:

    stock GetPlayeridByName ( name[ ] )
    {
    new TempName[ MAX_PLAYER_NAME ];
    for ( new i; i < MAX_PLAYERS; i++ )
    {
    GetPlayerName ( i , TempName , MAX_PLAYER_NAME );
    if ( strcmp ( TempName , name ) )
    return i;
    }
    }


    Das gesamte Ergebniss als Gamemode könnte das in etwa so aussehen: http://pastebin.com/8s7tut1r


    Ich hoffe ich habe euch geholfen.
    Feedback erwünscht.

    Hallo Leute,
    ich habe beschlossen ein Tutorial über das erstellen eines Haussystems zu machen.
    Dies ist kein Scripting-Basics Tutorial, und verlang mindestens Grundwissen und ein funktionierendes Gehirn um es zu verstehen!
    Fangen wir mal an...


    Das Ziel:
    Das Ziel dieses Tutorials ist es, am Ende ein Haussystem zu haben, das Häuser automatisch beim Spielstart erstellt. Spieler sollen die Häuser kaufen, vermieten und abschliessen können. Dabei soll das System möglichst wenig Arbeitsaufwand haben. Natürlich soll das Script nur so wenig Ressourcen brauchen, wie es machbar ist.


    Der Weg:
    Um unser Ziel zu verwirklichen werden wir 2 Dateien erstellen. In der einen Datei werden die Häuser zeilenweise gespeichert. In der anderen Datei werden wir das "Setup" des Hauses speichern. Also alle Informationen, wie z.B. Besitzer, Mieter, Miete ect..
    Um das ganze möglichst komfortabel zu gestalten verwenden wir das Filterscript h_interiors. Dies erweitert die SA:MP um 6 neue Callbacks und Funktionen.


    Die Dateien:
    .HOUSES - Datei
    Die Datei für die Häuser wird in etwa das Layout wie die AddInteriors() Funktion in h_interiors bekommen. Jedoch mit einem "|" als Zwischenzeichen, und einer ID am Anfang. Die ID dient der Zuordnung, damit wir die Hauskonfiguration aus der 2. Datei auch einem Haus zuordnen können.
    So würde eine solche Datei dann aussehen:

    Code
    1|Welcome home|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES
    2|Welcome Home|NULL|0|1959.484252|1340.293334|15.374607|1.088764|NULL|NULL|NULL|YES|Bye|NULL|2|225.111801|1240.140380|1082.140625|96.546875|NULL|NULL|NULL|YES


    Die ID am Anfang ist fortlaufend. (So wie man es z.B. von MySQL her kennt.
    .HOUSEDATA - Datei
    Die Housedata Datei ist eine viel kleinere Datei. In ihr wird nach folgendem Layout gespeichert:

    Code
    ID|KAUFPREIS|VERMIETBAR|BESITZER|MIETER|MIETE|SCHLOSS


    Somit könnte eine solche Datei so aussehen:

    Code
    1|2100|1|-1NOBODY-1|-1NOBODY-1|100|0
    2|12000|1|-1NOBODY-1|1NOBODY-1|100|0


    Diese 2 Datein legen wir nun mit Hilfe von der Windows Konsole an. Benutzer anderer Systeme werden schon wissen, wie sie das am einfachstem machen...
    Wir öffnen die Windows Konsole.
    Windows XP Benutzer:
    Start -> Ausführen -> cmd eingeben -> Enter drücken
    Windows Vista & 7 Benutzer:
    Start -> cmd eingeben -> Enter drücken
    Nun gebt ihr folgende Zeilen ein:

    Code
    cd Desktop
    dir > [filename].HOUSES
    dir > [filename].HOUSEDATA


    Danach können wir die Konsole schließen, und die 2 neuen Dateien auf dem Desktop in den Ordner Scriptfiles kopieren.


    Häuser erstellen:
    Am besten erstellen wir die Häuser mit dem AddInteriors Generator von h_interiors. Die Beschreibung und Bedienung mit einem Video sind hier zu finden. Wir müssen nun lediglich den generierten Funktionsaufruf ( AddInterior(....) ) umschreiben. Das machen wir, indem wir alles außer der Parameter entfernen, und zwischen die Parameter ein "|" einfügen.
    Also wird aus:

    Code
    AddInterior ( "Welcome home!" , NULL , 0 , 1953.704956 , 1342.881958 , 15.374607 , 86.697219 , NULL , NULL , NULL , YES , "byee" , NULL , 3 , 235.407196 , 1187.490844 , 1080.257812 , 83.480270 , NULL , NULL , NULL , YES );


    das:

    Code
    Welcome home!|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES


    Zum Schluss fügen wir vorne noch die ID ein. Dann wird daraus das:

    Code
    1|Welcome home!|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES


    In der HOUSES Datei sähe das mit mehreren Häusern so aus:

    Code
    1|Welcome home!|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES
    2|Welcome home!|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES
    3|Welcome home!|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES
    4|Welcome home in your Villa|NULL|0|1953.704956|1342.881958|15.374607|86.697219|NULL|NULL|NULL|YES|byee|NULL|3|235.407196|1187.490844|1080.257812|83.480270|NULL|NULL|NULL|YES


    --- Optional!
    Um mit dem AddInterior Generator direkt dieses Layout zu erstellen, muss in h_interiors mit AddInterior Generator die Zeile 543 mit dieser:
    format ( function , 1024 , "%s|%s|%d|%f|%f|%f|%f|NULL|NULL|NULL|%s" , p[ playerid ][ iM ] , VWorld , p[ playerid ][ iIID ] , p[ playerid ][ iX ] , p[ playerid ][ iY ] , p[ playerid ][ iZ ] , p[ playerid ][ iA ] , AK );
    und die Zeile 553 mit dieser Zeile ersetzt werden!
    format ( temp_function , 512 , "|%s|%s|%d|%f|%f|%f|%f|NULL|NULL|NULL|%s" , p[ playerid ][ oM ] , VWorld , p[ playerid ][ oIID ] , p[ playerid ][ oX ] , p[ playerid ][ oY ] , p[ playerid ][ oZ ] , p[ playerid ][ oA ] , AK );
    ---



    Das Scripting (endlich):
    Als erstes empfieht es sich die h_interiors include und ein paar defines und kleine Makros einzufügen:
    #include <h_interiors> // h_interiors include einbinden
    #define MAX_HOUSES 10 // Anzahl maximaler Häuser
    #define HOUSEFILE "tutorial.HOUSES" // Datei aller Häuser
    #define HOUSEDATAFILE "tutorial.HOUSEDATA" // Datei der Hausinformationen
    #define stf(%0) floatstr(%0) // Kürzeres floatstr
    #define stv(%0) strval(%0) // Kürzeres strval
    #define UNALLOWED_NAME "-1NOBODY-1" // Ein Spielername dient als Platzhalter.
    #define _GetPlayerMoney(%0) GetPlayerMoney(%0) // Kann geändert werden ( bei z.B. Anticheat System )
    #define _GivePlayerMoney(%0,%1) GivePlayerMoney(%0,%1) // Kann geändert werden ( bei z.B. Anticheat System )


    Nun legen wir ein paar Variablen an, die wir benötigen werden:
    enum houseinfo // Mehr muss von den Häusern nicht gespeichert werden, dank h_interiors.
    {
    sID, // sID wird die ID aus den Datein
    InteriorID // InteriorID ist die ID, die von AddInterior zurückgegeben wird.
    }


    enum housedata // Hier befinden sich die ganzen Hausinformationen
    {
    sID, // sID wird die ID aus den Datein
    Prize, // Haus Kaufpreis
    AllowRent, // Mieten erlauben 0 = Nein , 1 = Ja
    Owner[ MAX_PLAYER_NAME ], // Besitzername
    Tenant[ MAX_PLAYER_NAME ],// Mietername
    Rent, // Mietkosten
    Lockstate // Türschloss 0 = Offen , 1 = Verschlossen
    }


    new HouseData[ MAX_HOUSES ][ housedata ];
    new House[ MAX_HOUSES ][ houseinfo ];
    new ActualHouse[ MAX_PLAYERS ];// Interior, indem sich der Spieler befindet
    new CreatedHouses; // Anzahl erzeugter Häuser


    Nun sollten wir als erstes prüfen, ob ein Spieler mit unerlaubtem Namen verdinden möchte. Das geschieht so:
    public OnPlayerConnect(playerid)
    {
    new name[ MAX_PLAYER_NAME ];
    GetPlayerName ( playerid , name , MAX_PLAYER_NAME );
    if ( !strcmp ( name , UNALLOWED_NAME ) )
    {
    SendClientMessage ( playerid , 0x666666AA , "Es tut uns leid, aber dieser Name ist nicht zulässig." );
    Kick ( playerid );
    }
    return 1;
    }Darauf gehe ich nicht detailierter ein, denn das sollte wohl klar sein.



    Ich musste das Tutorial leider in 2 Teile teilen...
    also wartet auf Teil 2 :D

    Unabhängig vom Empfang hast du als Client (je nach Anzahl der Mitspieler) 2-5kb/s Datenvolumen.


    Wenn dem so wäre, könnte er auch mit Tempodrosselung spielen. Demnach würde das Überschreiten des Volumens keine Auswirkung haben.
    Allerdings würde dein Ping immer recht hoch sein. Ich schätze mal so 150 - 400ms.

    new OnlinePlayers;
    public OnPlayerSpawn ( playerid )
    {
    if ( !IsPlayerNPC ( 0 ) )
    {
    OnlinePlayers++;
    new File:rekord = fopen ( "rekord.txt" , io_read ) , cnt[ 6 ];
    fread ( rekord , cnt );
    if ( strval ( cnt ) < OnlinePlayers )
    {
    new ncnt[ 6 ] , msg[ 64 ];
    fclose ( rekord );
    rekord = fopen ( "rekord.txt" , io_write );
    format ( ncnt , 6 , "%d" , OnlinePlayers );
    fwrite ( rekord , ncnt );
    format ( msg , 64 , "Der aktuelle Rekord von %d Spielern wurde gebrochen. ( Aktuell %d Spieler online )" , strval ( cnt ) , strval ( ncnt ) );
    SendClientMessageToAll ( 0x666666AA , msg );
    }
    fclose ( rekord );
    }
    }


    public OnPlayerDisconnect(playerid, reason)
    {
    OnlinePlayers--;
    }


    jup, über einen kleinen Umweg:

    #define vagosspawn 180,-2183.0176,957.6070,80.0000
    SetSpawn ( playerid , skin , Float:X , Float:Y , Float:Z )
    {
    SetPlayerPos ( playerid , X , Y , Z );
    SetPlayerSkin ( playerid , skin );
    }


    public OnPlayerSpawn ( playerid )
    {
    SetSpawn ( playerid , vagosspawn );
    }

    ocmd:setzeitung(playerid, params[])
    {
    if(!isPlayerAnAdmin(playerid,2))
    {
    SendClientMessage (playerid,GRAU,"Du darfst diesen Befehl nicht benutzen!");
    }
    new pIN[128];
    if(sscanf(params,"s",pIN))
    {
    SendClientMessage(playerid,GRAU,"Benutze: /setzeitung [Inhalt]");
    }
    format(zeitungdatei,sizeof(zeitungdatei),"/ACCS/zeitung.ini");
    dini_Create(zeitungdatei);
    dini_Set(zeitungdatei,"Wetter",pIN);
    return 1;
    }


    Ich kenne dini nicht aus dem Kopf. Aber irgendwie so sollte es gehen.

    Wenn ich mir das so anschaue, sieht es aus, als möchtest du einen string speichern.
    dann mit pIN ein Array werden, und dann wird auch die Funktion dini_Int falsch sein.