[Tutorial] [Advanced]How to mimic Object-Oriented Programming in PAWN
#1

Object-Oriented Programming in PAWN


What is Object-Oriented Programming

Quote:

Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which are data structures that contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods. A distinguishing feature of objects is that an object's procedures can access and often modify the data fields of the object with which they are associated (objects have a notion of "this" or "self").

Quote from Wikipedia https://en.wikipedia.org/wiki/Object...ed_programming

If you are totally new to object-oriented programming I suggest you to read the whole wikipedia article.

Implementing it in PAWN

PAWN is not an object-oriented programming language, however it has a very useful feature that I will be using in this tutorial: tags.

Probably every PAWN scripter, even a beginner, knows the most used tag: Float:, which SA-MP native functions use for the 3D positioning system, health, etc.
However not everybody knows that you can define your own tags, and in fact this is a feature which is not very used by PAWN coders.
My PAWN class model is based on a few simple macro definitions and the use of tags (to prevent calling of methods on an object of a different class).

Downsides
  • No inheritance as well as no interfaces
  • No visibility setting (private, public, protected)
  • No constructor/method overloading (if you want to implement it, read this)
  • Only static-like syntax (e.g. ClassName.method(object, arg) instead of object.method(arg))


Let's get started!
PHP код:
/******************** class ClassExample *******************/
#define ClassExample. ClassExample_
#define ClassExample_max ClassExample:1024 
First, we want to keep a class clearly divided from other code to keep it tidy and to avoid confusion, so add a comment or something with the class name.
Then add these two definitions:
ClassExample. will now be precompiled as ClassExample_ to avoid compiler errors (as . is an undefined symbol).
Then we define the maxium number of instances for this class.


Fields
PHP код:
// Fields
stock ClassExample.myInteger[ClassExample.max];
stock Float:ClassExample.myFloat[ClassExample.max];
stock ClassExample.myString[ClassExample.max][64]; 
Let's define some class fields: we will use arrays for that (if we wanted to create a static field, then we wouldn't have used the [ClassExample.max] part).

Note: I used the "stock" keyword instead of "new" simply to avoid compiler warnings if later we aren't using those variables. I will always use "stock" vars and functions in this tutorial.


