Existing user? Sign in
Sign up
Games
Servers
Useful
User
Pass
2FA
Cerere plugin
Freakz Forum Index
->
Trash Bin
->
Trash
Author
Message
897
KillingFloor
[Mentally Stable]
Status: Offline
(since 09-02-2013 10:06)
Joined: 22 Nov 2012
Posts: 14
,
Topics: 4
Location:
Romania
Reputation:
51.5
Votes
: 2
Posted: 22-11-2012, 21:32:51
| Translate post to:
... (
Click for more languages
)
Salut all ! Puneti-mi si mie pe ADMIN_LEVEL_H acest plugin ...as face-o eu dar nu prea ma pricep bine
Daor cei cu acces ADMIN_LEVEL_H sa poata porni un sunet dar in rest toti playerii sa il poata auzi sunetul respectiv
/***************************************************************************
* This plugin reads keyword/wav/mp3 combinations from a configfile and when
* a player says one of the keywords, it will trigger HL to play that Wav/MP3
* file to all or dead/alive players. It allows reloading of the file without
* restarting the current level, as well as adding keyword/wav/mp3
* combinations from the console during gameplay. Also includes banning
* players from playing sounds.
*
* Credits:
* - Luke Sankey -> original author
* - HunteR -> modifications
*
* Functions included in this plugin:
* mp_sank_sounds_download 1/0 - turn internal download system on/off
* mp_sank_sounds_freezetime <x> - x = time in seconds to wait till first sounds are played (connect sound)
* mp_sank_sounds_obey_duration <x> - determine whos sounds may overlap (bit mask) (see readme.txt)
* amx_sound - turn Sank Sounds on/off
* amx_sound_help - prints all available sounds to console
* amx_sound_play <dir/sound> - plays a specific wav/mp3/speech
* amx_sound_add <keyword> <dir/sound> - adds a word/wav/mp3/speech
* amx_sound_reload <filename> - reload your snd-list.cfg or custom .cfg
* amx_sound_remove <keyword> <dir/sound> - remove a word/wav/mp3
* amx_sound_write <filename> - write all settings to custom .cfg
* amx_sound_reset <player> - resets quota for specified player
* amx_sound_debug - prints debugs (debug mode must be on, see define below)
* amx_sound_ban <player> - bans player from using sounds for current map
* amx_sound_unban <player> - unbans player from using sounds for current map
* amx_sound_top <x> - shows the top <x> most played keywords (leave <x> away for top 10)
*
* Config file settings:
* SND_WARN - The number at which a player will get warned for playing too many sounds each map
* SND_MAX - The number at which a player will get muted for playing too many sounds each map
* SND_MAX_DUR - The maximum amount of seconds a player can play sounds each map (float )
* SND_JOIN - The Sounds to play when a person joins the game
* SND_EXIT - The Sounds to play when a person exits the game
* SND_DELAY - Minimum delay between sounds (float)
* SND_MODE XX - Determinates who can play and who can hear sounds (see readme.txt for details)
* SND_IMMUNITY "XYZ" - Determine the access levels which shall have immunity to warn/kick/ban
* EXACT_MATCH 1/0 - Determinates if plugin triggers on exact match, or partial speech match
* ADMINS_ONLY 1/0 - Determinates if only admins are allowed to play sounds
* DISPLAY_KEYWORDS 1/0 - Determinates if keywords are shown in chat or not
*
* Commands available for each player:
* amx_sound_help - prints all available sounds to console
* say "/soundson" - now the player can hear sounds again
* say "/soundsoff" - player disables ability to hear sounds
* say "/sounds" - shows a list of all sounds
*
* ported to Amx Mod X by White Panther
*
* v1.0.2 (original 4.1 but this is AmxModX):
* - initial release for AmxModX
* - renamed commands to fit with AmxModX
* - Admin sounds cannot be seen by normal people when using amx_sound_help
* - sounds are precached from file
* - fix: check if soundfile exist before precache (that should solve some probs)
* - fix: if chat message was longer than 29 chars the first wav in cfg was played
*
* v1.1.3 :
* - fixed bug with spaces between keywords and wavs
* - multiple Join and Exit sounds can now be used
* - fixed bug where connect and disconnect sound have not been played
* - fixed bug where dead players could not hear sounds
* - added bot check
* - added option to only allow admins to play sounds
*
* v 1.2.4 :
* - added mp3 support (they have to be in <Mod-Dir>/sound too) (engine module needed therefore) (+ hotfix: wavs not being played)
* - changed the way of initializing each sound file (if bad file it wont be loaded and error msg will be printed)
* - changed SND_KICK to SND_MAX
* - increased default defines ( words: 40 - > 80 / each wavs: 10 -> 14 / file chars: 30 -> 60 )
* - fixed bug for 32 players
* - increased memory usage for variables to 64K (should fix probs)
* - while parsing there is now a check if file exists (if not it wont be put in list)
*
* v1.2.5:
* - added a cvar to enable or disable auto download (change will take place after restart/mapchange)
*
* v1.3:
* - fixed:
* - fixed prob where strings were copied into other strings with no size match
* - removed bot detection (maybe this was causing some problems, playing sounds to bots does not do any harm)
* - admin sounds could not be played (eg: hallo; misc/hi.wav;@misc/hi2.wav -> hi2.wav was not played, even by admins)
* - added:
* - type "/sounds" in chat to get a MOTD window with all sounds available (not all mods support MOTD window)
* - ability for speech sounds (like the AmxModX's speechmenu)
* - admin check to "amx_sound_debug" so in debugmode only admins can use it
* - list is now sorted by name for more readable output (sort by Bailopan) (sort can be turned off by define)
*
* v1.3.2:
* - fixed:
* - mp3 support not working
* - changed:
* - mp3 now dont need to be in sound folder but anywhere you want (anywhere in your mod folder though)
* just specify the correct path (eg: music/mymusic/my.mp3 or sound/testmp3/test.mp3 or mainfolder.mp3)
* - amx_sound_debug can now also be used if debug mode is off (this function prints the sound matrix)
*
* v1.3.3:
* - added:
* - cvar "mp_sank_sounds_freezetime" to define when first connect/disconnect sounds are played after mapchange (in seconds)
*
* v1.3.4:
* - fixed:
* - error where some players could not hear any sound
* - changed:
* - some log messages got better checks
* - reimplemented check for bots
*
* v1.3.5:
* - added:
* - with "/soundson" and "/soundsoff" each player can activate/deactivate the ability to hear sounds
*
* v1.3.7:
* - added:
* - "DISPLAY_KEYWORDS" to config, it determinates if keywords are shown in chat or not
* - option to load specific sounds only on specific maps
* - changed:
* - "SND_DELAY" is now a float
*
* v1.4.0:
* - added:
* - option to load packages of sounds, packages cycle with each map-change (packages must be numbered)
* - ability to ban people from using sounds (only for current map) ( amx_sound_ban <player> <1/0 OR on/off> )
* - changed:
* - precache method changed
* - all keywords are now stored into buffer, even those sounds that are not precached
* - code improvements
*
* v1.4.1:
* - fixed:
* - when setting DISPLAY_KEYWORDS to 0 chat was disabled
*
* v1.4.2:
* - fixed:
* - players could be banned from sounds after reconnect
* - added:
* - option to include sounds from "half-life.gcf" and <current mod>.gcf
*
* v1.4.2b:
* - fixed:
* - compile error when disabling mp3 support
*
* v1.4.3:
* - fixed:
* - keywords without or with wrong files will not be added anymore
* - possible errors fixed
* - error with MOTD display fixed
*
* v1.4.5:
* - fixed:
* - ADMINS_ONLY was not working always
* - players could only play less sound than specified in SND_MAX
* - runtime error with amx_sound_reload
* - added:
* - sounds can now also be used in team chat
* - amx_sound_unban to unban players
* - changed:
* - keyword check tweaked
* - amx_sound_ban now do not expect additional parameter "on / off" or "1 / 0"
*
* v1.4.7:
* - fixed:
* - keywords with admin and public sounds, could block normal players from playing normal sounds
* - runtime error which could stop plugin to work
* - message telling players to wait till next sound can be played is not displayed on every word anymore
*
* v1.5.0: ( AmxModX 1.71 or better ONLY )
* - fixed:
* - sounds being not in a subfolder ( eg: sound/mysound.wav ) will now be played
* - reconnecting to reset quota will not work anymore
* - no more overlapping sounds ( Join and Exit sounds will still overlap other but others cannot overlap them )
* - amx_sound_reset now accepts IDs too
* - sound quota could be increased even if no sound was played
* - added:
* - sound duration is now calculated
* - changed:
* - SND_DELAY does not affect admins anymore
* - SND_SPLIT has been replaced with more customizable SND_MODE
* - removed support to disable MP3
*
* v1.5.0b:
* - fixed:
* - rare runtime error
*
* v1.5.1:
* - fixed:
* - calculation for MP3's encoded with MPEG 2
* - added:
* - saying "/soundlist" will now show sound list like "/sounds" does
* - CVAR: "mp_sank_sounds_obey_duration" to determine if sounds may overlap or not ( default: 1 = do not overlap )
*
* v1.5.1b:
* - fixed:
* - runtime error in mp3 calculation
*
* v1.5.2:
* - fixed:
* - support for SND_DELAY was accidently removed
* - some possible minor bugs
* - added:
* - SND_MAX_DUR: maximum of seconds a player can play sounds each map
* - two new options for SND_MODE ( read help for more information )
*
* v1.5.3:
* - fixed:
* - admin being able to play sounds when "mp_sank_sounds_obey_duration" was on
* - added:
* - CVAR: "mp_sank_sounds_motd_address" to use a website to show all sounds ( empty cvar = no website will be used )
*
* v1.5.4:
* - fixed:
* - error in mp3 calculation
* - when using "mapnameonly" option, following options have been ignored
* - added:
* - minor detection for damaged/invalid files
* - changed:
* - both "SND-LIST.CFG" and "snd-list.cfg" will work now ( linux )
* - code improvements
* - faster config parsing/writing
*
* v1.5.5:
* - fixed:
* - error in mp3 calculation ( once again
)
* - added:
* - additional debug info for mp3's when compiled in DEGUB_MODE 1
*
* v1.5.6:
* - fixed:
* - sounds located in <MODDIR>/sounds/ (no subfolder) not being played if dead and alive not being splitted
* - long lines not being parsed correctly
* - players could play one more sound than allowed
*
* v1.6.0: (16.4.2007)
* - fixed:
* - speech sounds not being played
* - join / exit sound duration was incorrect
* - SND_WARN / SND_MAX error checking could display wrong error
* - added:
* - access can be defined for every sound and keyword seperately
* - changed:
* - partly rewritten
* - way of saving data
* - sounds when enabling and disabling Sank Sounds are not precached anymore ( hard coded )
* - many code improvements
*
* v1.6.2: (16.01.2008)
* - fixed:
* - removed debug message
* - admins are not included in overlapping check anymore
* - non admins could see sounds that are for admins only
* - bug when adding and removing sounds ingame to list (wierd keywords and sounds)
* - added:
* - "PLAY_COUNT_KEY" and "PLAY_COUNT" to data structure to count how often a key and sound has been used
* - messages for players when enabling/disabling sounds and if players have to wait cause of delay
* - changed:
* - sank sounds is now precaching sounds after plugin init (fakemeta modul needed)
* - no more engine, but therefore fakemeta is needed
* - minor code tweaks
*
* v1.6.3: (29.02.2008)
* - fixed:
* - runtime error if more sounds added than defined in MAX_KEYWORDS
* - commenting SND_JOIN and SND_EXIT (adding # or // infront of them) made the following sounds to be added to these options
* - changed:
* - CVAR "mp_sank_sounds_obey_duration" is now a bitmask (see readme.txt)
*
* v1.6.4: (21.12.2008)
* - added:
* - warning for unsupported mp3 files
* - changed:
* - mp3 detection code rewritten
*
* v1.6.5: (14.01.2009)
* - fixed:
* - wav detection for bad files
*
* v1.6.5b: (22.01.2009)
* - changed:
* - removed warning for unsupported mp3s (they are supported)
*
* v1.6.6: (03.03.2009)
* - fixed:
* - last entry in configfile was not sorted
* - runtime error with keywords without any sound
* - exploit where SND_JOIN and SND_EXIT could be used as keywords
* - changed:
* - SND_JOIN and SND_JOIN do not have to be before any other keyword
*
* v1.6.6b: (29.03.2009)
* - fixed:
* - runtime error
* - if SND_JOIN or SND_JOIN was not at the beginning and more sounds were added afterwards, those new sounds overwrote previous sounds
*
* v1.6.6c: (30.06.2009)
* - fixed:
* - removed debug message
*
* v1.6.6d: (03.07.2009)
* - fixed:
* - speech files not being played
*
* v1.7.0: (08.08.2011)
* - added:
* - further checks for bad configfiles
* - new option SND_IMMUNITY (defines all levels that shall get immunity)
* - info when used last sound
* - amx_sound_top <x> shows the top <x> (default 10) most played keywords during current map
* - using keywords followed by a ! (eg: haha!) will play the sound bound to a location (WAVs only)
* you can move away from the sound. NO config change needed
* - changed:
* - WAVs are not bound to <mod-dir>/sound folder anymore (config change needed unfortunately)
* all sounds now need the full path (eg: haha; sound/misc/haha.wav)
*
* v1.7.1: (11.08.2011)
* - fixed:
* - WAVs not downloading and producing error messages
*
* IMPORTANT:
* a) if u want to use the internal download system do not use more than 200 sounds (HL cannot handle it)
* (also depending on map, you may need to use even less)
* but if u disable the internal download system u can use as many sounds as the plugin can handle
* (max should be over 100000 sounds (depending on the Array Defines ), BUT the plugin speed
* is another question with thousands of sounds
)
*
* b) File has to look like this:
* SND_MAX; 20
* SND_MAX_DUR; 180.0
* SND_WARN; 17
* SND_JOIN; misc/hi.wav
* SND_EXIT; misc/comeagain.wav
* SND_DELAY; 0.0
* SND_MODE; 15
* SND_IMMUNITY; "l"
* EXACT_MATCH; 1
* ADMINS_ONLY; 0
* DISPLAY_KEYWORDS; 1
*
* # Word/Sound combinations:
* crap; misc/awwcrap.Wav;misc/awwcrap2.wav
* woohoo; misc/woohoo.wav
* @ha ha; misc/haha.wav
* @abm@godlike; misc/godlike.wav
* doh; misc/doh.wav;misc/doh2.wav;@misc/doh3.wav
* mp3; sound/mymp3.mp3;music/mymp3s/number2.mp3;mainfolder.mp3
* target; "target destroyed"
*
* mapname TESTMAP
* testmap; misc/doh.wav
* mapname TESTMAP2
* testmap2; misc/haha.wav;sound/mymp3.mp3
* testmap3; misc/hi.wav
*
* package 1
* haha2; misc/haha.wav
* doh3; misc/doh3.wav
* package 2
* hi; misc/hi.wav
*
* modspecific
* <keyword>; <location>/<name>.wav
*
* Follow these instructions
* wavs:
* - base directory is "mod-dir/sound/"
* - put EXACT PATH to the wav beginning from base directory (eg misc/test.wav or test2.wav)
* mp3:
* - base directory is "mod-dir/"
* - put the EXACT PATH to the mp3 (eg sound/test.mp3 or music/mymp3s/test2.mp3 or mainfolder.mp3)
* speech:
* - base directory is "mod-dir/sound/vox/"
* - these files are inside the steam package
* - for a list look at c)
* mapname:
* - type mapname <space> the real mapname (without .bsp)
* - everthing below will be loaded only on this map
* package:
* - type package <space> number
* - everthing below will be loaded only once and switched to next package on map-change
* - if only 1 package this package will be used every map-change
* modspecific:
* - every sound below that line must be inside half-life.gcf or <yourmod>.gcf
* - if you add other files then said above they may/will crash your server as these sounds are assumed to be existent
*
* c) speech sounds must be put in quotes (eg: target; "target destroyed")
* you may not put different speech types into 1 speech or the speech wont be played
* speech without directory is used from "vox/.."
* first specify the speech type (ONLY ONCE eg hgrunt/) and then put the words with spaces between each speech
* eg "hgrunt/yessir barney/stop1" will not work as 2 different speeches
* BUT "hgrunt/yessir no" will work
* get all available speech sounds here:
* "
http://www.adminmod.org/help/online/Admin_Mod_Reference/Half_Life_Sounds.htm"
;
*
* d) "@" infront of a
* - word means only admin can use this word
* - wav/mp3/speech/word means players can use the word but this sound is only played by admins
*
* e) custom admin access:
* - infront of a word/sound add @<ACCESS_LEVELS>@
* - replace <ACCESS_LEVELS> with the access levels you desire
* - @abc@ means: everyone with access a, b or c can use it
***************************************************************************/
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
// set this to 1 to get some debug messages
#define DEBUG_MODE 0
// turn this off to stop list from being sorted by keywords in alphabetic order
#define ALLOW_SORT 1
// Array Defines, ATTENTION: ( MAX_RANDOM + 1 ) * TOK_LENGTH must be smaller 2048 !!!
#define MAX_KEYWORDS 80 // Maximum number of keywords ( ATTENTION: 2 are reserved )
#define MAX_RANDOM 15 // Maximum number of sounds per keyword
#define TOK_LENGTH 60 // Maximum length of keyword and sound file strings
#define MAX_BANS 32 // Maximum number of bans stored
#define NUM_PER_LINE 6 // Number of words per line from amx_sound_help
#define BUFFER_LEN TOK_LENGTH * MAX_RANDOM
//#pragma dynamic 16384
#pragma dynamic 65536
#define ACCESS_ADMIN ADMIN_LEVEL_A
#define PLUGIN_AUTHOR "White Panther, Luke Sankey, HunteR"
#define PLUGIN_VERSION "1.7.1"
new Enable_Sound[] = "misc/woohoo.wav" // Sound played when Sank Soounds being enabled
new Disable_Sound[] = "misc/awwcrap.wav" // Sound played when Sank Soounds being disabled
new config_filename[128]
new SndCount[33] = {0, ...} // Holds the number telling how many sounds a player has played
new Float:SndLenghtCount[33] = {0.0, ...}
new SndOn[33] = {1, ...}
new SND_WARN = 0 // The number at which a player will get warned for playing too many sounds
new SND_MAX = 0 // The number at which a player will get kicked for playing too many sounds
new Float:SND_MAX_DUR = 0.0
new Float:SND_DELAY = 0.0 // Minimum delay between sounds
new SND_MODE = 15 // Determinates who can play and who can hear sounds (dead and alive)
new SND_IMMUNITY = ACCESS_ADMIN // Determine the access levels which shall have immunity to warn/kick/ban (default ACCESS_ADMIN for backwards compatability)
new EXACT_MATCH = 1 // Determinates if plugin triggers on exact match, or partial speech match
new ADMINS_ONLY = 0 // Determinates if only admins are allowed to play sounds
new DISPLAY_KEYWORDS = 1 // Determinates if keywords are shown in chat or not
new Float:NextSoundTime // spam protection
new Float:Join_exit_SoundTime // spam protection 2
new Float:LastSoundTime = 0.0
new bSoundsEnabled = 1 // amx_sound <on/off> or <1/0>
new CVAR_freezetime, CVAR_obey_duration
new g_max_players
new banned_player_steamids[MAX_BANS][60]
new restrict_playing_sounds[33]
new sound_quota_steamids[33][60]
new motd_sound_list_address[128]
enum
{
PARSE_SND_MAX,
PARSE_SND_MAX_DUR,
PARSE_SND_WARN,
PARSE_SND_DELAY,
PARSE_SND_MODE,
PARSE_SND_IMMUNITY,
PARSE_EXACT_MATCH,
PARSE_ADMINS_ONLY,
PARSE_DISPLAY_KEYWORDS,
PARSE_KEYWORD
}
enum
{
ERROR_NONE,
ERROR_MAX_KEYWORDS,
ERROR_STRING_LENGTH
}
enum
{
FLAG_IGNORE_AMOUNT = 1
}
enum
{
SOUND_TYPE_SPEECH,
SOUND_TYPE_MP3,
SOUND_TYPE_WAV,
SOUND_TYPE_WAV_LOCAL
}
enum SOUND_DATA_BASE
{
KEYWORD[TOK_LENGTH],
ADMIN_LEVEL_BASE,
SOUND_AMOUNT,
FLAGS,
PLAY_COUNT_KEY,
KEY_SOUNDS[BUFFER_LEN],
Float:DURATION[MAX_RANDOM],
ADMIN_LEVEL[MAX_RANDOM],
SOUND_TYPE[MAX_RANDOM],
PLAY_COUNT[MAX_RANDOM],
SOUND_DATA_BASE_END
}
new sound_data[MAX_KEYWORDS][SOUND_DATA_BASE]
public plugin_init( )
{
register_plugin("Sank Sounds Plugin", PLUGIN_VERSION, PLUGIN_AUTHOR)
register_cvar("sanksounds_version", PLUGIN_VERSION, FCVAR_SERVER)
set_cvar_string("sanksounds_version", PLUGIN_VERSION)
register_concmd("amx_sound_reset", "amx_sound_reset", ACCESS_ADMIN, " <user | all> : Resets sound quota for ^"user^", or everyone if ^"all^"")
register_concmd("amx_sound_add", "amx_sound_add", ACCESS_ADMIN, " <keyword> <dir/sound> : Adds a Word/Sound combo to the sound list")
register_clcmd("amx_sound_help", "amx_sound_help")
register_concmd("amx_sound", "amx_sound", ACCESS_ADMIN, " : Turns sounds on/off")
register_concmd("amx_sound_play", "amx_sound_play", ACCESS_ADMIN, " <dir/sound> : Plays sound to all users")
register_concmd("amx_sound_reload", "amx_sound_reload", ACCESS_ADMIN, " : Reloads config file. Filename is optional. If no filename, default is loaded")
register_concmd("amx_sound_remove", "amx_sound_remove", ACCESS_ADMIN, " <keyword> <dir/sound> : Removes a Word/Sound combo from the sound list. Must use quotes")
register_concmd("amx_sound_write", "amx_sound_write", ACCESS_ADMIN, " : Writes current sound configuration to file")
register_concmd("amx_sound_debug", "amx_sound_debug", ACCESS_ADMIN, "prints the whole Word/Sound combo list")
register_concmd("amx_sound_ban", "amx_sound_ban", ACCESS_ADMIN, " <name or #userid>: Bans player from using sounds for current map")
register_concmd("amx_sound_unban", "amx_sound_unban", ACCESS_ADMIN, " <name or #userid>: Unbans player from using sounds for current map")
register_concmd("amx_sound_top", "amx_sound_top", ACCESS_ADMIN, " <number> (optional): Shows the top X (default 10) most used keywords during this map")
register_clcmd("say", "HandleSay")
register_clcmd("say_team", "HandleSay")
register_cvar("mp_sank_sounds_download", "1")
CVAR_freezetime = register_cvar("mp_sank_sounds_freezetime", "0")
CVAR_obey_duration = register_cvar("mp_sank_sounds_obey_duration", "1")
register_cvar("mp_sank_sounds_motd_address", "")
g_max_players = get_maxplayers()
}
public plugin_cfg( )
{
get_cvar_string("mp_sank_sounds_motd_address", motd_sound_list_address, 127)
new configpath[61]
get_configsdir(configpath, 60)
format(config_filename, 127, "%s/SND-LIST.CFG", configpath) // Name of file to parse
// check if file in capital letter exists
// otherwise make it all lowercase and try to load it
if ( file_exists(config_filename) )
{
parse_sound_file(config_filename)
}else
{
strtolower(config_filename)
parse_sound_file(config_filename)
}
}
public client_putinserver( id )
{
restrict_playing_sounds[id] = -1
new steamid[60], i
get_user_authid(id, steamid, 59)
for ( i = 0; i < MAX_BANS; ++i )
{
if ( equal(steamid, banned_player_steamids[i]) )
restrict_playing_sounds[id] = i
}
if ( !equal(steamid, sound_quota_steamids[id]) )
{
copy(sound_quota_steamids[id], 59, steamid)
SndCount[id] = 0
SndLenghtCount[id] = 0.0
}
SndOn[id] = 1
new Float:gametime = get_gametime()
if ( gametime <= get_pcvar_num(CVAR_freezetime) )
return
if ( sound_data[0][SOUND_AMOUNT] == 0 )
return
if ( Join_exit_SoundTime >= gametime )
return
new rand = random(sound_data[0][SOUND_AMOUNT])
new playFile[TOK_LENGTH]
copy(playFile, TOK_LENGTH, sound_data[0][KEY_SOUNDS][TOK_LENGTH * rand])
if ( sound_data[0][ADMIN_LEVEL][rand] != 0
&& !(get_user_flags(id) & sound_data[0][ADMIN_LEVEL][rand]) )
return
playsoundall(playFile, sound_data[0][SOUND_TYPE][rand])
Join_exit_SoundTime = gametime + sound_data[0][DURATION][rand]
if ( NextSoundTime < Join_exit_SoundTime )
NextSoundTime = Join_exit_SoundTime
}
public client_disconnect( id )
{
SndOn[id] = 1
restrict_playing_sounds[id] = -1
new Float:gametime = get_gametime()
if ( gametime <= get_pcvar_num(CVAR_freezetime) )
return
if ( sound_data[1][SOUND_AMOUNT] == 0 )
return
if ( Join_exit_SoundTime >= gametime )
return
new rand = random(sound_data[1][SOUND_AMOUNT])
new playFile[TOK_LENGTH]
copy(playFile, TOK_LENGTH, sound_data[1][KEY_SOUNDS][TOK_LENGTH * rand])
if ( sound_data[1][ADMIN_LEVEL][rand] != 0
&& !(get_user_flags(id) & sound_data[1][ADMIN_LEVEL][rand]) )
return
playsoundall(playFile, sound_data[1][SOUND_TYPE][rand])
Join_exit_SoundTime = gametime + sound_data[1][DURATION][rand]
if ( NextSoundTime < Join_exit_SoundTime )
NextSoundTime = Join_exit_SoundTime
}
public amx_sound_reset( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new arg[33], target
read_argv(1, arg, 32)
if ( equal(arg, "all") == 1 )
{
client_print(id, print_console, "Sank Sounds >> Quota has been reseted for all players")
for ( target = 1; target <= g_max_players; ++target )
{
SndCount[target] = 0
SndLenghtCount[target] = 0.0
}
}else
{
target = cmd_target(id, arg, 1)
if ( !target )
return PLUGIN_HANDLED
SndCount[target] = 0
SndLenghtCount[target] = 0.0
new name[33]
get_user_name(target, name, 32)
client_print(id, print_console, "Sank Sounds >> Quota has been reseted for ^"%s^"", name)
}
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Adds a Word/Sound combo to the list. If it is a valid line in the config
// file, then it is a valid parameter here. The only difference is you can
// only specify one Sound file at a time with this command.
//
// Usage: amx_sound_add <keyword> <dir/sound>
// Usage: amx_sound_add <setting> <value>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_add( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new Word[TOK_LENGTH + 1], Sound[TOK_LENGTH + 1]
new configOption = 0
read_argv(1, Word, TOK_LENGTH)
read_argv(2, Sound, TOK_LENGTH)
if ( strlen(Word) <= 0
|| strlen(Sound) == 0 )
{
client_print(id, print_console, "Sank Sounds >>Invalid format")
client_print(id, print_console, "Sank Sounds >>USAGE: amx_sound_add keyword <dir/sound>")
return PLUGIN_HANDLED
}
// First look for special parameters
if ( equali(Word, "SND_MAX") )
{
SND_MAX = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "SND_MAX_DUR") )
{
SND_MAX_DUR = floatstr(Sound)
configOption = 1
}else if ( equali(Word, "SND_WARN") )
{
SND_WARN = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "SND_DELAY") )
{
SND_DELAY = floatstr(Sound)
configOption = 1
}else if ( equali(Word, "SND_MODE") )
{
SND_MODE = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "SND_IMMUNITY") )
{
SND_IMMUNITY = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "EXACT_MATCH") )
{
EXACT_MATCH = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "ADMINS_ONLY") )
{
ADMINS_ONLY = str_to_num(Sound)
configOption = 1
}else if ( equali(Word, "DISPLAY_KEYWORDS") )
{
DISPLAY_KEYWORDS = str_to_num(Sound)
configOption = 1
}
if ( configOption )
{
// Do some error checking on the user-input numbers
ErrorCheck()
return PLUGIN_HANDLED
}
// Loop once for each keyword
new i, j
for( i = 0; i < MAX_KEYWORDS; ++i )
{
// If an empty string, then break this loop
if ( strlen(sound_data[i][KEYWORD]) == 0 )
break
// If no match found, keep looping
if ( !equal(Word, sound_data[i][KEYWORD], TOK_LENGTH) )
continue
// See if the Sound already exists
for( j = 0; j < MAX_RANDOM; ++j )
{
// If an empty string, then break this loop
if ( strlen(sound_data[i][KEY_SOUNDS][TOK_LENGTH * j]) == 0 )
break
// See if this is the same as the new Sound
if ( equali(Sound, sound_data[i][KEY_SOUNDS][TOK_LENGTH * j], TOK_LENGTH) )
{
client_print(id, print_console, "Sank Sounds >> ^"%s; %s^" already exists", Word, Sound)
return PLUGIN_HANDLED
}
}
// If we reached the end, then there is no room
if ( j >= MAX_RANDOM - 1 )
client_print(id, print_console, "Sank Sounds >> No room for new Sound. Increase MAX_RANDOM and recompile")
else
{
// Word exists, but Sound is new to the list, so add entry
array_add_inner_element(i, j, Sound)
client_print(id, print_console, "Sank Sounds >> ^"%s^" successfully added to ^"%s^"", Sound, Word)
}
return PLUGIN_HANDLED
}
// If we reached the end, then there is no room
if ( i >= MAX_KEYWORDS )
client_print(id, print_console, "Sank Sounds >> No room for new Word/Sound combo. Increase MAX_KEYWORDS and recompile")
else
{
// Word/Sound combo is new to the list, so make a new entry
array_add_element(i, Word)
array_add_inner_element(i, j, Sound)
client_print(id, print_console, "Sank Sounds >> ^"%s; %s^" successfully added", Word, Sound)
}
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// amx_sound_help lists all amx_sound commands and keywords to the user.
//
// Usage: amx_sound_help
//////////////////////////////////////////////////////////////////////////////
public amx_sound_help( id )
{
print_sound_list(id)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Turns on/off the playing of the Sound files for this plugin only
//////////////////////////////////////////////////////////////////////////////
public amx_sound( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new onoff[5]
read_argv(1, onoff, 4)
if ( equal(onoff, "on")
|| equal(onoff, "1") )
{
if ( bSoundsEnabled == 1 )
console_print(id, "Sank Sounds >> Plugin already enabled")
else
{
bSoundsEnabled = 1
console_print(id, "Sank Sounds >> Plugin enabled")
client_print(0, print_chat, "[REDIRECTME]Pluginul este activat")
if ( Enable_Sound[0] )
{
new type = Enable_Sound[0] == '^"' ? SOUND_TYPE_SPEECH : ( Enable_Sound[strlen(Enable_Sound) - 1] == '3' ? SOUND_TYPE_MP3 : SOUND_TYPE_WAV )
playsoundall(Enable_Sound, type)
}
}
return PLUGIN_HANDLED
}else if ( equal(onoff, "off")
|| equal(onoff, "0") )
{
if ( bSoundsEnabled == 0 )
console_print(id, "Sank Sounds >> Plugin already disabled")
else
{
bSoundsEnabled = 0
console_print(id, "Sank Sounds >> Plugin disabled")
client_print(0, print_chat, "[REDIRECTME]Pluginul este dezactivat")
if ( Disable_Sound[0] )
{
new type = Disable_Sound[0] == '^"' ? SOUND_TYPE_SPEECH : ( Disable_Sound[strlen(Disable_Sound) - 1] == '3' ? SOUND_TYPE_MP3 : SOUND_TYPE_WAV )
playsoundall(Disable_Sound, type)
}
}
}
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Plays a sound to all players
//
// Usage: amx_sound_play <dir/sound>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_play( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new arg[128]
read_argv(1, arg, 127)
if ( strlen(arg) < 1 )
{
client_print(id, print_console, "Sank Sounds >> Sound is invalid.")
return PLUGIN_HANDLED
}
new type = arg[0] == '^"' ? SOUND_TYPE_SPEECH : ( arg[strlen(arg) - 1] == '3' ? SOUND_TYPE_MP3 : SOUND_TYPE_WAV )
playsoundall(arg, type)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Reloads the Word/Sound combos from filename
//
// Usage: amx_sound_reload <filename>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_reload( id , level , cid )
{
if ( !cmd_access(id, level, cid, 0) )
return PLUGIN_HANDLED
new parsefile[128]
read_argv(1, parsefile, 127)
// Initialize sound_data array
for( new i = 0; i < MAX_KEYWORDS; ++i )
array_clear_element(i)
parse_sound_file(parsefile, 0)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Removes a Word/Sound combo from the list. You must specify a keyword, but it
// is not necessary to specify a Sound if you want to remove all Sounds associated
// with that keyword
//
// Usage: amx_sound_remove <keyWord> <dir/sound>"
//////////////////////////////////////////////////////////////////////////////
public amx_sound_remove( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new Word[TOK_LENGTH + 1], Sound[TOK_LENGTH + 1]
read_argv(1, Word, TOK_LENGTH)
read_argv(2, Sound, TOK_LENGTH)
if ( strlen(Word) == 0 )
{
client_print(id, print_console, "Sank Sounds >> Invalid format")
client_print(id, print_console, "Sank Sounds >> USAGE: amx_sound_remove keyword <dir/sound>")
return PLUGIN_HANDLED
}
// speech must have extra ""
if ( strlen(Sound) != 0
&& containi(Sound, ".wav") == -1
&& containi(Sound, ".mp") == -1 )
format(Sound, TOK_LENGTH, "^"%s^"", Sound)
// Loop once for each keyWord
new iCurWord, jCurSound
for( iCurWord = 0; iCurWord < MAX_KEYWORDS; ++iCurWord )
{
// If an empty string, then break this loop, we're at the end
if ( strlen(sound_data[iCurWord][KEYWORD]) == 0 )
break
// Look for a Word match
if ( !equali(Word, sound_data[iCurWord][KEYWORD], TOK_LENGTH) )
continue
// If no Sound was specified, then remove the whole Word's entry
if ( strlen(Sound) == 0 )
{
// special check for join / exit keywords
if ( iCurWord < 2 )
{
// safe join / exit data
new temp_char = sound_data[iCurWord][KEYWORD][0]
new temp_flag = sound_data[iCurWord][FLAGS]
// Delete the last data
array_clear_element(iCurWord)
// restore data
sound_data[iCurWord][KEYWORD][0] = temp_char
sound_data[iCurWord][FLAGS] = temp_flag
// We reached the end
client_print(id, print_console, "Sank Sounds >> %s successfully cleared", Word)
return PLUGIN_HANDLED
}
array_remove(iCurWord)
client_print(id, print_console, "Sank Sounds >> %s successfully removed", Word)
return PLUGIN_HANDLED
}
// Just remove the one Sound, if it exists
for( jCurSound = 0; jCurSound < MAX_RANDOM; ++jCurSound )
{
// If an empty string, then break this loop, we're at the end
if ( !strlen(sound_data[iCurWord][KEY_SOUNDS][TOK_LENGTH * jCurSound]) )
break
// Look for a Sound match
if ( !equali(Sound, sound_data[iCurWord][KEY_SOUNDS][TOK_LENGTH * jCurSound], TOK_LENGTH) )
continue
if ( sound_data[iCurWord][SOUND_AMOUNT] == 1 ) // If this is the only Sound entry, then remove the entry altogether
{
array_remove(iCurWord)
client_print(id, print_console, "Sank Sounds >> %s successfully removed", Word)
}else
{
array_remove_inner(iCurWord, jCurSound)
client_print(id, print_console, "Sank Sounds >> %s successfully removed from %s", Sound, Word)
}
return PLUGIN_HANDLED
}
// We reached the end for this Word, and the Sound didn't exist
client_print(id, print_console, "Sank Sounds >> %s not found", Sound)
return PLUGIN_HANDLED
}
// We reached the end, and the Word didn't exist
client_print(id, print_console, "Sank Sounds >> %s not found", Word)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Saves the current configuration of Word/Sound combos to filename for possible
// reloading at a later time. You cannot overwrite the default file.
//
// Usage: amx_sound_write <filename>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_write( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new savefile[128]
read_argv(1, savefile, 127)
if ( strlen(savefile) == 0 )
{
client_print(id, print_console, "Sank Sounds >> You must specify a filename")
return PLUGIN_HANDLED
}
// disallow to use same filename as the default config_filename
if ( equali(savefile, config_filename) )
{
client_print(id, print_console, "Sank Sounds >> Illegal write to default sound config file")
client_print(id, print_console, "Sank Sounds >> Specify a different filename")
return PLUGIN_HANDLED
}
new TimeStamp[128], name[33], Text[BUFFER_LEN + TOK_LENGTH]
new Textlen = BUFFER_LEN + TOK_LENGTH - 1
get_user_name(id, name, 32)
get_time("%H:%M:%S %A %B %d, %Y", TimeStamp, 127)
new file = fopen(savefile, "w+")
if ( !file )
{
log_amx("Sank Sounds >> Unable to read from ^"%s^" file", savefile)
return PLUGIN_HANDLED
}
formatex(Text, Textlen, "# TimeStamp:^t^t%s^n", TimeStamp)
fputs(file, Text)
formatex(Text, Textlen, "# File created by:^t%s^n", name)
fputs(file, Text)
fputs(file, "^n") // blank line
fputs(file, "# Important parameters:^n")
formatex(Text, Textlen, "SND_MAX;^t^t%d^n", SND_MAX)
fputs(file, Text)
formatex(Text, Textlen, "SND_MAX_DUR;^t^t%.1f^n", SND_MAX_DUR)
fputs(file, Text)
formatex(Text, Textlen, "SND_WARN;^t^t%d^n", SND_WARN)
fputs(file, Text)
new joinex_snd_buff[BUFFER_LEN]
cfg_write_keysound(0, joinex_snd_buff, BUFFER_LEN - 1)
formatex(Text, Textlen, "SND_JOIN;^t^t%s^n", joinex_snd_buff)
fputs(file, Text)
joinex_snd_buff[0] = 0
cfg_write_keysound(1, joinex_snd_buff, BUFFER_LEN - 1)
formatex(Text, Textlen, "SND_EXIT;^t^t%s^n", joinex_snd_buff)
fputs(file, Text)
formatex(Text, Textlen, "SND_DELAY;^t^t%f^n", SND_DELAY)
fputs(file, Text)
formatex(Text, Textlen, "SND_MODE;^t^t%d^n", SND_MODE)
fputs(file, Text)
new snd_imm_str[32]
get_flags(SND_IMMUNITY, snd_imm_str, 26)
formatex(Text, Textlen, "SND_IMMUNITY;^t^t^"%s^"^n", snd_imm_str)
fputs(file, Text)
formatex(Text, Textlen, "EXACT_MATCH;^t^t%d^n", EXACT_MATCH)
fputs(file, Text)
formatex(Text, Textlen, "ADMINS_ONLY;^t^t%d^n", ADMINS_ONLY)
fputs(file, Text)
formatex(Text, Textlen, "DISPLAY_KEYWORDS;^t%d^n", DISPLAY_KEYWORDS)
fputs(file, Text)
fputs(file, "^n") // blank line
fputs(file, "# Word/Sound combinations:^n")
for ( new i = 2; i < MAX_KEYWORDS; ++i ) // first 2 elements are reserved for Join / Exit sounds
{
// See if we reached the end
if ( strlen(sound_data[i][KEYWORD]) == 0 )
break
cfg_write_keyword(i, Text, Textlen)
cfg_write_keysound(i, Text, Textlen)
new text_len = strlen(Text)
if ( text_len + 2 <= BUFFER_LEN )
{
Text[text_len] = '^n' // add new line
Text[text_len + 1] = 0
}
// Now write the formatted string to the file
fputs(file, Text)
// And loop for the next Sound
}
fclose(file)
client_print(id, print_console, "Sank Sounds >> Configuration successfully written to %s", savefile)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Prints out Word/Sound combo matrix for debugging purposes. Kinda cool, even
// if you're not really debugging.
//
// Usage: amx_sound_debug
// Usage: amx_sound_reload <filename>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_debug( id , level , cid )
{
if ( !cmd_access(id, level, cid, 1)
&& id > 0 )
return PLUGIN_HANDLED
new i, j, join_snd_buff[BUFFER_LEN], exit_snd_buff[BUFFER_LEN]
if ( !is_dedicated_server()
&& id == 1 ) // for listenserver and with id = 1 we can use server_print
id = 0
if ( id )
client_print(id, print_console, "SND_WARN: %d^nSND_MAX: %d^nSND_MAX_DUR: %5.1f^n", SND_WARN, SND_MAX, SND_MAX_DUR)
else
server_print("SND_WARN: %d^nSND_MAX: %d^nSND_MAX_DUR: %5.1f^n", SND_WARN, SND_MAX, SND_MAX_DUR)
for( i = 0; i < MAX_RANDOM; ++i )
{
new tempstr[TOK_LENGTH]
if ( strlen(sound_data[0][KEY_SOUNDS][TOK_LENGTH * i]) )
{
formatex(tempstr, TOK_LENGTH, "%s;", sound_data[0][KEY_SOUNDS][TOK_LENGTH * i])
add(join_snd_buff, BUFFER_LEN, tempstr)
}
if ( strlen(sound_data[1][KEY_SOUNDS][TOK_LENGTH * i]) )
{
formatex(tempstr, TOK_LENGTH, "%s;", sound_data[1][KEY_SOUNDS][TOK_LENGTH * i])
add(exit_snd_buff, BUFFER_LEN, tempstr)
}
}
new snd_imm_str[32]
get_flags(SND_IMMUNITY, snd_imm_str, 26)
if ( id )
{
client_print(id, print_console, "SND_JOIN: %s", join_snd_buff)
client_print(id, print_console, "SND_EXIT: %s", exit_snd_buff)
client_print(id, print_console, "SND_DELAY: %f^nSND_MODE: %d^nSND_IMMUNITY: %s^nEXACT_MATCH: %d", SND_DELAY, SND_MODE, snd_imm_str, EXACT_MATCH)
client_print(id, print_console, "ADMINS_ONLY: %d^nDISPLAY_KEYWORDS: %d", ADMINS_ONLY, DISPLAY_KEYWORDS)
}else
{
server_print("SND_JOIN: %s^n", join_snd_buff)
server_print("SND_EXIT: %s^n", exit_snd_buff)
server_print("SND_DELAY: %f^nSND_MODE: %d^nSND_IMMUNITY: %s^nEXACT_MATCH: %d^n", SND_DELAY, SND_MODE, snd_imm_str, EXACT_MATCH)
server_print("ADMINS_ONLY: %d^nDISPLAY_KEYWORDS: %d^n", ADMINS_ONLY, DISPLAY_KEYWORDS)
}
// Print out the matrix of sound data, so we got what we think we did
for( i = 2; i < MAX_KEYWORDS; ++i ) // first 2 elements are reserved for Join / Exit sounds
{
if ( strlen(sound_data[i][KEYWORD]) == 0 )
break
new access_level[32]
get_flags(sound_data[i][ADMIN_LEVEL_BASE], access_level, 31)
if ( id )
client_print(id, print_console, "^n[%d] ^"%s^" with %d sound%s and level ^"%s^" (played: %d)", i - 2, sound_data[i][KEYWORD], sound_data[i][SOUND_AMOUNT], sound_data[i][SOUND_AMOUNT] > 1 ? "s" : "", access_level, sound_data[i][PLAY_COUNT_KEY])
else
server_print("^n[%d] ^"%s^" with %d sound%s and level ^"%s^" (played: %d)", i - 2, sound_data[i][KEYWORD], sound_data[i][SOUND_AMOUNT], sound_data[i][SOUND_AMOUNT] > 1 ? "s" : "", access_level, sound_data[i][PLAY_COUNT_KEY])
for( j = 0; j < MAX_RANDOM; ++j )
{
if ( strlen(sound_data[i][KEY_SOUNDS][j * TOK_LENGTH]) == 0 )
continue
get_flags(sound_data[i][ADMIN_LEVEL][j], access_level, 31)
if ( id )
client_print(id, print_console, " ^"%s^" - time: %5.2f - admin level ^"%s^" (played: %d)", sound_data[i][KEY_SOUNDS][j * TOK_LENGTH], sound_data[i][DURATION][j], access_level, sound_data[i][PLAY_COUNT][j])
else
server_print(" ^"%s^" - time: %5.2f - admin level ^"%s^" (played: %d)", sound_data[i][KEY_SOUNDS][j * TOK_LENGTH], sound_data[i][DURATION][j], access_level, sound_data[i][PLAY_COUNT][j])
}
}
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Bans players from using sounds for current map
//
// Usage: amx_sound_ban <player>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_ban( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new arg[33]
read_argv(1, arg, 32)
new player = cmd_target(id, arg, 1)
if ( !player )
return PLUGIN_HANDLED
if ( get_user_flags(player) & SND_IMMUNITY )
return PLUGIN_HANDLED
if ( restrict_playing_sounds[player] == -1 )
{
new found, empty = -1
new steamid[60]
get_user_authid(id, steamid, 59)
for ( new i = 0; i < MAX_BANS; ++i )
{
if ( empty == -1
&& !banned_player_steamids[i][0] )
empty = i
if ( !equal(steamid, banned_player_steamids[i]) )
continue
found = 1
break
}
if ( !found )
{
if ( empty == -1 )
empty = 0
copy(banned_player_steamids[empty], 59, steamid)
restrict_playing_sounds[player] = empty
}
}
new name[33]
get_user_name(player, name, 32)
client_print(id, print_console, "Sank Sounds >> Player ^"%s^" has been banned from using sounds", name)
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Unbans players from using sounds for current map
//
// Usage: amx_sound_unban <player>
//////////////////////////////////////////////////////////////////////////////
public amx_sound_unban( id , level , cid )
{
if ( !cmd_access(id, level, cid, 2) )
return PLUGIN_HANDLED
new arg[33]
read_argv(1, arg, 32)
new player = cmd_target(id, arg)
if ( !player )
return PLUGIN_HANDLED
if ( restrict_playing_sounds[player] != -1 )
{
new found = -1
new steamid[60]
get_user_authid(id, steamid, 59)
for ( new i = 0; i < MAX_BANS; ++i )
{
if ( !equal(steamid, banned_player_steamids[i]) )
continue
found = i
break
}
if ( found != -1 )
banned_player_steamids[found][0] = 0
restrict_playing_sounds[player] = -1
}
new name[33]
get_user_name(player, name, 32)
client_print(id, print_console, "Sank Sounds >> Player ^"%s^" has been unbanned from using sounds", name)
return PLUGIN_HANDLED
}
public amx_sound_top( id , level , cid )
{
if ( !cmd_access(id, level, cid, 1) )
return PLUGIN_HANDLED
new arg[33]
read_argv(1, arg, 32)
new topX = 10
if ( strlen(arg) > 0 )
topX = str_to_num(arg)
if ( topX < 1
|| topX > 50 )
{
client_print(id, print_console, "Sank Sounds >> Set a value from 1 to 50 or leave it blank")
return PLUGIN_HANDLED
}
new topIDs[50] = {-1, ...}
new topCount[50] = {0, ...}
for ( new keyIndex = 0; keyIndex < MAX_KEYWORDS; ++keyIndex )
{
// end of list reached
if ( sound_data[keyIndex][KEYWORD][0] == 0 )
break
for ( new i = 0; i < topX; ++i )
{
if ( sound_data[keyIndex][PLAY_COUNT_KEY] <= topCount[i] )
continue
// copy all other down
for ( new j = topX - 1; j > i; --j )
{
topIDs[j] = topIDs[j - 1]
topCount[j] = topCount[j - 1]
}
topIDs[i] = keyIndex
topCount[i] = sound_data[keyIndex][PLAY_COUNT_KEY]
break
}
}
new text[512]
new counter = 0
client_print(id, print_console, "Sank Sounds >> Top %d:", topX)
while ( counter < topX )
{
if ( topIDs[counter] != -1 )
format(text, 511, "%s(%d) %s^n", text, topCount[counter], sound_data[topIDs[counter]][KEYWORD])
else
counter = topX - 1
if ( (counter % 10 == 0
&& counter != 0 )
|| counter == topX - 1 )
{
client_print(id, print_console, text)
text[0] = 0
}
++counter
}
return PLUGIN_HANDLED
}
//////////////////////////////////////////////////////////////////////////////
// Everything a person says goes through here, and we determine if we want to
// play a sound or not.
//
// Usage: say <anything>
//////////////////////////////////////////////////////////////////////////////
public HandleSay( id )
{
// If sounds are not enabled, then skip this whole thing
if ( !bSoundsEnabled )
return PLUGIN_CONTINUE
// player is banned from playing sounds
if ( restrict_playing_sounds[id] != -1 )
return PLUGIN_CONTINUE
new Speech[128]
read_args(Speech, 127)
remove_quotes(Speech)
// credit to SR71Goku for fixing this oversight:
new speachLen = strlen(Speech)
if ( !speachLen )
return PLUGIN_CONTINUE
if ( equal(Speech, "/sound", 6) )
{
if ( Speech[6] == 's' )
{
if ( Speech[7] == 'o'
&& Speech[8] == 'n' )
{
SndOn[id] = 1
client_print(id, print_chat, "[REDIRECTME]Veti auzi toate sunetele din nou")
}else if ( Speech[7] == 'o'
&& Speech[8] == 'f'
&& Speech[9] == 'f'
&& Speech[10] == 0 )
{
SndOn[id] = 0
client_print(id, print_chat, "[REDIRECTME]Eu voi opri redarea sunetelor pentru tine")
}else if ( Speech[7] == 0 )
print_sound_list(id, 1)
else
return PLUGIN_CONTINUE
return PLUGIN_HANDLED
}else if ( Speech[6] == 'l'
&& Speech[7] == 'i'
&& Speech[8] == 's'
&& Speech[9] == 't'
&& Speech[10] == 0 )
{
print_sound_list(id, 1)
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
new ListIndex = -1
new pinToLocation = (Speech[speachLen - 1] == '!')
// Check to see if what the player said is a trigger for a sound
for ( new i = 2; i < MAX_KEYWORDS; ++i ) // first 2 elements are reserved for Join / Exit sounds
{
// end of list reached
if ( sound_data[i][KEYWORD][0] == 0 )
break
if ( equali(Speech, sound_data[i][KEYWORD])
|| (EXACT_MATCH == 1
&& pinToLocation == 1
&& speachLen == strlen(sound_data[i][KEYWORD]) + 1
&& equali(Speech, sound_data[i][KEYWORD], speachLen - 1) )
|| ( EXACT_MATCH == 0
&& containi(Speech, sound_data[i][KEYWORD]) != -1 ) )
{
// check for access
if ( sound_data[i][ADMIN_LEVEL_BASE] == 0
|| get_user_flags(id) & sound_data[i][ADMIN_LEVEL_BASE] )
ListIndex = i
break
}
}
// check If player used NO sound trigger
if ( ListIndex == -1 )
return PLUGIN_CONTINUE
new obey_duration_mode = get_pcvar_num(CVAR_obey_duration)
new admin_flags = get_user_flags(id)
new Float:gametime = get_gametime()
if ( gametime > NextSoundTime + SND_DELAY // 1. check for sound overlapping + delay time
|| ( admin_flags & ADMIN_RCON // 2. check if super admin
&& !(obey_duration_mode & 4) ) // 2b. check if super admin have to obey duration
|| ( admin_flags & SND_IMMUNITY // 3. check if admin
&& !(obey_duration_mode & 2) ) // 3b. check if admin have to obey duration
|| ( !(obey_duration_mode & 1) // 4. check if overlapping is allowed
&& gametime > LastSoundTime + SND_DELAY ) ) // 4b. or for delay time
{
// check if player is allowed to play sounds depending on config
new alive = is_user_alive(id)
if ( SND_MODE & ( alive + 1 )
&& !QuotaExceeded(id) ) // If the user has not exceeded their quota, then play a Sound
{
new rand = random(sound_data[ListIndex][SOUND_AMOUNT])
new timeout
new playFile[TOK_LENGTH]
// This for loop runs around until it finds a real file to play
// Defaults to the first Sound file, if no file is found at random.
for( timeout = MAX_RANDOM; // Initial condition
timeout >= 0 && !strlen(playFile); // While these are true
--timeout ) // Update each iteration
{
rand = random(sound_data[ListIndex][SOUND_AMOUNT])
// If for some reason we never find a file
// then default to the first Sound entry
if ( !timeout )
rand = 0
// check if sound has access defined, if so only allow admins to use it
if ( sound_data[ListIndex][ADMIN_LEVEL][rand] == 0
|| ( get_user_flags(id) & sound_data[ListIndex][ADMIN_LEVEL][rand] ) )
copy(playFile, TOK_LENGTH, sound_data[ListIndex][KEY_SOUNDS][rand * TOK_LENGTH])
}
if ( playFile[0] )
{
NextSoundTime = gametime + sound_data[ListIndex][DURATION][rand]
// Increment their playsound count
++SndCount[id]
SndLenghtCount[id] += sound_data[ListIndex][DURATION][rand]
// increment counter
++sound_data[ListIndex][PLAY_COUNT_KEY]
++sound_data[ListIndex][PLAY_COUNT][rand]
new type = sound_data[ListIndex][SOUND_TYPE][rand]
if ( pinToLocation == 1
&& type == SOUND_TYPE_WAV )
type = SOUND_TYPE_WAV_LOCAL
playsoundall(playFile, type, SND_MODE & 16, alive)
LastSoundTime = gametime
}
}
}else if ( gametime <= NextSoundTime + SND_DELAY
&& obey_duration_mode != 0 )
client_print(id, print_chat, "[REDIRECTME]Sunetul inca se aude ( asteapta %3.1f secunde )", NextSoundTime + SND_DELAY - gametime)
else
client_print(id, print_chat, "[REDIRECTME]Nu folosi?i prea des sunete ( asteapta %3.1f secunde )", LastSoundTime + SND_DELAY - gametime)
if ( DISPLAY_KEYWORDS == 0 )
return PLUGIN_HANDLED
return PLUGIN_CONTINUE
}
//////////////////////////////////////////////////////////////////////////////
// Parses the sound file specified by loadfile. If loadfile is empty, then
// it parses the default config_filename.
//////////////////////////////////////////////////////////////////////////////
parse_sound_file( loadfile[] , precache_sounds = 1 )
{
if ( !strlen(loadfile) )
copy(loadfile, 127, config_filename)
if ( !file_exists(loadfile) )
{
// file does not exist
log_amx("Sank Sounds >> Cannot find ^"%s^" file", loadfile)
return
}
new current_package_str[4]
new current_package, package_num
if ( vaultdata_exists("sank_sounds_current_package") )
{
get_vaultdata("sank_sounds_current_package", current_package_str, 3)
current_package = str_to_num(current_package_str)
}
new allowed_to_precache = 1, allow_check_existence = 1, allow_to_use_sounds = 1
new allow_global_precache = get_cvar_num("mp_sank_sounds_download")
new mapname[32]
get_mapname(mapname, 31)
new i
new ListIndex = -1
new tmpIndex = -1
new maxLineBuf_len = ( BUFFER_LEN + TOK_LENGTH ) - 1
new strLineBuf[BUFFER_LEN + TOK_LENGTH]
new error_code = ERROR_NONE
new parse_option = PARSE_KEYWORD
new temp_str[128]
new check_for_semi
new position
new file = fopen(loadfile, "r")
if ( !file )
{
log_amx("Sank Sounds >> Unable to read from ^"%s^" file", loadfile)
return
}
while ( fgets(file, strLineBuf, maxLineBuf_len) )
{
if ( (strLineBuf[0] == '^n') // empty line
|| ( strLineBuf[0] == 10 && strLineBuf[1] == '^n' ) // empty line
|| ( strLineBuf[0] == '/' && strLineBuf[1] == '/' ) // comment
|| (strLineBuf[0] == '#') ) // another comment
continue
trim(strLineBuf) // remove newline and spaces
if ( equali(strLineBuf, "package ", 8) )
{
++package_num
if ( current_package )
{
if ( current_package == str_to_num(strLineBuf[8]) )
allowed_to_precache = 1
else
allowed_to_precache = 0
}else
{
current_package = 1
allowed_to_precache = 1
}
allow_to_use_sounds = 1
allow_check_existence = 1
continue
}else if ( equali(strLineBuf, "mapname ", 8) )
{
if ( equali(strLineBuf[8], mapname) )
allowed_to_precache = 1
else
allowed_to_precache = 0
allow_to_use_sounds = 1
allow_check_existence = 1
continue
}else if ( equali(strLineBuf, "mapnameonly ", 12) )
{
if ( equali(strLineBuf[12], mapname) )
{
allowed_to_precache = 1
allow_to_use_sounds = 1
}else
{
allowed_to_precache = 0
allow_to_use_sounds = 0
}
allow_check_existence = 1
continue
}else if ( equali(strLineBuf, "modspecific", 11) )
{
allow_to_use_sounds = 1
allow_check_existence = 0
continue
}
if ( !allow_to_use_sounds ) // check for sounds that can be used only on specified map
continue
if ( ListIndex >= MAX_KEYWORDS )
{
log_amx("Sank Sounds >> Sound list truncated. Increase MAX_KEYWORDS. Stopped parsing file ^"%s^"^n", loadfile)
break
}
error_code = ERROR_NONE
position = 0
for( i = 0; i < MAX_RANDOM; ++i )
{
// check if reached end of buffer ( input has been parsed )
if ( position >= strlen(strLineBuf) )
{
strLineBuf[0] = 0
break
}
temp_str[0] = 0 // reset
check_for_semi = contain(strLineBuf[position], ";")
if ( check_for_semi != -1 )
{
copyc(temp_str, 127, strLineBuf[position], ';')
position += check_for_semi + 1
}else
{
copy(temp_str, 127, strLineBuf[position])
position += strlen(temp_str)
}
// Now remove any spaces or tabs from around the strings -- clean them up
trim(temp_str)
// check if file length is bigger than array
if ( strlen(temp_str) > TOK_LENGTH )
{
error_code = ERROR_STRING_LENGTH
break
}
if ( i == 0 )
{ // first entry is not a sound file
if ( equali(temp_str, "SND_MAX") )
parse_option = PARSE_SND_MAX
else if ( equali(temp_str, "SND_MAX_DUR") )
parse_option = PARSE_SND_MAX_DUR
else if ( equali(temp_str, "SND_WARN") )
parse_option = PARSE_SND_WARN
else if ( equali(temp_str, "SND_DELAY") )
parse_option = PARSE_SND_DELAY
else if ( equali(temp_str, "SND_MODE") )
parse_option = PARSE_SND_MODE
else if ( equali(temp_str, "SND_IMMUNITY") )
parse_option = PARSE_SND_IMMUNITY
else if ( equali(temp_str, "EXACT_MATCH") )
parse_option = PARSE_EXACT_MATCH
else if ( equali(temp_str, "ADMINS_ONLY") )
parse_option = PARSE_ADMINS_ONLY
else if ( equali(temp_str, "DISPLAY_KEYWORDS") )
parse_option = PARSE_DISPLAY_KEYWORDS
else
{
parse_option = PARSE_KEYWORD
if ( ListIndex != -1
&& sound_data[ListIndex][SOUND_AMOUNT] == 0
&& !(sound_data[ListIndex][FLAGS] & FLAG_IGNORE_AMOUNT) ) // check if allowed to ignore amount of sounds ( eg: SND_JOIN / SND_EXIT )
log_amx("Sank Sounds >> Found keyword without any valid sound. Skipping this keyword: ^"%s^"", sound_data[ListIndex][KEYWORD])
else
++ListIndex
if ( ListIndex >= MAX_KEYWORDS )
{
error_code = ERROR_MAX_KEYWORDS
break
}
new result = array_add_element(ListIndex, temp_str)
if ( result > -1 )
{
tmpIndex = result
--ListIndex
}else
{
tmpIndex = -1
if ( result == -1 )
ListIndex = 2
}
}
}else
{
switch ( parse_option )
{
case PARSE_SND_MAX:
{
SND_MAX = str_to_num(temp_str)
}
case PARSE_SND_MAX_DUR:
{
SND_MAX_DUR = floatstr(temp_str)
}
case PARSE_SND_WARN:
{
SND_WARN = str_to_num(temp_str)
}
case PARSE_SND_DELAY:
{
SND_DELAY = floatstr(temp_str)
}
case PARSE_SND_MODE:
{
SND_MODE = str_to_num(temp_str)
}
case PARSE_SND_IMMUNITY:
{
if ( temp_str[0] == '^"' )
{
new temp_str2[32]
copyc(temp_str2, 31, temp_str[1], '^"')
if ( strlen(temp_str2) == 0 )
SND_IMMUNITY = (1<<30)
else
SND_IMMUNITY = read_flags(temp_str2)
}else
SND_IMMUNITY = read_flags(temp_str)
}
case PARSE_EXACT_MATCH:
{
EXACT_MATCH = str_to_num(temp_str)
}
case PARSE_ADMINS_ONLY:
{
ADMINS_ONLY = str_to_num(temp_str)
}
case PARSE_DISPLAY_KEYWORDS:
{
DISPLAY_KEYWORDS = str_to_num(temp_str)
}
case PARSE_KEYWORD:
{
new error_value = -1
if ( tmpIndex != -1 )
error_value = array_add_inner_element(tmpIndex, i - 1, temp_str, allow_check_existence, allow_global_precache, precache_sounds, allowed_to_precache)
else
error_value = array_add_inner_element(ListIndex, i - 1, temp_str, allow_check_existence, allow_global_precache, precache_sounds, allowed_to_precache)
if ( error_value == -1 )
{
// sound could not be added, so clear that array entry
if ( tmpIndex != -1 )
array_clear_inner_element(tmpIndex, i - 1)
else
array_clear_inner_element(ListIndex, i - 1)
continue
}
}
}
}
}
// Error occured so skip Word/Sound Combo
if ( error_code == ERROR_MAX_KEYWORDS )
{
log_amx("Sank Sounds >> Sound list truncated. Increase MAX_KEYWORDS. Stopped parsing file ^"%s^"^n", loadfile)
break
}
if ( error_code == ERROR_STRING_LENGTH )
{
log_amx("Sank Sounds >> Skipping this word/sound combo. Word or Sound is too long: ^"%s^". Length is %i but max is %i (change name/remove spaces in config or increase TOK_LENGTH)", temp_str, strlen(temp_str), TOK_LENGTH)
continue
}
if ( error_code != ERROR_NONE )
{
log_amx("Sank Sounds >> Fatal Error")
continue
}
// If we finished MAX_RANDOM times, and strLineBuf[position] still has contents
// then we should have a bigger MAX_RANDOM
else if ( position < strlen(strLineBuf) )
{
log_amx("Sank Sounds >> Sound list partially truncated. Increase MAX_RANDOM. Continuing to parse file ^"%s^"^n", loadfile)
}
}
fclose(file)
if ( ListIndex != -1 )
{
if ( sound_data[ListIndex][SOUND_AMOUNT] == 0
&& !(sound_data[ListIndex][FLAGS] & FLAG_IGNORE_AMOUNT) ) // check if allowed to ignore amount of sounds ( eg: SND_JOIN / SND_EXIT )
{
log_amx("Sank Sounds >> Found keyword without any valid sound. Skipping this keyword: ^"%s^"", sound_data[ListIndex][KEYWORD])
sound_data[ListIndex][KEYWORD][0] = 0
--ListIndex
}
}
// Now we have all of the data from the text file in our data structures.
// Next we do some error checking, some setup, and we're done parsing!
ErrorCheck()
++current_package
if ( current_package > package_num )
current_package = 1
num_to_str(current_package, current_package_str, 3)
set_vaultdata("sank_sounds_current_package", current_package_str)
//++ListIndex
#if ALLOW_SORT == 1
if ( ListIndex > 1 )
sort_HeapSort(ListIndex - 1) // -2 cause first two are reserved for join/exit sounds
#endif
}
//////////////////////////////////////////////////////////////////////////////
// Returns 0 if the user is allowed to say things
// Returns 1 and mutes the user if the quota has been exceeded.
//////////////////////////////////////////////////////////////////////////////
QuotaExceeded( id )
{
// check if is admin
new admin_check = ( get_user_flags(id) & SND_IMMUNITY )
if ( ADMINS_ONLY && !admin_check )
return 1
// If the sound limitation is disabled, then return happily.
if ( admin_check )
return 0
if ( SND_MAX != 0 )
{
if ( SndCount[id] >= SND_MAX )
{
if ( SndCount[id] - 3 < SND_MAX )
{
client_print(id, print_chat, "[REDIRECTME]Ai fost avertizat, esti mut")
// player is already muted, we increament here to save a variable to protect player from "you are muted" spam ( only 3 warnings )
++SndCount[id]
}
return 1
}else if ( SndCount[id] >= SND_WARN )
{
if ( SndCount[id] + 1 == SND_MAX )
client_print(id, print_chat, "[REDIRECTME]Asta a fost ultimul tau sunet")
else
client_print(id, print_chat, "[REDIRECTME]A plecat inainte sa ia mute", SND_MAX - SndCount[id] - 1)
}
}
if ( SND_MAX_DUR != 0.0
&& SndLenghtCount[id] > SND_MAX_DUR )
return 1
return 0
}
//////////////////////////////////////////////////////////////////////////////
// Checks the input variables for invalid values
//////////////////////////////////////////////////////////////////////////////
ErrorCheck( )
{
// Can't have negative delay between sounds
if ( SND_DELAY < 0.0 )
{
log_amx("Sank Sounds >> SND_DELAY cannot be negative. Setting to value: 0")
SND_DELAY = 0.0
}
// If SND_MAX is zero, then sounds quota is disabled. Can't have negative quota
if ( SND_MAX < 0 )
{
SND_MAX = 0 // in case it was negative
log_amx("Sank Sounds >> SND_MAX cannot be negative. Setting to value: 0")
}
// If SND_MAX_DUR is zero, then sounds quota is disabled. Can't have negative quota
if ( SND_MAX_DUR < 0.0 )
{
SND_MAX_DUR = 0.0 // in case it was negative
log_amx("Sank Sounds >> SND_MAX_DUR cannot be negative. Setting to value: 0.0")
}
// If SND_WARN is zero, then we can't have warning every time a keyword is said,
// so we default to 3 less than max
else if ( ( SND_WARN <= 0 && SND_MAX != 0 )
|| SND_MAX < SND_WARN )
{
if ( SND_MAX < SND_WARN )
// And finally, if they want to warn after a person has been
// muted, that's silly, so we'll fix it.
log_amx("Sank Sounds >> SND_WARN cannot be higher than SND_MAX")
else if ( SND_WARN <= 0 )
log_amx("Sank Sounds >> SND_WARN cannot be set to zero")
if ( SND_MAX > 3 )
SND_WARN = SND_MAX - 3
else
SND_WARN = SND_MAX - 1
log_amx("Sank Sounds >> SND_WARN set to default value: %i", SND_WARN)
}
}
playsoundall( sound[] , type , split_dead_alive = 0 , sender_alive_status = 0 )
{
new alive
for( new i = 1; i <= g_max_players; ++i )
{
if ( !is_user_connected(i) )
continue
if ( is_user_bot(i) )
continue
if ( !SndOn[i] )
continue
alive = is_user_alive(i)
if ( !(SND_MODE & ( alive * 4 + 4 )) )
continue
if ( split_dead_alive
&& alive != sender_alive_status // make sure if splited both are in same group
&& !(SND_MODE & ( alive * 32 + 32 )) ) // OR check if different groups may hear each other
continue
if ( type == SOUND_TYPE_MP3 )
client_cmd(i, "mp3 play ^"%s^"", sound)
else if ( type == SOUND_TYPE_WAV_LOCAL )
client_cmd(i, "play ^"%s^"", sound)
else if ( type == SOUND_TYPE_SPEECH )
client_cmd(i, "spk %s", sound)
else
client_cmd(i, "spk ^"%s^"", sound)
}
}
print_sound_list( id , motd_msg = 0 )
{
new text[256], motd_buffer[2048], ilen, skip_for_loop
new info_text[64] = "say < keyword >: plays A sound. keYwords are listed Below:"
if ( strlen(motd_sound_list_address) > 3 ) // make sure at least you have something like: a.b (
http://a.b
)
{
copy(motd_buffer, 127, motd_sound_list_address)
skip_for_loop = 1
motd_msg = 1
}else if ( motd_msg )
ilen = format(motd_buffer, 2047, "<body bgcolor=#000000><font color=#FFB000><pre>%s^n", info_text)
else
client_print(id, print_console, info_text)
// Loop once for each keyword
new i, j = -1
for ( i = 2; i < MAX_KEYWORDS && skip_for_loop == 0; ++i ) // first 2 elements are reserved for Join / Exit sounds
{
// If an invalid string, then break this loop
if ( strlen(sound_data[i][KEYWORD]) == 0
|| strlen(sound_data[i][KEYWORD]) > TOK_LENGTH )
break
// check if player can see admin sounds
++j
new found_stricted = 0
if ( sound_data[i][ADMIN_LEVEL_BASE] == 0
|| get_user_flags(id) & sound_data[i][ADMIN_LEVEL_BASE] )
{
if ( motd_msg )
ilen += format(motd_buffer[ilen], 2047 - ilen, "%s", sound_data[i][KEYWORD])
else
add(text, 255, sound_data[i][KEYWORD])
}else
{
--j
found_stricted = 1
}
if ( !found_stricted )
{
if ( j % NUM_PER_LINE == NUM_PER_LINE - 1 )
{
// We got NUM_PER_LINE on this line,
// so print it and start on the next line
if ( motd_msg )
ilen += format(motd_buffer[ilen], 2047 - ilen, "^n")
else
{
client_print(id, print_console, "%s", text)
text[0] = 0
}
}else
{
if ( motd_msg )
ilen += format(motd_buffer[ilen], 2047 - ilen, " | ")
else
add(text, 255, " | ")
}
}
}
if ( motd_msg
&& strlen(motd_buffer) )
show_motd(id, motd_buffer)
else if ( strlen(text) )
client_print(id, print_console, text)
}
#if ALLOW_SORT == 1
// 4 functions for array sort ( by Bailopan ) ( customized to fit plugin )
sort_HeapSort( ListIndex )
{
new i
new aSize = ( ListIndex / 2 ) - 1
for ( i = aSize; i >= 0; --i )
sort_SiftDown(i, ListIndex - 1)
for ( i = ListIndex - 1; i >= 1; --i )
{
array_switch_elements(0, i)
sort_SiftDown(0, i - 1)
}
}
sort_compare( elem1 , elem2 )
{
// skip first 2 elements ( join / exit )
elem1 += 2
elem2 += 2
new i = 0
for ( i = 0; i < TOK_LENGTH; ++i )
{
if ( sound_data[elem1][KEYWORD][i] != sound_data[elem2][KEYWORD][i] )
{
if ( sound_data[elem1][KEYWORD][i] > sound_data[elem2][KEYWORD][i] )
return 1
return -1
}
}
return 0
}
sort_SiftDown( root , bottom )
{
new done, child
while ( ( root * 2 <= bottom ) && !done )
{
if ( root * 2 == bottom )
child = root * 2
else if ( sort_compare(root * 2, root * 2 + 1) > 0 )
child = root * 2
else
child = root * 2 + 1
if ( sort_compare(root, child) < 0 )
{
array_switch_elements(root, child)
root = child
}else
done = 1
}
}
array_switch_elements( element_one , element_two )
{
// skip first 2 elements ( join / exit )
element_one += 2
element_two += 2
new i
new temp_sounds[BUFFER_LEN]
new temp_keyword[TOK_LENGTH]
new temp_int, Float:temp_float, temp_access, temp_access_base, temp_type, temp_flags, temp_play_count
copy(temp_keyword, TOK_LENGTH, sound_data[element_one][KEYWORD])
for ( i = 0; i < BUFFER_LEN; ++i )
temp_sounds[i] = sound_data[element_one][KEY_SOUNDS][i]
temp_int = sound_data[element_one][SOUND_AMOUNT]
temp_access_base = sound_data[element_one][ADMIN_LEVEL_BASE]
temp_flags = sound_data[element_one][FLAGS]
temp_play_count = sound_data[element_one][PLAY_COUNT_KEY]
copy(sound_data[element_one][KEYWORD], TOK_LENGTH, sound_data[element_two][KEYWORD])
for ( i = 0; i < BUFFER_LEN; ++i )
sound_data[element_one][KEY_SOUNDS][i] = sound_data[element_two][KEY_SOUNDS][i]
sound_data[element_one][SOUND_AMOUNT] = sound_data[element_two][SOUND_AMOUNT]
sound_data[element_one][ADMIN_LEVEL_BASE] = sound_data[element_two][ADMIN_LEVEL_BASE]
sound_data[element_one][FLAGS] = sound_data[element_two][FLAGS]
sound_data[element_one][PLAY_COUNT_KEY] = sound_data[element_two][PLAY_COUNT_KEY]
copy(sound_data[element_two][KEYWORD], TOK_LENGTH, temp_keyword)
for ( i = 0; i < BUFFER_LEN; ++i )
sound_data[element_two][KEY_SOUNDS][i] = temp_sounds[i]
sound_data[element_two][SOUND_AMOUNT] = temp_int
sound_data[element_two][ADMIN_LEVEL_BASE] = temp_access_base
sound_data[element_two][FLAGS] = temp_flags
sound_data[element_two][PLAY_COUNT_KEY] = temp_play_count
for ( i = 0; i < MAX_RANDOM; ++i )
{
temp_float = sound_data[element_one][DURATION][i]
sound_data[element_one][DURATION][i] = _:sound_data[element_two][DURATION][i]
sound_data[element_two][DURATION][i] = _:temp_float
temp_access = sound_data[element_one][ADMIN_LEVEL][i]
sound_data[element_one][ADMIN_LEVEL][i] = sound_data[element_two][ADMIN_LEVEL][i]
sound_data[element_two][ADMIN_LEVEL][i] = temp_access
temp_type = sound_data[element_one][SOUND_TYPE][i]
sound_data[element_one][SOUND_TYPE][i] = sound_data[element_two][SOUND_TYPE][i]
sound_data[element_two][SOUND_TYPE][i] = temp_type
temp_play_count = sound_data[element_one][PLAY_COUNT][i]
sound_data[element_one][PLAY_COUNT][i] = sound_data[element_two][PLAY_COUNT][i]
sound_data[element_two][PLAY_COUNT][i] = temp_play_count
}
}
#endif
array_add_element( num , keyword[] )
{
new join_check = equali(keyword, "SND_JOIN")
new exit_check = equali(keyword, "SND_EXIT")
// if index is 0 or 1 but not the correct keyword then make sure to save in correct array position
if ( join_check == 0
&& exit_check == 0 )
{
if ( num == 0
|| num == 1 )
{
join_check = -1
exit_check = -1
num = 2
}
}else
{
if ( num > 1 )
{
if ( join_check != 0 )
{
num = 0
exit_check = -1
}else if ( exit_check != 0 )
{
num = 1
join_check = -1
}
}
}
if ( join_check > 0
|| exit_check > 0 )
sound_data[num][FLAGS] |= FLAG_IGNORE_AMOUNT
sound_data[num][ADMIN_LEVEL_BASE] = cfg_parse_access(keyword)
copy(sound_data[num][KEYWORD], TOK_LENGTH, keyword)
sound_data[num][PLAY_COUNT_KEY] = 0
return (join_check == -1 && exit_check == -1)
? -1
: (join_check == -1 || exit_check == -1)
? num : -2
}
array_add_inner_element( num , elem , soundfile[] , allow_check_existence = 1 , allow_global_precache = 0 , precache_sounds = 0 , allowed_to_precache = 0 )
{
sound_data[num][ADMIN_LEVEL][elem] = cfg_parse_access(soundfile)
sound_data[num][SOUND_TYPE][elem] = soundfile[0] == '^"' ? SOUND_TYPE_SPEECH : ( soundfile[strlen(soundfile) - 1] == '3' ? SOUND_TYPE_MP3 : SOUND_TYPE_WAV )
sound_data[num][PLAY_COUNT][elem] = 0
// check if not speech sounds
if ( soundfile[0] != '^"' )
{
new sound_file_name[TOK_LENGTH + 1 + 10]
new is_mp3 = ( containi(soundfile, ".mp3") != -1 )
new isWav_inSound_folder = 0
if ( !is_mp3 )
{ // ".mp3" in not in the string
if ( equali(soundfile, "sound/", 6) )
{
formatex(sound_file_name, TOK_LENGTH + 10, "%s", soundfile)
isWav_inSound_folder = 1
}else
formatex(sound_file_name, TOK_LENGTH + 10, "sound/../%s", soundfile)
}
else
{
copy(sound_file_name, TOK_LENGTH + 10, soundfile)
}
if ( allow_check_existence )
{
if ( !file_exists(sound_file_name) )
{
log_amx("Sank Sounds >> Trying to load a file that dont exist. Skipping this file: ^"%s^"", sound_file_name)
return -1
}
sound_data[num][DURATION][elem] = _:cfg_get_duration(sound_file_name, is_mp3 ? SOUND_TYPE_MP3 : SOUND_TYPE_WAV )
if ( sound_data[num][DURATION][elem] <= 0.0 )
{
log_amx("Sank Sounds >> Sound duration is not valid. File is damaged. Skipping this file: ^"%s^"", sound_file_name)
return -1
}
}
if ( allow_global_precache
&& precache_sounds == 1
&& allowed_to_precache )
{
if ( is_mp3
|| !isWav_inSound_folder )
engfunc(EngFunc_PrecacheGeneric, soundfile)
else
engfunc(EngFunc_PrecacheSound, soundfile[6])
}
}
copy(sound_data[num][KEY_SOUNDS][TOK_LENGTH * elem], TOK_LENGTH, soundfile)
++sound_data[num][SOUND_AMOUNT]
return 1
}
array_clear_element( index )
{
for ( new i = 0; i < TOK_LENGTH; ++i )
sound_data[index][KEYWORD][i] = 0
sound_data[index][SOUND_AMOUNT] = 0
sound_data[index][ADMIN_LEVEL_BASE] = 0
sound_data[index][FLAGS] = 0
sound_data[index][PLAY_COUNT_KEY] = 0
for ( new i = 0; i < MAX_RANDOM; ++i )
array_clear_inner_element(index, i)
}
array_clear_inner_element( index , elem )
{
for ( new i = 0; i < TOK_LENGTH; ++i )
sound_data[index][KEY_SOUNDS][TOK_LENGTH * elem + i] = 0
sound_data[index][DURATION][elem] = _:0.0
sound_data[index][ADMIN_LEVEL][elem] = 0
sound_data[index][SOUND_TYPE][elem] = 0
sound_data[index][PLAY_COUNT][elem] = 0
}
array_copy_element( dest , source )
{
copy(sound_data[dest][KEYWORD], TOK_LENGTH, sound_data[source][KEYWORD])
sound_data[dest][SOUND_AMOUNT] = sound_data[source][SOUND_AMOUNT]
sound_data[dest][ADMIN_LEVEL_BASE] = sound_data[source][ADMIN_LEVEL_BASE]
sound_data[dest][FLAGS] = sound_data[source][FLAGS]
sound_data[dest][PLAY_COUNT_KEY] = sound_data[source][PLAY_COUNT_KEY]
for ( new i = 0; i < MAX_RANDOM; ++i )
array_copy_inner_elements(dest, i, source, i)
}
array_copy_inner_elements( array1 , elem1 , array2 , elem2 )
{
copy(sound_data[array1][KEY_SOUNDS][TOK_LENGTH * elem1], TOK_LENGTH, sound_data[array2][KEY_SOUNDS][TOK_LENGTH * elem2])
sound_data[array1][DURATION][elem1] = _:sound_data[array2][DURATION][elem2]
sound_data[array1][ADMIN_LEVEL][elem1] = sound_data[array2][ADMIN_LEVEL][elem2]
sound_data[array1][SOUND_TYPE][elem1] = sound_data[array2][SOUND_TYPE][elem2]
sound_data[array1][PLAY_COUNT][elem1] = sound_data[array2][PLAY_COUNT][elem2]
}
array_remove( index )
{
// Keep looping array, copying the next into the current
for ( ; index < MAX_KEYWORDS; ++index )
{
// We are at last List element or there is no succesor
// so clear it cause we want to remove one element anyway
if ( index == MAX_KEYWORDS - 1
|| sound_data[index + 1][KEYWORD][0] == 0 )
{
// Delete data
array_clear_element(index)
// We reached the end
return
}
// Copy the next data over the current
array_copy_element(index, index + 1)
}
}
array_remove_inner( index , elem )
{
// we are removing an element, so decrease counter
--sound_data[index][SOUND_AMOUNT]
for( ; elem < MAX_RANDOM; ++elem )
{
// If we're about to copy data that doesn't exist,
// then just erase the last entry instead of copying
if ( elem == MAX_RANDOM - 1
|| sound_data[index][KEY_SOUNDS][TOK_LENGTH * (elem + 1)] == 0 )
{
// Delete Sound
array_clear_inner_element(index, elem)
// We reached the end
return
}
// else
// Copy the next data over the current
array_copy_inner_elements(index, elem, index, elem + 1)
}
}
cfg_write_keyword( index , Text[] , Textlen )
{
Text[0] = 0
if ( sound_data[index][ADMIN_LEVEL_BASE] )
{
new access_str[32]
get_flags(sound_data[index][ADMIN_LEVEL_BASE], access_str, 31)
formatex(Text, Textlen, "@%s@%s;^t^t", access_str, sound_data[index][KEYWORD])
}else
formatex(Text, Textlen, "%s;^t^t", sound_data[index][KEYWORD])
}
cfg_write_keysound( index , Text[] , Textlen )
{
new access_str[32]
for ( new j = 0; j < MAX_RANDOM && strlen(sound_data[index][KEY_SOUNDS][TOK_LENGTH * j]); ++j )
{
if ( sound_data[index][ADMIN_LEVEL][j] )
{
get_flags(sound_data[index][ADMIN_LEVEL][j], access_str, 31)
format(Text, Textlen, "%s@%s@%s;", Text, access_str, sound_data[index][KEY_SOUNDS][TOK_LENGTH * j])
}else
format(Text, Textlen, "%s%s;", Text, sound_data[index][KEY_SOUNDS][TOK_LENGTH * j])
}
}
cfg_parse_access( str[] )
{
new access_level
if ( str[0] == '@' )
{
new second_at = contain(str[1], "@")
if ( second_at != -1 )
{
new temp_access[32]
copy(temp_access, second_at, str[1])
strtolower(temp_access)
access_level = read_flags(temp_access)
copy(str, 127, str[second_at + 1 + 1])
}else
{
access_level = SND_IMMUNITY
copy(str, 127, str[1])
}
}
return access_level
}
Float:cfg_get_duration( sound_file[] , type )
{
switch ( type )
{
case SOUND_TYPE_WAV:
{
return cfg_get_wav_duration(sound_file)
}
case SOUND_TYPE_MP3:
{
return cfg_get_mp3_duration(sound_file)
}
}
return 0.0
}
Float:cfg_get_wav_duration( wav_file[] )
{
new file = fopen(wav_file, "rb")
new dummy_input
new i
for ( i = 0; i < 24; ++i )
dummy_input = fgetc(file)
// 24th byte
new hertz = fgetc(file)
// 25th byte
hertz += fgetc(file) * 256
// 26th byte
hertz += fgetc(file) * 256 * 256
for ( i = 27; i < 34; ++i )
dummy_input = fgetc(file)
// 34th byte
new bitrate = fgetc(file)
// bytes for data length start right after ascii "data", so search for it
// normally it is at 35 but also saw at 44, so just in case add bigger search area
new data_found
do
{
dummy_input = fgetc(file)
if ( dummy_input == 'd' )
data_found = 1
else if ( dummy_input == 'a'
&& data_found == 1 )
data_found = 2
else if ( dummy_input == 't'
&& data_found == 2 )
data_found = 3
else if ( dummy_input == 'a'
&& data_found == 3 )
data_found = 4
else
data_found = 0
}while ( dummy_input != -1 && data_found < 4 )
if ( dummy_input == -1
|| hertz <= 0
|| bitrate <= 0
|| data_found != 4 )
{
fclose(file)
return 0.0
}
// 1st byte after data
new data_length = fgetc(file)
// 2nd byte after data
data_length += fgetc(file) * 256
// 3rd byte after data
data_length += fgetc(file) * 256 * 256
// 4th byte after data
data_length += fgetc(file) * 256 * 256 * 256
fclose(file)
return float(data_length) / ( float(hertz * bitrate) / 8.0 )
}
enum
{
MP3_MPEG_VERSION_BIT1 = 8,
MP3_MPEG_VERSION_BIT2 = 16,
MP3_LAYER_BIT1 = 2,
MP3_LAYER_BIT2 = 4,
MP3_PROTECT_BIT = 1,
MP3_BITRATE_BIT1 = 16,
MP3_BITRATE_BIT2 = 32,
MP3_BITRATE_BIT3 = 64,
MP3_BITRATE_BIT4 = 128,
MP3_BITRATE_INVALID = 15,
MP3_SAMPLERATE_BIT1 = 4,
MP3_SAMPLERATE_BIT2 = 8,
MP3_SAMPLERATE_INVALID = 3,
MP3_PADDING_BIT = 2,
MP3_PRIVATE_BIT = 1,
}
// bitrate info
new const bitrate_table[] = {
//MPEG 2 & 2.5
0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1, // Layer I
0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1, // Layer II
0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1, // Layer III
//MPEG 1
0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1, // Layer I
0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1, // Layer II
0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1, // Layer III
}
#if DEBUG_MODE == 1
// frequency info
new const samplingrate_table[] = {
11025, 12000, 8000, 0, // MPEG 2.5 // have not seen MPEG 2.5, so UNTESTED
-1, -1, -1, 0, // reserved
22050, 24000, 16000, 0, // MPEG 2
44100, 48000, 32000, 0 // MPEG 1
}
#endif
Float:cfg_get_mp3_duration( mp3_file[] )
{
new file = fopen(mp3_file, "rb")
new byte, found_header, file_pos
new byte2
new mpeg_version
new layer
new mp3_bitrate
new mp3_samplerate
new result = -1
do
{
byte = fgetc(file)
if ( byte == -1 )
break
++file_pos
if ( byte != 255 )
continue
byte = fgetc(file)
byte2 = fgetc(file)
result = verify_header(byte, byte2, mpeg_version, layer, mp3_bitrate, mp3_samplerate)
if ( result == -1 )
{
fseek(file, file_pos, SEEK_SET)
++file_pos
continue
}else
break
}while ( !found_header && byte != -1 )
fclose(file)
if ( byte == -1 )
return 0.0
new mpeg_version_for_bitrate = 0
if ( mpeg_version == 3 )
mpeg_version_for_bitrate = 1
new mp3_bitrate_kbps = bitrate_table[mpeg_version_for_bitrate * ( 3 * 16 ) + ( layer - 1 ) * 16 + mp3_bitrate]
#if DEBUG_MODE == 1
log_amx("Sank Sounds >> DEBUG for file ^"%s^"", mp3_file)
log_amx("Sank Sounds >> Data bytes = %i / %i", byte, byte2)
log_amx("Sank Sounds >> Header position = %i", file_pos)
new mpeg_version_str[10]
if ( mpeg_version == 0 )
copy(mpeg_version_str, 9, "MPEG 2.5")
else if ( mpeg_version == 2 )
copy(mpeg_version_str, 9, "MPEG 2")
else if ( mpeg_version == 3 )
copy(mpeg_version_str, 9, "MPEG 1")
log_amx("Sank Sounds >> MPEG version = %i / Format: %s", mpeg_version, mpeg_version_str)
log_amx("Sank Sounds >> Layer = %i", layer)
log_amx("Sank Sounds >> Bitrate = %iKbps (%i)", mp3_bitrate_kbps, mp3_bitrate)
new mp3_samplerate_hz = samplingrate_table[mpeg_version * 4 + mp3_samplerate]
log_amx("Sank Sounds >> Samplerate = %iHz (%i)", mp3_samplerate_hz, mp3_samplerate)
#endif
new size_of_file = file_size(mp3_file, 0)
if ( mp3_bitrate_kbps == 0 )
return 0.0
//song length...
return float(size_of_file) / ( float(mp3_bitrate_kbps) * 1000.0 ) * 8.0
}
verify_header( header , header2 , &mpeg_version , &layer , &mp3_bitrate , &mp3_samplerate)
{
// check if first 3 bits set
if ( header & 0xe0 != 0xe0 )
return -1
layer = 4
- ( header & MP3_LAYER_BIT1 ) / MP3_LAYER_BIT1
+ ( header & MP3_LAYER_BIT2 ) / MP3_LAYER_BIT1
if ( layer != 3 )
return -1
mp3_bitrate = ( header2 & MP3_BITRATE_BIT1 ) / MP3_BITRATE_BIT1
+ ( header2 & MP3_BITRATE_BIT2 ) / MP3_BITRATE_BIT1
+ ( header2 & MP3_BITRATE_BIT3 ) / MP3_BITRATE_BIT1
+ ( header2 & MP3_BITRATE_BIT4 ) / MP3_BITRATE_BIT1
if ( mp3_bitrate & MP3_BITRATE_INVALID == MP3_BITRATE_INVALID )
return -1
mp3_samplerate = ( header2 & MP3_SAMPLERATE_BIT1 ) / MP3_SAMPLERATE_BIT1
+ ( header2 & MP3_SAMPLERATE_BIT2 ) / MP3_SAMPLERATE_BIT1
if ( mp3_samplerate & MP3_SAMPLERATE_INVALID == MP3_SAMPLERATE_INVALID )
return -1
mpeg_version = ( header & MP3_MPEG_VERSION_BIT1 ) / MP3_MPEG_VERSION_BIT1
+ ( header & MP3_MPEG_VERSION_BIT2 ) / MP3_MPEG_VERSION_BIT1
return 1
}
/*
* plugin_sank_sounds.sma
* Author: Luke Sankey
* Date: March 21, 2001 - Original hard-coded version
* Date: July 2, 2001 - Rewrote to be text file configurable
* Date: November 18, 2001 - Added admin_sound_play command, new variables
* SND_DELAY, SND_SPLIT and EXACT_MATCH, as well as the ability to
* have admin-only sounds, like the original version had.
* Date: March 30, 2002 - Now ignores speech of length zero.
* Date: May 30, 2002 - Updated for use with new playerinfo function
* Date: November 12, 2002 - Moved snd-list.cfg file to new location, and
* made it all lower-case. Sorry, linux guys, if it confuses you.
* Added some new ideas from Bill Bateman:
* 1.) added SND_PUNISH and changed SND_KICK to SND_MAX
* 2.) ability to either speak or play sounds
*
* Last Updated: May 12, 2003
*
*
*
* HunteR's modifications:
* - Players no longer kicked, they are "muted" (no longer able to play sounds)
* - All sounds are now "spoken" (using the speak command)
* - As a result, all "\" must become "/"
* - Ability to reset a player's sound count mid-game
*
* My most deepest thanks goes to William Bateman (aka HunteR)
*
http://thepit.shacknet.nu
*
[email protected]
* For he was the one who got me motivated once again to write this plugin
* since I don't run a server anymore. And besides that, he helped write
* parts of it.
*
* I hope you enjoy this new functionality on the old plugin_sank_sounds
*/
0
0
Back to top
SuperStar's
[Mentally Stable]
Status: Offline
(since 25-11-2012 12:11)
Joined: 03 Nov 2012
Posts: 152
,
Topics: 15
Location:
Cautama
Reputation:
45.8
Votes
: 4
Posted: 23-11-2012, 00:11:58
| Translate post to:
... (
Click for more languages
)
Uite aici Amxx:
http://amx.freakz.ro/compiled/sank_sounds_7pm89ml1.amxx
Sursa:
http://www.girlshare.ro/31925637.7
Sper ca e cea ce vrei, dami PM daca vrei sa te ajut la instalare.
-Connect Vest.Freakz.Ro
-Connect Zombie.Freakz.Ro
-Connect Vest.Freakz.Ro
-Admin pe Vest.Freakz.Ro
0
0
Back to top
KillingFloor
[Mentally Stable]
Status: Offline
(since 09-02-2013 10:06)
Joined: 22 Nov 2012
Posts: 14
,
Topics: 4
Location:
Romania
Reputation:
51.5
Votes
: 2
Posted: 23-11-2012, 14:40:19
| Translate post to:
... (
Click for more languages
)
nu mai inteles, am zis...ca vreau doar cei care au acces la ADMIN_LEVEL_H sa poata sa scrie in chat un sunet si acesta sa porneasca...Adica de exemplu daca un player scrie limbaj....[limbaj fiind sound] sa nu auda nici un sunet dar in schimb daca un admin care are acces ca ADMIN_LEVEL_H si scrie limbaj sa se auda sunetul care e bus pe acest cuvant...
Sper ca m-am facut inteles
0
0
Back to top
SuperStar's
[Mentally Stable]
Status: Offline
(since 25-11-2012 12:11)
Joined: 03 Nov 2012
Posts: 152
,
Topics: 15
Location:
Cautama
Reputation:
45.8
Votes
: 4
Posted: 23-11-2012, 17:11:14
| Translate post to:
... (
Click for more languages
)
Uite, din CFG se modifica ce vrei tu si acolo se adauga ce misc-uri vrei tu.
Download:
http://www.girlshare.ro/31927003.9
Am modificat astfel incat doar admini care au flg-ul "H" sa poata folosi comenzile acestea.
Apropo, trebuie neaparat sa downloadezi si .amxx
Download:
http://amx.freakz.ro/compiled/sank_sounds_7pm89ml1.amxx
-Connect Vest.Freakz.Ro
-Connect Zombie.Freakz.Ro
-Connect Vest.Freakz.Ro
-Admin pe Vest.Freakz.Ro
0
0
Back to top
KillingFloor
[Mentally Stable]
Status: Offline
(since 09-02-2013 10:06)
Joined: 22 Nov 2012
Posts: 14
,
Topics: 4
Location:
Romania
Reputation:
51.5
Votes
: 2
Posted: 23-11-2012, 17:34:48
| Translate post to:
... (
Click for more languages
)
aia ce mi-ai pus uh acolo in cfg nu e ce am cerut eu....aia este pentru cei care au acces ADMIN_LEVEL_H pot da in continu sunete cat timp vor ei iar cei ce nu au ADMIN_LEVEL_H trebuie sa astepte cateva secunde inainte sa mai puna alt sunet....
0
0
Back to top
SuperStar's
[Mentally Stable]
Status: Offline
(since 25-11-2012 12:11)
Joined: 03 Nov 2012
Posts: 152
,
Topics: 15
Location:
Cautama
Reputation:
45.8
Votes
: 4
Posted: 23-11-2012, 18:02:48
| Translate post to:
... (
Click for more languages
)
Hmmmm, nu stiu ce sa zic, nu prea am lucrat pe astefel de pluginuri cu sound, credeam ca asa e bine, dar daca nu e bine, poate te ajuta ShaKaL.
-Connect Vest.Freakz.Ro
-Connect Zombie.Freakz.Ro
-Connect Vest.Freakz.Ro
-Admin pe Vest.Freakz.Ro
0
0
Back to top
Freakz Forum Index
->
Trash Bin
->
Trash
The time now is 02-10-2024, 20:45:14
Copyright info
Based on phpBB
ro
/
com
B
Login
I forgot my password
World of Warcraft
Login for more...
Download WoW 7.3.5
Misc
eSports
Achievements
Buy reputation with votes
Reputation trades
Forum rules
Ban list
Members list
User guide (FAQ)
World of Warcraft
View details