HLMOD.HU Forrás Megtekintés - www.hlmod.hu
  1. /*
  2. * Modified by Safety1st
  3. * 2/1/2014
  4. *
  5. * Original topic: https://forums.alliedmods.net/showthread.php?t=186460
  6. *
  7. * Changes are:
  8. * - huge performance optimization thanks to ConnorMcLeod's idea
  9. * - proper knife attacks handling
  10. * - fixed 'Run time error 4: index out of bounds' error
  11. * - revized redisplaying menu algorithm, now it also could be easily closed to continue playing
  12. * - corrected colorchat function
  13. * - other optimizations
  14. */
  15.  
  16. #include <amxmodx>
  17. #include <fakemeta>
  18. #include <hamsandwich>
  19.  
  20. #define SERVER_TAG " DM VOTE "
  21. #define USE_MENU_FIX // should be commented for AMXX 1.8.3
  22. #define MAX_PLAYERS 32
  23.  
  24. enum _:TASKS
  25. {
  26. TASK_VOTE_INIT = 1445,
  27. TASK_VOTE_TIME,
  28. TASK_VOTE_COUNTDOWN
  29. }
  30.  
  31. enum _:MODES
  32. {
  33. NORMAL = 0,
  34. HEAD
  35. }
  36.  
  37. new g_iMaxPlayers, g_iVoteTime, g_iVoteCountdown = 7
  38.  
  39. new cvar_votetime, cvar_blockknife_dmg, cvar_voteinit_time
  40.  
  41. #define NO_MENU -1
  42. #define MENU_CLOSE 2
  43.  
  44. new g_vote[MODES], g_menu_id[ MAX_PLAYERS + 1 ] = { NO_MENU, NO_MENU, ... }
  45.  
  46. // macro; %1 - variable being modified, %2 - player index
  47. #define CheckFlag(%1,%2) ( %1 & ( 1 << (%2-1) ) )
  48. #define AddFlag(%1,%2) ( %1 |= ( 1 << (%2-1) ) )
  49. new giHasVoted, giHasCloseOption
  50.  
  51. new const sound_countdown[ ][ ] = {
  52. "misc/one.wav",
  53. "misc/two.wav",
  54. "misc/three.wav",
  55. "misc/four.wav",
  56. "misc/five.wav"
  57. }
  58.  
  59. // temporary holds info
  60. new szBuffer[MODES][64]
  61. new iSum, iNormal, iHead
  62.  
  63. const m_iMenu = 205 // cbaseplayer offset
  64.  
  65. new gMsgSayText
  66.  
  67. public plugin_precache() {
  68. for( new i = 0; i < sizeof sound_countdown; i++ )
  69. precache_sound( sound_countdown[i] )
  70. }
  71.  
  72. public plugin_init() {
  73. register_plugin( "DeathMatch Mod's Vote", "1.6", "Neeeeeeeeeel.- / Safety1st" )
  74.  
  75. register_dictionary( "dm_vote.txt" )
  76.  
  77. cvar_votetime = register_cvar( "dm_vote_time", "15" )
  78. cvar_blockknife_dmg = register_cvar( "dm_block_knife_dmg", "0" )
  79. cvar_voteinit_time = register_cvar( "dm_voteinit_time", "40" )
  80.  
  81. g_iMaxPlayers = get_maxplayers()
  82. gMsgSayText = get_user_msgid("SayText")
  83. }
  84.  
  85. public plugin_cfg() {
  86. set_task( get_pcvar_float( cvar_voteinit_time ), "show_vote", TASK_VOTE_INIT )
  87. }
  88.  
  89. public client_disconnect(id) {
  90. g_menu_id[id] = NO_MENU
  91. }
  92.  
  93. public show_vote()
  94. {
  95. if( g_iVoteCountdown == 7 )
  96. {
  97. g_iVoteCountdown--
  98. set_task( 1.0, "show_vote", TASK_VOTE_COUNTDOWN, _, _, "b" )
  99. return
  100. }
  101.  
  102. else if( g_iVoteCountdown )
  103. {
  104. set_hudmessage( 85, 255, 0, -1.0, 0.09, 1, 6.0, 1.0 )
  105.  
  106. if( g_iVoteCountdown != 1 )
  107. {
  108. for( new i = 1; i <= g_iMaxPlayers; i++ )
  109. {
  110. if( is_user_connected( i ) )
  111. show_hudmessage( i, "%L", i, "VOTE_STARTS_IN", g_iVoteCountdown - 1 )
  112. }
  113.  
  114. PlaySound( g_iVoteCountdown - 2 )
  115.  
  116. g_iVoteCountdown--
  117.  
  118. return
  119. }
  120.  
  121. else
  122. {
  123. for( new i = 1; i <= g_iMaxPlayers; i++ )
  124. {
  125. if( is_user_connected( i ) )
  126. show_hudmessage( i, "%L", i, "VOTE_STARTING" )
  127. }
  128. }
  129. }
  130.  
  131. remove_task( TASK_VOTE_COUNTDOWN )
  132.  
  133. g_iVoteTime = get_pcvar_num( cvar_votetime )
  134.  
  135. set_task( 1.0, "time_vote", TASK_VOTE_TIME, _, _, "b" )
  136.  
  137. for( new i = 1; i <= g_iMaxPlayers; i++ )
  138. {
  139. if( is_user_connected( i ) )
  140. prepare_menu_vote( i )
  141. }
  142.  
  143. return
  144. }
  145.  
  146. public time_vote()
  147. {
  148. if( !g_iVoteTime ) {
  149. remove_task( TASK_VOTE_TIME )
  150. finish_vote()
  151. return
  152. }
  153.  
  154. set_hudmessage( 85, 255, 0, -1.0, 0.09, 1, 6.0, 1.0 )
  155.  
  156. for( new i = 1; i <= g_iMaxPlayers; i++ )
  157. {
  158. if( is_user_connected( i ) )
  159. show_hudmessage( i, "%L", i, "VOTE_FINISHS_IN", g_iVoteTime )
  160. }
  161.  
  162. g_iVoteTime--
  163. }
  164.  
  165. public finish_vote() {
  166. // hide menu
  167. show_menu( 0, 0, "^n", .time = 1 )
  168.  
  169. new result = g_vote[ HEAD ] == g_vote[ NORMAL ] ? 0 : 1
  170. if( result )
  171. calculate_votes()
  172.  
  173. for( new i = 1; i <= g_iMaxPlayers; i++ )
  174. {
  175. if( is_user_connected( i ) )
  176. {
  177. if( !result )
  178. ChatColor( i, "!g[%s] %L", SERVER_TAG, i, "VOTE_NOWINNER" )
  179.  
  180. else
  181. {
  182. ChatColor( i, "!g[%s] %L", SERVER_TAG, i, "VOTE_FINISH", iHead, iNormal )
  183. ChatColor( i, "!g[%s] %L", SERVER_TAG, i, "VOTE_RESULT", g_vote [ HEAD ] > g_vote[ NORMAL ] ? "Csak fejlövés" : "Normál" )
  184. }
  185. }
  186. }
  187.  
  188. if( g_vote[ HEAD ] > g_vote[ NORMAL ] )
  189. RegisterHam( Ham_TraceAttack, "player", "fw_TraceAttack" ) // we will block all damage except headshots and knife
  190.  
  191. set_cvar_num( "sv_restart", 3 )
  192. }
  193.  
  194. public PlaySound( sound )
  195. client_cmd( 0, "spk ^"%s^"", sound_countdown[ sound ] )
  196.  
  197. public prepare_menu_vote(id) {
  198. formatex( szBuffer[NORMAL], 63, "%L", id, "VOTE_MENU_TITLE" )
  199. new Menu = menu_create( szBuffer[NORMAL], "menu_vote" ) // menu ids start from 0
  200. new iCallback = menu_makecallback( "CallbackMenu" )
  201. menu_additem( Menu, "", .callback = iCallback )
  202. menu_additem( Menu, "", .callback = iCallback )
  203. formatex( szBuffer[NORMAL], 63, "%L", id, "VOTE_MENU_EXIT" )
  204. menu_additem( Menu, szBuffer[NORMAL] )
  205. menu_setprop( Menu, MPROP_EXIT, MEXIT_NEVER )
  206.  
  207. show_menu_vote( id, Menu )
  208. g_menu_id[id] = Menu
  209. }
  210.  
  211. public CallbackMenu( id, menu, item ) {
  212. if( CheckFlag( giHasVoted, id ) )
  213. return ITEM_DISABLED
  214.  
  215. return ITEM_IGNORE
  216. }
  217.  
  218. show_menu_vote( id, menu ) {
  219. formatex( szBuffer[NORMAL], 63, "Normál \r[\y%d%%\r]", iSum ? iNormal : 0 )
  220. formatex( szBuffer[HEAD], 63, "Csak fejlövés \r[\y%d%%\r]", iSum ? iHead : 0 )
  221.  
  222. menu_item_setname( menu, NORMAL, szBuffer[NORMAL] )
  223. menu_item_setname( menu, HEAD, szBuffer[HEAD] )
  224.  
  225. if( CheckFlag( giHasVoted, id ) && !CheckFlag( giHasCloseOption, id ) ) {
  226. formatex( szBuffer[NORMAL], 63, "%L", id, "VOTE_MENU_CLOSE" )
  227. menu_item_setname( menu, MENU_CLOSE, szBuffer[NORMAL] )
  228. AddFlag( giHasCloseOption, id )
  229. }
  230.  
  231. #if defined USE_MENU_FIX
  232. /* Fix for AMXX menus, more info here:
  233. https://bugs.alliedmods.net/show_bug.cgi?id=4778
  234. https://forums.alliedmods.net/showthread.php?t=230887 */
  235. set_pdata_int( id, m_iMenu, 0 )
  236. #endif
  237.  
  238. menu_display( id, menu )
  239. }
  240.  
  241. public menu_vote( id, menu, item ) {
  242. if( item == MENU_EXIT )
  243. return PLUGIN_HANDLED
  244. /* do you see that? Even if a menu hasn't 'exit' option it is called anyway when menu is redisplayed for player.
  245. who made this crap called 'new menus'?! */
  246.  
  247. if( item == MENU_CLOSE ) {
  248. // destroy the menu and don't disturb player anymore
  249. menu_destroy(menu)
  250. g_menu_id[id] = NO_MENU
  251. return PLUGIN_HANDLED
  252. }
  253.  
  254. AddFlag( giHasVoted, id );
  255.  
  256. g_vote[item]++
  257.  
  258. new uName[ 33 ]
  259. get_user_name( id, uName, charsmax( uName ) )
  260. ChatColor( 0, "!g[%s] %L", SERVER_TAG, id, "VOTE_HAS_CHOOSEN", uName, !item ? "Normál" : "Csak fejlövés" )
  261.  
  262. new iMenuId
  263. calculate_votes()
  264. for( new i = 1; i <= g_iMaxPlayers; i++ ) {
  265. iMenuId = g_menu_id[i]
  266. if( iMenuId != NO_MENU )
  267. show_menu_vote( i, iMenuId )
  268. }
  269.  
  270. return PLUGIN_HANDLED
  271. }
  272.  
  273. calculate_votes() {
  274. iSum = g_vote[ NORMAL ] + g_vote[ HEAD ]
  275. if( iSum ) {
  276. iNormal = g_vote[ NORMAL ] * 100 / iSum
  277. iHead = 100 - iNormal
  278. }
  279. }
  280.  
  281. public fw_TraceAttack( victim, attacker, Float:damage, Float:direction[3], trace, damageBits ) {
  282. if( get_user_weapon( attacker ) == CSW_KNIFE ) {
  283. if( get_pcvar_num( cvar_blockknife_dmg ) )
  284. return HAM_SUPERCEDE
  285. }
  286. else if( victim != attacker && ( 1 <= attacker <= g_iMaxPlayers ) && get_tr2( trace, TR_iHitgroup ) != HIT_HEAD ) {
  287. set_tr2( trace, TR_flFraction, 1.0 ) // didn't hit anything
  288. return HAM_SUPERCEDE
  289. }
  290.  
  291. return HAM_IGNORED
  292. }
  293.  
  294. ChatColor( const id, const input[ ], any:... ) {
  295. new count = 1, players[32], player
  296. static msg[191]
  297. vformat( msg, 190, input, 3 )
  298.  
  299. replace_all( msg, 190, "!g", "^4" )
  300. replace_all( msg, 190, "!y", "^1" )
  301. replace_all( msg, 190, "!team", "^3" )
  302. replace_all( msg, 190, "!team2", "^0" )
  303.  
  304. if( id ) {
  305. if( !is_user_connected(id) )
  306. return
  307. players[0] = id
  308. }
  309. else
  310. get_players(players, count, "ch")
  311.  
  312. for( new i = 0; i < count; i++ ) {
  313. player = players[i]
  314. message_begin(MSG_ONE_UNRELIABLE, gMsgSayText, _, player)
  315. write_byte(player)
  316. write_string(msg)
  317. message_end()
  318. }
  319. }