Constructor
PHP код:
// Constructor
stock ClassExample:ClassExample(iFloat:fs[])
{
    static 
ClassExample:this ClassExample:-1;
    
this++;
    
//
    
ClassExample.myInteger[this] = i;
    
ClassExample.myFloat[this] = f;
    
format(ClassExample.myString[this], 64s);
    
//
    
return this;

Let's create the constructor!
As before, we define it as a stock function.
First of all, we use the ClassExample: tag to make it return a value with that tag.
Then we call the constructor "ClassExample". You can call it whatever you like, but if you are tidy you will use the same constructor name for all your classes, for example I will always call it <NameOfTheClass>.
Then let's add some arguments to the constructor, in this example, an integer, a float and a string.
Inside the constructor we create a static local variable tagged ClassExample: to keep the instance (object) identifier, we initialize it to -1, then increment it. Every instance of ClassExample will now have a different id.
Finally, let's set the object fields to the arguments of the constructor and return the object handler.

Methods
PHP код:
// Methods
stock ClassExample.toString(ClassExample:this)
{
    new 
ret[128];
    
format(
        
ret
        
128
        
"ClassExample %d\n\t\
        myInteger: %d\n\t\
        myFloat: %.4f\n\t\
        myString: %s\n\n"

        
_:this// _: removes the ClassExample: tag, transforming it to a printable integer
        
ClassExample.myInteger[this],
        
ClassExample.myFloat[this],
        
ClassExample.myString[this]
    );
    return 
ret;

For the sake of tidiness, all the non-static methods that we create, will have as first parameter the object handler, and we will call it "this". In other programming languages, such as Java, the object on which the method is called is implicit, but in PAWN it's difficult to achieve (if you have suggestions please comment below).
This method, called "toString", will return a string containing the state of the object.



Getter/Setter Methods
PHP код:
// Getter/Setter methods
stock ClassExample.getMyInteger(ClassExample:this)
{
    return 
ClassExample.myInteger[this];
}
stock ClassExample.setMyInteger(ClassExample:thisi)
{
    
ClassExample.myInteger[this] = i;
}
stock Float:ClassExample.getMyFloat(ClassExample:this)
{
    return 
ClassExample.myFloat[this];
}
stock ClassExample.setMyFloat(ClassExample:thisFloat:f)
{
    
ClassExample.myFloat[this] = f;
}
stock ClassExample.getMyString(ClassExample:this)
{
    return 
ClassExample.myString[this];
}
stock ClassExample.setMyString(ClassExample:thiss[])
{
    
format(ClassExample.myString[this], 64s);
}
/************************************************************/ 
We can create getters and setters to access our fields (optional).

Instantiating, calling methods and setting field values
PHP код:
main()
{
    
// creating instances
    
new ClassExample:ClassExample(201525.524"Hello World!");
    new 
ClassExample:ClassExample(1257.52"Goodbye World!");
    new 
ClassExample:ClassExample(5212.52"Hello Again World!");
    
    
// calling toString
    
printf(ClassExample.toString(a));
    
printf(ClassExample.toString(b));
    
printf(ClassExample.toString(c));
    
    
// changing and testing fields directly
    
printf("We incremented a's integer directly\n");
    
ClassExample.myInteger[a] ++;
    
format(ClassExample.myString[a], 64"Happy new year!");
    
printf(ClassExample.toString(a));
    
    
// changing and testing fields by getter and setter methods
    
printf("b.getMyString: '%s'"ClassExample.getMyString(b));
    
    
printf("We are now setting b's string value by calling its setter method\n");
    
ClassExample.setMyString(b"Changed value!");
    
    
printf("b.getMyString: '%s'"ClassExample.getMyString(b));

Now let's test the class!

Output:

Код:
[15:08:16] ClassExample 0
	myInteger: 2015
	myFloat: 25.5240
	myString: Hello World!


[15:08:16] ClassExample 1
	myInteger: 125
	myFloat: 7.5199
	myString: Goodbye World!


[15:08:16] ClassExample 2
	myInteger: 521
	myFloat: 2.5199
	myString: Hello Again World!


[15:08:16] We incremented a's integer directly

[15:08:16] ClassExample 0
	myInteger: 2016
	myFloat: 25.5240
	myString: Happy new year!


[15:08:16] b.getMyString: 'Goodbye World!'
[15:08:16] We are now setting b's string value by calling its setter method

[15:08:16] b.getMyString: 'Changed value!'
Example usages

I want to show you a very basic, uncomplete and untested house system made with a House class for demostrating purposes.

PHP код:

/*
* This is an example on how to mimic a very basic Object-Oriented programming in the PAWN language
* by SaSiNO97_ Aka Sasinosoft
*/
#include <a_samp>
/******************** class House *******************/
#define House. House_
#define House_max House:256
// Fields
stock House.owner[House.max][MAX_PLAYER_NAME];
stock House.price[House.max];
stock Float:House.x[House.max];
stock Float:House.y[House.max];
stock Float:House.z[House.max];
// Static final field example
// - static because it's not depending on a single House object (no [House.max])
// - final because it's defined as "const" and can't be changed
stock const House.invalidOwnerName[MAX_PLAYER_NAME] = "#@!/*-";
// Constructor
stock House:House(priceFloat:xFloat:yFloat:z)
{
    static 
House:this House:-1;
    
this++;
    
//
    
format(House.owner[this], MAX_PLAYER_NAMEHouse.invalidOwnerName);
    
House.price[this] = price;
    
House.x[this] = x;
    
House.y[this] = y;
    
House.z[this] = z;
    
Create3DTextLabel("House"0x33AA33FFxyz5.00true);
    
//
    
return this;
}
// Methods
stock House.toString(House:this)
{
    new 
ret[128];
    
format(
        
ret
        
128
        
"House %d\n\t\
        Owner: %s\n\t\
        Price: $%d\n\t\
        Position: (%.4f, %.4f, %.4f)\n\n"

        
_:this,
        
House.owner[this],
        
House.price[this],
        
House.x[this],
        
House.y[this],
        
House.z[this]
    );
    return 
ret;
}
stock bool:House.buy(House:thisplayerid)
{
    if(!
IsPlayerConnected(playerid))
        return 
false;
    
    if(!
strcmp(House.owner[this], House.invalidOwnerName))
    {
        
SendClientMessage(playerid0xFFFFFFFF"[ERROR]This house is already owned.");
        return 
false;
    }
    
    if(
GetPlayerMoney(playerid) < House.price[this])
    {
        
SendClientMessage(playerid0xFFFFFFFF"[ERROR]You can't afford this house.");
        return 
false;
    }
    
    if(!
IsPlayerInRangeOfPoint(playerid1.5House.x[this], House.y[this], House.z[this]))
    {
        
SendClientMessage(playerid0xFFFFFFFF"[ERROR]You aren't close to this house.");
        return 
false;
    }
    
    new 
nickname[MAX_PLAYER_NAME];
    
GetPlayerName(playeridnicknameMAX_PLAYER_NAME);
    
GivePlayerMoney(playerid, -House.price[this]); // 
    
format(House.owner[this], MAX_PLAYER_NAMEnickname);
    
SendClientMessage(playerid0xFFFFFFFF"[CONGRATULATIONS]You now own this house!");
    return 
true;
}
stock bool:House.sell(House:thisplayerid)
{
    if(!
IsPlayerConnected(playerid))
        return 
false;
    
    
    new 
nickname[MAX_PLAYER_NAME];
    
GetPlayerName(playeridnicknameMAX_PLAYER_NAME);
    
    if(
strcmp(House.owner[this], nickname) != 0)
    {
        
SendClientMessage(playerid0xFFFFFFFF"[ERROR]This house is not yours.");
        return 
false;
    }
    
    if(!
IsPlayerInRangeOfPoint(playerid1.5House.x[this], House.y[this], House.z[this]))
    {
        
SendClientMessage(playerid0xFFFFFFFF"[ERROR]You aren't close to this house.");
        return 
false;
    }
    
    
GivePlayerMoney(playeridfloatround(House.price[this]*0.7floatround_ceil)); // 
    
format(House.owner[this], MAX_PLAYER_NAMEHouse.invalidOwnerName);
    
SendClientMessage(playerid0xFFFFFFFF"[CONGRATULATIONS]You sold your house!");
    return 
true;
}
// Example of a static method (a method that is not linked to a specific object of the class)
stock House.printAll()
{
    for(new 
House:i=House:0i<House.maxi++) // Loop through all Houses
    
{
        if(
House.x[i] != 0.0// check if it was created
        
{
            
printf(House.toString(i)); // prints it state
        
}
    }
}
/************************************************************/
main()
{
    
printf("House Class Example by SaSiNO97_!");
}
public 
OnGameModeInit()
{
    
// Let's create some anonymous objects of the class House (positions are totally random, so don't look for them inside the game :P)
    
House(150001520.0214.015.0);
    
House(250001620.0244.012.0);
    
House(500001120.0714.014.0);
    
House(360001120.0714.014.0);
    
House(140001220.0614.024.0);
    
House.printAll();
}
public 
OnPlayerCommandText(playeridcmdtext[])
{
    if(!
strcmp(cmdtext"/buy"true))
    {
        for(new 
House:i=House:0i<House.maxi++) // Loop through all Houses
        
{
            if(
IsPlayerInRangeOfPoint(playerid1.5House.x[i], House.y[i], House.z[i])) // If player is in range of a house
            
{
                
House.buy(iplayerid); // Call the method "buy"
                
break; // Exit the loop
            
}
        }
        return 
1;
    }
    
    if(!
strcmp(cmdtext"/sell"true)) // Similar to previous
    
{
        for(new 
House:i=House:0i<House.maxi++)
        {
            if(
IsPlayerInRangeOfPoint(playerid1.5House.x[i], House.y[i], House.z[i]))
            {
                
House.sell(iplayerid);
                break;
            }
        }
        return 
1;
    }
    return 
0;

Reply


Messages In This Thread
[Advanced]How to mimic Object-Oriented Programming in PAWN - by Sasino97 - 13.12.2015, 14:11
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by HydraHumza - 13.12.2015, 14:14
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by vassilis - 13.12.2015, 15:00
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Sasino97 - 13.12.2015, 16:21
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Yashas - 13.12.2015, 17:02
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by N0FeaR - 13.12.2015, 21:28
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Sasino97 - 26.01.2016, 20:55
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by KillerDVX - 27.01.2016, 11:27
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by SystemX - 30.01.2016, 17:11
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by NewbProgrammer - 31.01.2016, 05:59
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Sasino97 - 02.02.2016, 11:23
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Scranton - 18.08.2016, 12:29
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Eoussama - 07.11.2017, 18:31
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Xeon™ - 07.11.2017, 19:21
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by 0x88 - 04.12.2017, 10:39
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by SammyJ - 04.12.2017, 11:08
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by Kar - 07.12.2017, 04:29
Re: [Advanced]How to mimic Object-Oriented Programming in PAWN - by BiosMarcel - 07.12.2017, 14:31

Forum Jump:


Users browsing this thread: 2 Guest(s)