[Tutorial] Making A Simple Hunger/Thirst System
#1

Simple Hunger/Thirst System
Tutorial by Voxel

Introduction
Hello and welcome to my new tutorial on how to make a hunger and thirst system. In this tutorial we
will be using SQLite to save the players stats, if you are not familiar with SQLite I reccomend taking a look
at my SQLite filterscript and the tutorial which is made by Konstatinos.
This tutorial will expect you to know the basics for pawn scripting.
Lets get going shall we?!

Tutorial
Setting up our database to save our stats

First of all we are going to need some enums for our stats
pawn Code:
enum USER_DATA
{
    USER_ID, //here we save the account id in the database
    USER_NAME[MAX_PLAYER_NAME], //same goes for the name
    USER_PASSWORD[129], //and we save the password
    USER_HUNGER, //here we save the players hunger
    USER_THIRST, //and the thirst aswell

    bool: USER_LOGGED_IN
};

new User[MAX_PLAYERS][USER_DATA];
new DB: Database;
Next we will set up the rest of the database
pawn Code:
public OnFilterScriptInit()
{
    Database = db_open("server.db");
    db_query(Database, "CREATE TABLE IF NOT EXISTS users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(24) COLLATE NOCASE, password VARCHAR(129), hunger INTEGER DEFAULT 0 NOT NULL, thirst INTEGER DEFAULT 0 NOT NULL)");
    AddPlayerClass(1, 2528.9143,-1667.7504,15.1689,91.2860,0,0,0,0,0,0);
    return 1;
}

public OnPlayerConnect(playerid)
{
    for(new i; i < _: USER_DATA; ++i) User[playerid][USER_DATA: i] = 0;

    GetPlayerName(playerid, User[playerid][USER_NAME], MAX_PLAYER_NAME);

    new Query[71], DBResult: Result;
    format(Query, sizeof(Query), "SELECT password FROM users WHERE username = '%s' LIMIT 0, 1", DB_Escape(User[playerid][USER_NAME]));
    Result = db_query(Database, Query);
    if(db_num_rows(Result))
    {
        db_get_field_assoc(Result, "password", User[playerid][USER_PASSWORD], 129);
        ShowPlayerDialog(playerid, 0, DIALOG_STYLE_PASSWORD, "Login pannel", "Login pannel", "Please login to play", "Login", "Exit");
    }
    else ShowPlayerDialog(playerid, 1, DIALOG_STYLE_PASSWORD, "Register pannel", "Please register to play", "Register", "Exit");
    db_free_result(Result);
    return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
    if(User[playerid][USER_LOGGED_IN] == true)
    {
        new Query[128];
        format(Query, sizeof(Query), "UPDATE users SET hunger= %s, thirst = %s WHERE username = '%s'", User[playerid][USER_HUNGER], User[playerid][USER_THIRST], DB_Escape(User[playerid][USER_NAME]));
        db_query(Database, Query);
    }
    for(new i; i < _: USER_DATA; ++i) User[playerid][USER_DATA: i] = 0;
    return 1;
}

