Top 5 highscores getting a effect

AngeLOT

Active Member
Joined
Dec 14, 2017
Messages
440
Reaction score
71
Hey Otland, would like to request a script since I cant find it anywhere :/

The script is simple:
If you are top 1-5 of highscore, u getting always a effect like every 2 sec and thats all xD
 
Last edited:

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
Server version?
What classifies them as high scoring?
 

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
Sorry totally forgot to write my tfs version.. im using tfs 0.4



Their level ^^
I'm not good with databases unfortunately.
I could script it to work with the 5 highest level players currently logged in, if you want.
 

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
Ye could work aswell :p
Tested on 0.3.7
(If there are multiple people with the same 'highscore' they will all get the animation for that highscore tier.)
Lua:
Example..?
Levels of players on server == 10, 10, 9, 8, 8, 8, 8, 7, 6, 5, 4, 3, 1

Players 10, 10 -> "first place"
Player 9 -> "second place"
Players 8, 8, 8, 8 -> "third place"
Player 7 -> "fourth place"
Player 6 -> "fifth place"

Players 5, 4, 3, 1 -> Get no effect.
I think interval on 0.4 is in seconds instead of milliseconds.
If I'm wrong, change interval to 2000.
XML:
<globalevent name="highscore_animation" interval="2" event="script" value="highscore_animation.lua"/>
Lua:
local config = { -- player with high scores get these effects, based on their ranking
    [1] = CONST_ME_HEARTS, -- first place
    [2] = CONST_ME_CRAPS,
    [3] = CONST_ME_FIREWORK_BLUE,
    [4] = CONST_ME_FIREWORK_YELLOW,
    [5] = CONST_ME_FIREWORK_RED   -- fifth place
}

local function compare(a, b)
    return a[1] > b[1]
end

local function send_effect(cid, rank)
    doSendMagicEffect(getCreaturePosition(cid), config[rank])
end

