I personally use a grid with three columns. The first column is "Name", the second is "Time Remaining", the third is called "Reply" which contains a script to run on expiration of the cooldown or -1 for no script. Any object with a cooldown grid runs a script called manage_cooldowns in their step event. The manage_cooldowns script cycles through the Y axis of the name column, and adjusts the time remaining column by -1 for each valid entry it finds. If a time remaining column reaches 0 the reply script is executed if there is one. Afterwards, the entire row is deleted for reuse.
When I want to add a cooldown, I use a script create_cooldown(obj_id,"name_of_cooldown",time,reply_script). If there is not enough space in the grid it resizes the grid by making it taller.
I also have another script, called on_cooldown(obj_id,"name_of_cooldown") which uses ds_grid_value_exists to check if a certain cooldown exists in the name column.
So essentially with this handy scripts I could use the following logic...
Code:
if !on_cooldown(player_object,"Shoot")
{
create_cooldown(player_object,"Shoot",500,-1)
}