public OnPlayerSpawn(playerid)
{
    return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
    return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if(dialogid == 0)
    {
        if(response)
        {
            if(!inputtext[0]) return ShowPlayerDialog(playerid, 0, DIALOG_STYLE_PASSWORD, "Login pannel", "Please login to play", "Login", "Exit");
            new buf[129];
            WP_Hash(buf, 129, inputtext);
            if(!strcmp(buf, User[playerid][USER_PASSWORD], false))
            {
                new Query[75], DBResult: Result;
                format(Query, sizeof(Query), "SELECT * FROM users WHERE username = '%s' LIMIT 0, 1", DB_Escape(User[playerid][USER_NAME]));
                Result = db_query(Database, Query);
                if(db_num_rows(Result))
                {
                    db_get_field_assoc(Result, "userid", Query, 7);
                    User[playerid][USER_ID] = strval(Query);

                    db_get_field_assoc(Result, "hunger", Query, 3);
                    User[playerid][USER_HUNGER] = strval(Query);

                    db_get_field_assoc(Result, "thirst", Query, 3);
                    User[playerid][USER_THIRST] = strval(Query);

                    User[playerid][USER_LOGGED_IN] = true;

                    SendClientMessage(playerid, -1, "You have successfully logged in to your account!");
                }
                db_free_result(Result);
            }
            else
            {
                SendClientMessage(playerid, -1, "Incorrect password!");
                ShowPlayerDialog(playerid, 0, DIALOG_STYLE_PASSWORD, "Login pannel", "Please login to play", "Login", "Exit");

            }
        }
        else Kick(playerid);
        return 1;
    }
    if(dialogid == 1)
    {
        if(response)
        {
            if(!IsValidPassword(inputtext))
            {
                SendClientMessage(playerid, -1, "The password is invalid, Valid characters are: A-Z, a-z, 0-9");
                ShowPlayerDialog(playerid, 1, DIALOG_STYLE_PASSWORD, "Register pannel", "Please register to play", "Register", "Exit");
                return 1;
            }
            if(strlen(inputtext) < 3 || strlen(inputtext) > 24)
            {
                SendClientMessage(playerid, -1, "The password is invalid, Its lenght should be 3-24 characters");
                ShowPlayerDialog(playerid, 1, DIALOG_STYLE_PASSWORD, ""COL_BLUE"SERVER NAME"COL_WHITE" Register pannel", "Please register to play", "Register", "Exit");
                return 1;
            }
            new Query[208];
            WP_Hash(User[playerid][USER_PASSWORD], 129, inputtext);
            format(Query, sizeof(Query), "INSERT INTO users (username, password) VALUES ('%s', '%s')", DB_Escape(User[playerid][USER_NAME]), DB_Escape(User[playerid][USER_PASSWORD]));
            db_query(Database, Query);

            User[playerid][USER_LOGGED_IN] = true;
            SendClientMessage(playerid, -1, "You have just registered to our server! You have been automatically logged in!");
        }
        else Kick(playerid);
        return 1;
    }
    return 1;
}

stock DB_Escape(text[])
{
    new ret[ 80 * 2 ], ch, i, j;
    while((ch = text[i++]) && j < sizeof(ret))
    {
        if(ch == '\'')
        {
            if(j < sizeof(ret) - 2)
            {
                ret[j++] = '\'';
                ret[j++] = '\'';
            }
        }
        else if(j < sizeof(ret))
        {
            ret[j++] = ch;
        }
        else
        {
            j++;
        }
    }
    ret[sizeof(ret) - 1] = '\0';
    return ret;
}

stock IsValidPassword(const password[])
{
    for(new i = 0; password[i] != EOS; ++i)
    {
        switch(password[i])
        {
            case '0'..'9', 'A'..'Z', 'a'..'z': continue;
            default: return 0;
        }
    }
    return 1;
}
Now that we have a database to save our hunger and thirst in we can begin the real tutorial!
Right so first we will set the player stats when he first registers to the server:
pawn Code:
//this is our register part of the script over at the dialogs
User[playerid][USER_LOGGED_IN] = true;
SendClientMessage(playerid, -1, "You have just registered to our server! You have been automatically logged in!");
User[playerid][USER_HUNGER] == 100); //we set the hunger to 100
User[playerid][USER_THIRST] == 100); //and the thirst to 100
Now that we set the values correctly we can begin making the functions to decrease these stats
an example would be:
pawn Code:
//you need this define on top of your script to detect keys properly
#define PRESSED(%0) \
    (((newkeys & (%0)) == (%0)) && ((oldkeys & (%0)) != (%0)))

