[Tutorial] Getting a player's position in a race (GetPlayerRacePosition)
#1

Hello I have been making a race system for my GM, and used this tutorial for it: https://sampforum.blast.hk/showthread.php?tid=401252

However I noticed it did not include a race position thing, and we all know that every single race on a decent server has a race position thing, no?

So I searched for tutorials and looked in other filterscripts (such as yRace and rRace).
But I couldn't find a way of doing it, so I tried to do it myself and succeeded.
So because there's no (good) tutorial of doing this I'm going to show you how I did it.

IMPORTANT: I use this function with the race system I made from the tutorial (see top), if your race system operates differently, chances are that this isn't going to work...

So how is this tutorial going to be?
Well I just made the following function:
pawn Код:
GetPlayerPosition(playerid, raceid)
which returns the player's position
And I'll just show you the stock code, and then explain every line.

| Step One |

Ok so step one, as always is adding a new array.
I'm only going to show you the array to store the position, not all other race arrays in the racing system...

Add this to your PlayerInfo enum (I guess you have one?)
pawn Код:
enum PlayerInfo
{
    //blabla
    Pos,
}

new PInfo[MAX_PLAYERS][PlayerInfo];
This simply stores the player's position.
Now we for safety reasons I like to set this array to 0 in several callbacks, I don't think it's really necesairy, but I do it to make sure there's no bugs...
So add this:
pawn Код:
PInfo[playerid][PlayerInfo] = 0;
under the following Callbacks:
pawn Код:
public OnPlayerConnect(playerid)
stock LeaveRace(playerid, raceid) //Or whathever you use in your script when a player leaves a race
| Step Two |

Ok now let's go to the function itself.

here's the code
pawn Код:
stock GetPlayerPosition(playerid, raceid)
{
    new index = 0;
    for(new j = 0; j < MAX_PLAYERS; j++)
    {
        if(PInfo[j][Racing] == raceid)
        {
            if(j != playerid)
            {
                if(PInfo[playerid][RaceCheckpoint] < PInfo[j][RaceCheckpoint])
                {
                    index++; //for everyone who is in front of the player index goes ++
                }
               
                if(PInfo[playerid][RaceCheckpoint] == PInfo[j][RaceCheckpoint]) //who is in the same section as the player
                {
                    new PlayerCheckPoint = PInfo[playerid][RaceCheckpoint];
                    new ElseCheckPoint = PInfo[j][RaceCheckpoint];
                   
                    if(GetPlayerDistanceFromPoint(playerid, RaceInfo[raceid][checkpointx][PlayerCheckPoint]+1,RaceInfo[raceid][checkpointy][PlayerCheckPoint]+1, RaceInfo[raceid][checkpointz][PlayerCheckPoint]+1) > GetPlayerDistanceFromPoint(j, RaceInfo[raceid][checkpointx][ElseCheckPoint]+1, RaceInfo[raceid][checkpointy][ElseCheckPoint]+1, RaceInfo[raceid][checkpointz][ElseCheckPoint]+1))
                    {
                        index++;
                    }
                }
            }
        }
    }
   
    PInfo[playerid][Pos] = index +1;
   
    return PInfo[playerid][Pos];
}
Let's explain line by line:
pawn Код:
new index = 0;
This is a new 'index', we'll use it to store the amount of players that are infront of the player we track the position.
-> the player's position will eventually be index +1 , since it's all the players in front of him +1
But how do we count the amount of players in front of the tracked one?