function onThink(cid, interval, lastExecution)
    local player_data, temp1, temp2 = {}, 0, 1
    for _, pid in ipairs(getPlayersOnline()) do
        player_data[#player_data + 1] = {getPlayerLevel(pid), pid}
    end
    table.sort(player_data, compare)
    for i = 1, #player_data do
        if temp1 == 0 or temp1 == player_data[i][1] then
            addEvent(send_effect, 0, player_data[i][2], temp2)
        elseif temp2 < 5 then
            addEvent(send_effect, 0, player_data[i][2], temp2 + 1)
            temp2 = temp2 + 1
        else
            break
        end
        temp1 = player_data[i][1]
    end
    return true
end
 
OP
AngeLOT

AngeLOT

Active Member
Joined
Dec 14, 2017
Messages
440
Reaction score
71
Tested on 0.3.7
(If there are multiple people with the same 'highscore' they will all get the animation for that highscore tier.)
Lua:
Example..?
Levels of players on server == 10, 10, 9, 8, 8, 8, 8, 7, 6, 5, 4, 3, 1

Players 10, 10 -> "first place"
Player 9 -> "second place"
Players 8, 8, 8, 8 -> "third place"
Player 7 -> "fourth place"
Player 6 -> "fifth place"

Players 5, 4, 3, 1 -> Get no effect.
I think interval on 0.4 is in seconds instead of milliseconds.
If I'm wrong, change interval to 2000.
XML:
<globalevent name="highscore_animation" interval="2" event="script" value="highscore_animation.lua"/>
Lua:
local config = { -- player with high scores get these effects, based on their ranking
    [1] = CONST_ME_HEARTS, -- first place
    [2] = CONST_ME_CRAPS,
    [3] = CONST_ME_FIREWORK_BLUE,
    [4] = CONST_ME_FIREWORK_YELLOW,
    [5] = CONST_ME_FIREWORK_RED   -- fifth place
}

local function compare(a, b)
    return a[1] > b[1]
end

local function send_effect(cid, rank)
    doSendMagicEffect(getCreaturePosition(cid), config[rank])
end

function onThink(cid, interval, lastExecution)
    local player_data, temp1, temp2 = {}, 0, 1
    for _, pid in ipairs(getPlayersOnline()) do
        player_data[#player_data + 1] = {getPlayerLevel(pid), pid}
    end
    table.sort(player_data, compare)
    for i = 1, #player_data do
        if temp1 == 0 or temp1 == player_data[i][1] then
            addEvent(send_effect, 0, player_data[i][2], temp2)
        elseif temp2 < 5 then
            addEvent(send_effect, 0, player_data[i][2], temp2 + 1)
            temp2 = temp2 + 1
        else
            break
        end
        temp1 = player_data[i][1]
    end
    return true
end
Thanks you really much! <3
 

4drik

Active Member
Joined
Jun 30, 2014
Messages
172
Reaction score
106
I think that the list of players should be done in login, logout and level advance functions.
Not in onThink.
 

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
Can I ask why?
So that you don't need to re-create the list of players constantly, which will reduce overhead. (time it takes for your computer to do the calculations)
I think that the list of players should be done in login, logout and level advance functions.
Not in onThink.
in pre tfs 1.0x servers each section (actions/globalevents/creatureevents/et cetera) is localised, and cannot share tables with each other. (trust me, I've tried)

The only way to change it like you propose is to use storage values, and then use onThink to send the animations to the players with the correct storage values.
Honestly, it's probably the better solution, just a bit more time consuming to create initially..
But that's assuming a few things.

Counter-point 1, this is assuming players are not being assholes.
Example: Logging in & out constantly.

Counter-point 2, this is assuming the server is a low-rate exploration server.
Example: Highrate server, where player levels are constantly changing.
Example: War-based server, where player levels are changing constantly, and players are dying, which causes a logout and login as well as an advance function to trigger.

Those are the bigger examples, but there are some smaller things you could look at as well.

When these above events are happening, the overhead to the server would be huge, especially if you multiply the amount of players online doing the same 'destructive' behaviour.

For these reasons, I believe it's best to have a consistent triggering of the event, instead of low and high spikes of activity.

------------------------------
As for my script, if you are not worried about getting the most up-to-date information, we could change it to update the player highscores every 5 minutes, for example, to reduce the amount of overhead the script causes.

(So it checks for first to fifth place once per 5 minutes, and then sends the animations to the players for a full 5 minutes, before checking for player highscores again.)

(Note: If a highscoring player logs out or dies inbetween the 5 minute checks, they will no longer have the effect, until the next 5 minute check.)

So, same as before.
5 * 60 = 300 seconds
But, if it counts in milliseconds, use 300 * 1000 = 300000
XML:
<globalevent name="highscore_animation" interval="300" event="script" value="highscore_animation.lua"/>
Lua:
local config = { -- player with high scores get these effects, based on their ranking
    [1] = CONST_ME_HEARTS, -- first place
    [2] = CONST_ME_CRAPS,
    [3] = CONST_ME_FIREWORK_BLUE,
    [4] = CONST_ME_FIREWORK_YELLOW,
    [5] = CONST_ME_FIREWORK_RED   -- fifth place
}

local function compare(a, b)
    return a[1] > b[1]
end

local function send_effect(cid, rank, amount)
    if not isPlayer(cid) then
        return true
    end
    doSendMagicEffect(getCreaturePosition(cid), config[rank])
    if amount > 0 then
        addEvent(send_effect, 2000, cid, rank, amount - 1)
    end
end

function onThink(cid, interval, lastExecution)
    local player_data, temp1, temp2 = {}, 0, 1
    for _, pid in ipairs(getPlayersOnline()) do
        player_data[#player_data + 1] = {getPlayerLevel(pid), pid}
    end
    table.sort(player_data, compare)
    for i = 1, #player_data do
        if temp1 == 0 or temp1 == player_data[i][1] then
            addEvent(send_effect, 0, player_data[i][2], temp2, 150)
        elseif temp2 < 5 then
            addEvent(send_effect, 0, player_data[i][2], temp2 + 1, 150)
            temp2 = temp2 + 1
        else
            break
        end
        temp1 = player_data[i][1]
    end
    return true
end
 

4drik

Active Member
Joined
Jun 30, 2014
Messages
172
Reaction score
106
in pre tfs 1.0x servers each section (actions/globalevents/creatureevents/et cetera) is localised, and cannot share tables with each other. (trust me, I've tried)
I never used TFS but since I can do it on YurOTS, it's all the more on such a powerful engine as TFS.
There is no situation in which the code in previous posts would be better.
So your counter-points are rather a false guess, but of course, your solution is faster to write.

As if someone wanted to write it better.
In game.h declare:
C++:
std::list<Player*> topPlayers;
Then compare players in functions:
C++:
void Game::addPlayer(Player* player)
void Game::removePlayer(Player* player)
bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel,
uint32_t newLevel)
In the last function, exclude here:
C++:
if (skill == SKILL_LEVEL){
...
}
Add top 5 to topPlayers list:
C++:
for(int i = 0; i <= 4; i++ )
topPlayers.push_back(table[i]);
In a function:
C++:
void Player::onThink(uint32_t interval)
Check if the player is in the list:
C++:
if(std::find(topPlayers.begin(), topPlayers.end(), this) != topPlayers.end()){
...
}
Send the effect.

#Down:
You're right, I did not specify in the previous post.
 
Last edited:

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
Sorry, yes, if you code it into the source it's going to be faster.
I had assumed you meant via lua scripts, not via source.

Just a small rebuttal;
It's not a 'false guess', that if 1 player decides to log in and out repeatedly, it would trigger both the login and logout functions repeatedly, which would make it check more often then every 2 seconds. (and if there is 20-30 people doing this..? Or after a server save when 70 people try to log in right away?)

Unless I'm mistaken in what your trying to tell me. :/
 
Last edited:

tetra20

DD
Joined
Jan 17, 2009
Messages
1,306
Reaction score
285
Location
Egypt
Sorry, yes, if you code it into the source it's going to be faster.
I had assumed you meant via lua scripts, not via source.

Just a small rebuttal;
It's not a 'false guess', that if 1 player decides to log in and out repeatedly, it would trigger both the login and logout functions repeatedly, which would make it check more often then every 2 seconds. (and if there is 20-30 people doing this..? Or after a server save when 70 people try to log in right away?)

Unless I'm mistaken in what your trying to tell me. :/
The first lua code xikini pasted is actually fine for lua(maybe?),It has an order of O(Nlogn) where n is the number of players,which wouldn't lag even if you repeated it every 2 seconds with 10000 players online(probably?)

In my own opinion,if you are seeking better time and less memory,this'd probably be done in source with std::set

1-when a player logs in,insert both his {level,id} into set
2-when the player logout,erase both his {level,id} from the set

Both operations above are O(logn),and each 2 seconds,as the set is already sorted by itself,you'd only have to check the last 5 positions in the set as they have the highest 5 players,with their ids

That's O(logn) time,O(n) memory

to achieve an O(1) memory,you can do the following..
1-when a player logs in,insert both his {level,id} into set
2-while the set size is greater than 5,erase the smallest element(beginning of set)
3-when the player logout,erase both his {level,id} from the set
 

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
3,925
Reaction score
2,046
The first lua code xikini pasted is actually fine for lua(maybe?),It has an order of O(Nlogn) where n is the number of players,which wouldn't lag even if you repeated it every 2 seconds with 10000 players online(probably?)

In my own opinion,if you are seeking better time and less memory,this'd probably be done in source with std::set

1-when a player logs in,insert both his {level,id} into set
2-when the player logout,erase both his {level,id} from the set

Both operations above are O(logn),and each 2 seconds,as the set is already sorted by itself,you'd only have to check the last 5 positions in the set as they have the highest 5 players,with their ids

That's O(logn) time,O(n) memory

to achieve an O(1) memory,you can do the following..
1-when a player logs in,insert both his {level,id} into set
2-while the set size is greater than 5,erase the smallest element(beginning of set)
3-when the player logout,erase both his {level,id} from the set
You gotta account for many people having the same level though.
If 18 people all have level 10, and level 10 is the highest level on the server, you gotta give them all the effect, not just the first 5 to login.
This was the harder part of the script, tbh.
 

tetra20

DD
Joined
Jan 17, 2009
Messages
1,306
Reaction score
285
Location
Egypt
You gotta account for many people having the same level though.
If 18 people all have level 10, and level 10 is the highest level on the server, you gotta give them all the effect, not just the first 5 to login.
This was the harder part of the script, tbh.
Back to O(logn) time and O(n) memory
1-when a player logs in,insert both his {level,id} into set
2-when the player logout,erase both his {level,id} from the set
now you have best players Added in the list,just iterate in the following way
two variables highestLevelFound = 0,timesFound = 0;
once you encounter a new highestLevelFound,increase the timesFound by 1,if the timesFound reaches 6,break for example

{10,10,10,9,8,8,7,6,5}
those are the iterations
1-Set highestLevelFound to 10,timesFound = 1,and send effect to player
2-highestLevelFound is already equal to the second player,so just send effect to player
3-highestLevelFound is already equal to the third player,so just send effect to player
4-set HighestLevelFound to 9,timesFound = 2,and send effect to player
5-set highestLevelFound to 8,timesFound = 3,and send effect to player
6-highestLevelfound already equal to 8,just send effect to player
7-set highestLevelFound to 7,timesFound = 4,and send effect to player
8-set highestLevelFound to 6,timesFound = 5,and send effect to player
9-set highestLevelFound to 5,timesFound = 6,Break

The worst case here is O(n) where n is number of players,but this will rarely happen,there isn't that much people with same level?
Best case is O(1) <-- which will mostly happen

This is just another way to do it,but all the ways are fine,your way is actually good enough and forgive my long post
 
Top