//everytime you jump you will loose a bit of energy which means u will get more hungry and thirsty
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
     if(PRESSED(KEY_JUMP))
     {
          User[playerid][USER_HUNGER] -= 5); //we take 5 of the players hunger
          User[playerid][USER_THIRST] -= 7); //and 7 of the thirst
     }
     return 1;
}
Now that we have something that decreases our hunger and thirst we need to detect if the hunger or thirst is getting to low and the player will loose some health and maybe you could
also disable his sprinting since you would have no energy for that. So it would look like this:
pawn Code:
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
     if(PRESSED(KEY_JUMP))
     {
          if(User[playerid][USER_HUNGER] <= 10 || User[playerid][USER_THIRST] <= 10)
          {
               SendClientMessage(playerid, -1, "You do not have enough energy to jump, try eating and drinking"); //send a message informing the player his energy is to low
               new Float:X, Float:Y, Float:Z;
               GetPlayerPos(playerid, x, y, z);
               SetPlayerPos(playerid, x, y, z);
               //just a quick way to disable the jumping (not tested)
          }
          else
          {
               //if the player have larger hunger and thirst then 10
               User[playerid][USER_HUNGER] -= 5); //we take 5 of the players hunger
               User[playerid][USER_THIRST] -= 7); //and 7 of the thirst
          }
     }
     return 1;
}
Now we will need a way to eat and drink to increase our hunger and thirst, you can use this at your
code where you buy food and such:
pawn Code:
//example
if(listitem == 0) //asuming you have a dialog and this is list item 0 which would be a burger or something
{
     User[playerid][USER_HUNGER] += 20); //we give the player +20 hunger
     SendClientMessage(playerid, -1, "You ate a burger, +20 hunger");
}
if(listitem == 1) //and this would be a soda or such
{
     User[playerid][USER_THIRST] += 20); //we give the player +20 thirst
     SendClientMessage(playerid, -1, "You drank a soda, +20 thirst");
}
Allright so now we will make a /energy command to check how hungry and thirsty you are.
pawn Code:
//Using ZCMD
CMD:energy(playerid,params[])
{
    new hunger= User[playerid][USER_HUNGER]; //we make a variable to store our stats in
    new thirst= User[playerid][USER_THIRST]; //same here
    new string[60],stats[60];
       
    format(string, sizeof string, "Hunger: %s\nThirst: %s", hunger, thirst); //we display the stats in our dialog
    format(stats, sizeof stats, "%s", string);
    ShowPlayerDialog(playerid,6,DIALOG_STYLE_MSGBOX,"Energy",string,"Ok","");
    return 1;
}
Thats about it, you can make more features like a timer every 10 minutes that decreases the hunger and thirst
and when you get low on energy you loose some health etc.

I hope you learned something and have fun!
Reply
#2

Very nice indeed +1
Reply
#3

You shouldn't really have to this at all just make sure all settings are set when a player logs in or registers.

pawn Code:
for(new i; i < _: USER_DATA; ++i) User[playerid][USER_DATA: i] = 0;
Reply
#4

Quote:
Originally Posted by [uL]Pottus
View Post
You shouldn't really have to this at all just make sure all settings are set when a player logs in or registers.

pawn Code:
for(new i; i < _: USER_DATA; ++i) User[playerid][USER_DATA: i] = 0;
That would work for regular player stats such as kills and deaths but not with hunger and thirst in this system where they are set to 100 which would mean your stomach is full etc.

But thanks for your reply!
Reply
#5

Well you would just load them from your database when a player joins that is what I do on the DayZ server so you can't tell me it wouldn't work
Reply
#6

Quote:
Originally Posted by [uL]Pottus
View Post
Well you would just load them from your database when a player joins that is what I do on the DayZ server so you can't tell me it wouldn't work
Oh lol okay !
Reply
#7

Seems legit... Gonna use it for my gamemode...
Reply
#8

Quote:
Originally Posted by twerko13
View Post
Seems legit... Gonna use it for my gamemode...
Good luck!
Reply
#9

It works, even used this as a base for my admin system.... Gonna put you in my server credits (visible to everyone on the server)!
Reply
#10

Quote:
Originally Posted by twerko13
View Post
It works, even used this as a base for my admin system.... Gonna put you in my server credits (visible to everyone on the server)!
Heh glad it works and enjoy! thanks.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)