here's how:
pawn Код:
for(new j = 0; j < MAX_PLAYERS; j++) //everyone
    {
This just loops through all the players that could be online
-> i equals all the players that could be online

pawn Код:
if(PInfo[j][Racing] == raceid)  //Use your own variable to define wether a player is racing or not
        {
-> now i stands for everyone who is racing the/a race, because we filtered everyone who isn't in the race.

pawn Код:
if(j != playerid) // who is not the checked player
            {
We just filtered ourself, since we cannot really see if 'you' are in front of 'you'...

pawn Код:
if(PInfo[playerid][RaceCheckpoint] < PInfo[j][RaceCheckpoint]) //use your own variable to get the player's checkpoint
                {
Now I'm going to give some explanation:
pawn Код:
PInfo[playerid][RaceCheckpoint]
This is the checkpoint the player has last entered, the progress of the player in the race. If a player has already done 2 checkpoints, this will be2.
I'm sure your racing system has something similar to track the player's progress, no?
pawn Код:
PInfo[j][RaceCheckpoint]
This is the checkpoint all the other racers have last entered, the same story as the previous variable, but this is for every other racer.

So this:
pawn Код:
if(PInfo[playerid][RaceCheckpoint] < PInfo[j][RaceCheckpoint])
Means if the tracked player has done less checkpoints than all the other racers.
Let's move on:
pawn Код:
index++; //for everyone who is in front of the player index goes ++
So, what's this?
Well we constantly do our first loop, and now we end up here, so everything that happens in here has passed all the "if's" hence, index will go ++ for everyone who is in front of the player, right?



Now whe have only checked for all the players that have done more checkpoints than the tracked player.
But what about all the players that have done the same amount of checkpoints as i, but is in front of me?

you can see it like this:

Код:
 . | . 0 .. | ... |


. represents a racer
0 represents me
| represents a checkpoint

So now index has only gone up for the first three racers, since they have done more checkpoints than me, but what about the 3 other racers that have done the same amount of checkpoints yet as me?
So this code:
pawn Код:
if(PInfo[playerid][RaceCheckpoint] == PInfo[j][RaceCheckpoint]) //who is in the same section as the player
                {
This checks if all the other races have done the same amount of checkpoints as I -> they are in the same 'section'.
So now 'j' represents that.

pawn Код:
new PlayerCheckPoint = PInfo[playerid][RaceCheckpoint];
                    new ElseCheckPoint = PInfo[j][RaceCheckpoint];
This is just to make things shorter, PlayerCheckPoint is the last checkpoint the player has entered, ElseCheckPoint is the amount of checkpoints all the other racers (who are in the same section as me) have done.


pawn Код:
if(GetPlayerDistanceFromPoint(playerid, RaceInfo[raceid][checkpointx][PlayerCheckPoint]+1,RaceInfo[raceid][checkpointy][PlayerCheckPoint]+1, RaceInfo[raceid][checkpointz][PlayerCheckPoint]+1) > GetPlayerDistanceFromPoint(j, RaceInfo[raceid][checkpointx][ElseCheckPoint]+1, RaceInfo[raceid][checkpointy][ElseCheckPoint]+1, RaceInfo[raceid][checkpointz][ElseCheckPoint]+1))
                    {
Let's filter 'j' again, so now we calculate the distance the player is away from their next checkpoint ([PlayerCheckPoint] +1), and the distance in between every other racer and their next checkpoint.

So if the racer's distance is bigger than the others we continue:
pawn Код:
index++;
So index goes up for every racer that has done the same amount of checkpoints than me, and is closer to the next one than me.

So there we go.

Last bits:
pawn Код:
}
                }
            }
        }
    }
   
    PInfo[playerid][Pos] = index +1;
   
    return PInfo[playerid][Pos];
}
Now, index represents the amount of players in front of you
-> your position = index +1

I don't think you really need to make the PInfo[playerid][Pos] variable, you can also just return index +1, but I like to make a P variable.

So we returned the position, now when you use the function, it will output the player's position.


| Step Three |

Here's an example script:
pawn Код:
public RaceTimeS(raceid) //Called every second when a race has started
{
    for(new i; i<MAX_PLAYERS; i++)
    {
        if( !IsPlayerConnected( i ) ) continue;
        printf( "if(PInfo[i][Racing] == raceid)::if(%d == %d)", PInfo[i][Racing], raceid );
        if(PInfo[i][Racing] == raceid)
        {
            print( "PInfo[i][racetimeS] ++;" );
            RaceInfo[raceid][racetimeS] ++;
            printf( "PInfo[i][racetimeS] = %d", RaceInfo[raceid][racetimeS] );

            print( "TD: Time Taken will be called.." );
            new string[56];
            format (string, sizeof string, "%s Time Taken" , ReturnSToM(RaceInfo[raceid][racetimeS]));
            TextDrawSetString(TD_TIME[i], string);
            print( "TD: Time Taken has been called.." );

            print( "TD: Position will be called.." );
            new Position[26];
            format(Position, sizeof Position, "%d/%d Position",GetPlayerPosition(i, raceid), RaceInfo[raceid][originalcount]);
            TextDrawSetString(TD_POS[i], Position);
            print( "TD: Position has been called.." );
        }
    }
    return 1;
}
This code works fine for me, but I have only tested it with 2 players yet, not with 3 or more...
So hopefully it will work aswell.

Hope it helped you.
Bye.
Reply
#2

Your race place method should work fine but if your calling it every second there is a much better way to do it and get more information at the same time.

1.) Get all the players distances from the end of the race
2.) Quick sort the list

I think this is a better way since now you don't have to loop through all players for every player. But you wouldn't even notice with 10 players with a 100 players however it would be a better option another good thing is you also have the players progression and amount left to complete the race.
Reply
#3

but GetPlayerDistance works only for straight lines, so if the race isn't a straight line, the information will be false
Reply
#4

That is true but it does work fairly decently if you place your checkpoints good and it will always be accurate for determining player placement.
Reply
#5

But I have many races in my server that have a lot of bends, f.e kart, race aswell as many races that end close to where they started...
For example a world race, starting in Los santos beach f.e and ends there too, but it goes around all san andreas, it won't work if I just calculate distance from end...
Reply
#6

Yes it will, you need to precalculate the distance between each checkpoint and keep adding it up so for instance you have 10 CP's in a loop each 100 meters apart CP 4 would be a distance of 400 meters from the start. Say the player is going for the fifth CP you would get the distance of the player from the fifth CP + 400 meters to get their position understand ?
Reply
#7

Damn, that's right, would you mind showing it in some code?
Reply
#8

Sure give me a day or so and I'll prepare you some example code.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)