├── _config.yml
├── .gitignore
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── build.yml
├── addons
└── sourcemod
│ ├── translations
│ ├── chi
│ │ └── customvotes.phrases.txt
│ ├── customvotes.phrases.txt
│ └── pt
│ │ └── customvotes.phrases.txt
│ ├── configs
│ └── customvotes.cfg
│ └── scripting
│ └── customvotes.sp
├── README.md
├── thirdparty
└── includes
│ └── afk_manager.inc
└── LICENSE
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Visual Studio code
2 | .vscode/
3 | *.code-workspace
4 |
5 | # Compiled plugin binary
6 | *.smx
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # plugin source
5 | *.sp text
6 | # plugin binary
7 | *.smx binary
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Game Specific**
17 | Is this feature for a specific game?
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Game Server**
24 | * Sourcemod version
25 | * Metamod version
26 | * Custom Votes plugin version
27 | * Game
28 |
29 |
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/addons/sourcemod/translations/chi/customvotes.phrases.txt:
--------------------------------------------------------------------------------
1 | "Phrases"
2 | {
3 | "Already Voted"
4 | {
5 | "chi" "[SM] 这个投票你已经投过了."
6 | }
7 |
8 | "Ban Warning"
9 | {
10 | "chi" "[SM] 如果你现在断开连接,你将会被ban!"
11 | }
12 |
13 | "No Valid Clients"
14 | {
15 | "chi" "[SM] 没有可用的玩家."
16 | }
17 |
18 | "No Votes Remaining"
19 | {
20 | "chi" "[SM] 你可能不能继续投这个投票了."
21 | }
22 |
23 | "Voting No Longer Available"
24 | {
25 | "chi" "[SM] 所有人不能继续投这个投票了."
26 | }
27 |
28 | "Not Enough Valid Clients"
29 | {
30 | "chi" "[SM] 没有足够的玩家投票."
31 | }
32 |
33 | "Votes Remaining"
34 | {
35 | "#format" "{1:i}"
36 | "chi" "[SM] 投票 {1} 冷却中 时间:(s)秒."
37 | }
38 |
39 | "Vote Delay"
40 | {
41 | "#format" "{1:i}"
42 | "chi" "[SM] 在投票之前你必须多等 {1} 秒."
43 | }
44 |
45 | "Vote Cooldown"
46 | {
47 | "#format" "{1:i}"
48 | "chi" "[SM] 在继续投票之前你必须多等 {1} 秒"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/addons/sourcemod/translations/customvotes.phrases.txt:
--------------------------------------------------------------------------------
1 | "Phrases"
2 | {
3 | "Already Voted"
4 | {
5 | "en" "[SM] You have already voted for this item."
6 | }
7 |
8 | "Ban Warning"
9 | {
10 | "en" "[SM] You will be banned if you disconnect while this vote is in progress!"
11 | }
12 |
13 | "No Valid Clients"
14 | {
15 | "en" "[SM] No clients available."
16 | }
17 |
18 | "No Votes Remaining"
19 | {
20 | "en" "[SM] You may no longer vote for this item."
21 | }
22 |
23 | "Voting No Longer Available"
24 | {
25 | "en" "[SM] This vote is no longer available to players."
26 | }
27 |
28 | "Not Enough Valid Clients"
29 | {
30 | "en" "[SM] There are not enough players to vote for this."
31 | }
32 |
33 | "Votes Remaining"
34 | {
35 | "#format" "{1:i}"
36 | "en" "[SM] You may vote for this {1} more time(s)."
37 | }
38 |
39 | "Vote Delay"
40 | {
41 | "#format" "{1:i}"
42 | "en" "[SM] You must wait {1} more second(s) before voting."
43 | }
44 |
45 | "Vote Cooldown"
46 | {
47 | "#format" "{1:i}"
48 | "en" "[SM] You must wait {1} more second(s) before voting for this again."
49 | }
50 | }
--------------------------------------------------------------------------------
/addons/sourcemod/translations/pt/customvotes.phrases.txt:
--------------------------------------------------------------------------------
1 | "Phrases"
2 | {
3 | "Already Voted"
4 | {
5 | "pt" "[SM] Você já votou neste item."
6 | }
7 |
8 | "Ban Warning"
9 | {
10 | "pt" "[SM] Você será banido se desconectar enquanto essa votação está em progresso!"
11 | }
12 |
13 | "No Valid Clients"
14 | {
15 | "pt" "[SM] Nenhum cliente disponível."
16 | }
17 |
18 | "No Votes Remaining"
19 | {
20 | "pt" "[SM] Você não pode mais votar neste item."
21 | }
22 |
23 | "Voting No Longer Available"
24 | {
25 | "pt" "[SM] Esta votação não está mais disponível para os jogadores."
26 | }
27 |
28 | "Not Enough Valid Clients"
29 | {
30 | "pt" "[SM] Não há jogadores suficiente para votar nesse item."
31 | }
32 |
33 | "Votes Remaining"
34 | {
35 | "#format" "{1:i}"
36 | "pt" "[SM] Você pode votar nesse item mais {1} veze(s)."
37 | }
38 |
39 | "Vote Delay"
40 | {
41 | "#format" "{1:i}"
42 | "pt" "[SM] Você deve esperar {1} segundo(s) para votar."
43 | }
44 |
45 | "Vote Cooldown"
46 | {
47 | "#format" "{1:i}"
48 | "pt" "[SM] Você deve esperar {1} segundo(s) para votar neste item novamente."
49 | }
50 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.gnu.org/licenses/gpl-3.0)
2 | [](http://makeapullrequest.com)
3 | 
4 |
5 | **Custom Votes Redux - Modified**
6 | =================================
7 |
8 |
9 | This is a modified version of
10 | [ReFlexPoison](https://forums.alliedmods.net/member.php?u=149090)‘s [custom
11 | votes redux](https://forums.alliedmods.net/showthread.php?t=235115) plugin.
12 |
13 | Fixes and Features:
14 | * Added vote logging.
15 | * Added KZTimer support.
16 | * Added option to reset max vote passes on wave failure (TF2-MVM only).
17 | * Added vote evasion detection and logging.
18 | * Auto ban on vote evasion.
19 | * Now utilizes Multi-Colors to support more game chat colors.
20 | * Increased the map limit to 1024.
21 | * Fixed call_notify not working and errors when a 'simple' vote is used without a convar as command.
22 | * Fixed default mapcycle resulting in an empty map list.
23 | * Fixed incorrectly adding quotes to vote results, making {VOTER_ID}, {VOTER_INDEX}, {TARGET_ID}, and {TARGET_INDEX} pretty much unusable.
24 | * Fixed some memory leaks.
25 |
26 |
27 | Examples logs from my server:
28 |
29 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 | Vote Type: List
31 |
32 | L 04/28/2018 - 13:21:12: [Custom Votes] Vote Give Cash to RED started by
33 | andycaleb ( STEAM_ID ). Selected Option: 4000
34 |
35 | L 04/28/2018 - 13:33:39: [Custom Votes] Vote Give Cash to RED started by Shadow
36 | Mario \| blw.tf ( STEAM_ID ). Selected Option: 3000
37 |
38 | L 04/28/2018 - 13:34:00: [Custom Votes] Last vote ( Give Cash to RED ) passed. (
39 | Option: 3000 ).
40 |
41 | Vote Type: Map
42 |
43 |
44 | L 04/29/2018 - 00:11:33: [Custom Votes] Vote Change the map started by
45 | andrewdasher673 ( STEAM_ID ). To change map from mvm_coaltown to
46 | mvm_tunnels_a9fix1.
47 |
48 | L 04/29/2018 - 00:11:35: [Custom Votes] Last vote ( Change the map ) passed. Map
49 | was changed from mvm_coaltown to mvm_tunnels_a9fix1.
50 |
51 |
52 | Vote Type: Players
53 |
54 | L 04/28/2018 - 14:30:43: [Custom Votes] Vote Ban player started by TF2 GAMER (
55 | STEAM_ID ) targeting rees123 ( STEAM_ID ).
56 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57 |
58 | **Download**
59 | =================================
60 | For stable version, check the [releases](https://github.com/caxanga334/cvreduxmodified/releases) tab.
61 | For development version, download the repository.
62 |
63 | You can also download the latest development version from the [automated builds](https://github.com/caxanga334/cvreduxmodified/actions).
64 |
65 | Feel free to report bugs/give suggestions.
66 |
67 | For contributions, please use pull requests.
68 |
69 | **Compiling**
70 | =================================
71 | * Download the plugin source from the reposity
72 | * Get the latest version of kztimer.inc from [here](https://bitbucket.org/kztimerglobalteam/kztimerglobal/src/master/scripting/include/kztimer.inc).
73 | * Get the latest version of afk_manager.inc from [here](https://forums.alliedmods.net/showthread.php?p=708265).
74 | * Get the latest version of multicolors from [here](https://github.com/Bara/Multi-Colors).
75 | * Get the latest version of sourcebanspp.inc from [here](https://github.com/sbpp/sourcebans-pp/).
76 |
--------------------------------------------------------------------------------
/thirdparty/includes/afk_manager.inc:
--------------------------------------------------------------------------------
1 | #if defined _afkmanager_included
2 | #endinput
3 | #endif
4 | #define _afkmanager_included
5 |
6 | /**
7 | * Specifies AFK immunity types.
8 | */
9 | enum AFKImmunity:
10 | {
11 | AFKImmunity_None = 0,
12 | AFKImmunity_Move = 1,
13 | AFKImmunity_Kick = 2,
14 | AFKImmunity_Full = 3,
15 | };
16 |
17 | /**
18 | * Sets the AFK immunity type of the client index.
19 | *
20 | * @param client The client index of the player whos immunity type is being set.
21 | * @param type The client AFK immunity type.
22 | * @error The client index is invalid or AFK immunity type is invalid.
23 | */
24 | native void AFKM_SetClientImmunity(int client, AFKImmunity type);
25 |
26 | /**
27 | * Gets the current AFK spectator team number.
28 | *
29 | * @noparam
30 | * @return The spectator team number.
31 | * @noerror
32 | */
33 | native int AFKM_GetSpectatorTeam();
34 |
35 | /**
36 | * Gets the current AFK status of the client index.
37 | *
38 | * @param client The client index of the player being checked.
39 | * @return The clients AFK status.
40 | * @error The client index is invalid.
41 | */
42 | native bool AFKM_IsClientAFK(int client);
43 |
44 | /**
45 | * Gets the current AFK time in seconds of the client index. If the client
46 | * has an invalid AFK time -1 will be returned.
47 | *
48 | * Examples of clients who would have invalid AFK time values:
49 | * Admins with full immunity.
50 | * Disconnected clients.
51 | * Bots, Fake Clients or Source TV.
52 | *
53 | * @param client The client index of the player being checked.
54 | * @return The clients AFK time or -1 if there is an error.
55 | * @error The client index is invalid or time is invalid.
56 | */
57 | native int AFKM_GetClientAFKTime(int client);
58 |
59 |
60 | // Private Forwards
61 |
62 | // Global Forwards
63 |
64 | /**
65 | * Called when an AFK InitializePlayer() fires.
66 | *
67 | * @param client The client index of the client in the event.
68 | * @return Plugin_Stop to stop creating AFK Timer (Full Immunity), any other value for default behavior.
69 | *
70 | */
71 | forward Action AFKM_OnInitializePlayer(int client);
72 |
73 |
74 | /**
75 | * Called when an AFK event fires.
76 | *
77 | * @param name This is the name of the AFK event.
78 | *
79 | * Event Names:
80 | *
81 | * "afk_spawn_move" - Event fired when a client should be moved to spectator for Spawn AFK.
82 | * "afk_move" - Event fired when a client should be moved to spectator for normal AFK.
83 | * "afk_kick" - Event fired when a client should be kicked.
84 | *
85 | * @param client The client index of the client in the event.
86 | * @return Plugin_Continue to complete event.
87 | *
88 | */
89 | forward Action AFKM_OnAFKEvent(const char[] name, int client);
90 |
91 |
92 | /**
93 | * Called when a client has been set AFK.
94 | *
95 | * @param client The client index of the player being flagged.
96 | * @noreturn
97 | */
98 | forward void AFKM_OnClientAFK(int client);
99 |
100 |
101 | /**
102 | * Called when a client is back from AFK.
103 | *
104 | * @param client The client index of the player being flagged.
105 | * @noreturn
106 | */
107 | forward void AFKM_OnClientBack(int client);
108 |
109 |
110 | public SharedPlugin __pl_afkmanager =
111 | {
112 | name = "afkmanager",
113 | file = "afk_manager4.smx",
114 | #if defined REQUIRE_PLUGIN
115 | required = 1
116 | #else
117 | required = 0
118 | #endif
119 | };
120 |
121 | #if !defined REQUIRE_PLUGIN
122 | public __pl_afkmanager_SetNTVOptional()
123 | {
124 | MarkNativeAsOptional("AFKM_SetClientImmunity");
125 | MarkNativeAsOptional("AFKM_GetSpectatorTeam");
126 | MarkNativeAsOptional("AFKM_IsClientAFK");
127 | MarkNativeAsOptional("AFKM_GetClientAFKTime");
128 | }
129 | #endif
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Compile Plugin
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | tags: [ '*' ]
7 | pull_request:
8 | branches: [ master ]
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | SM_VERSION: ['1.10.x', '1.11.x', '1.12.x', '1.13.x']
17 |
18 | steps:
19 | - uses: actions/checkout@v4.2.2
20 |
21 | - uses: benjlevesque/short-sha@v3.0
22 | id: short-sha
23 |
24 | - name: Set environment variables
25 | run: |
26 | SOURCEMOD_PATH=$GITHUB_WORKSPACE/addons/sourcemod
27 | BUILD_PATH=$GITHUB_WORKSPACE/build
28 | echo "SOURCEMOD_PATH=$SOURCEMOD_PATH" >> $GITHUB_ENV
29 | echo "BUILD_PATH=$BUILD_PATH" >> $GITHUB_ENV
30 | echo "SCRIPTS_PATH=$SOURCEMOD_PATH/scripting" >> $GITHUB_ENV
31 | echo "INCLUDES_PATH=$SOURCEMOD_PATH/scripting/include" >> $GITHUB_ENV
32 | echo "PLUGINS_PATH=$SOURCEMOD_PATH/plugins" >> $GITHUB_ENV
33 |
34 | - name: Setup SourcePawn Compiler ${{ matrix.SM_VERSION }}
35 | id: setup_sp
36 | uses: rumblefrog/setup-sp@master
37 | with:
38 | version: ${{ matrix.SM_VERSION }}
39 |
40 | - name: Make Folders
41 | run: |
42 | mkdir include
43 | mkdir -p include/multicolors
44 | working-directory: ${{ env.SCRIPTS_PATH }}
45 |
46 | - name: Get Include Files
47 | run: |
48 | echo "Begin downloading include files"
49 |
50 | wget https://github.com/sbpp/sourcebans-pp/raw/refs/heads/php81/game/addons/sourcemod/scripting/include/sourcebanspp.inc -O sourcebanspp.inc
51 | wget https://bitbucket.org/kztimerglobalteam/kztimerglobal/raw/4382ec21c01ac7998412aa8b7106825a81612b2f/scripting/include/kztimer.inc -O kztimer.inc
52 | wget https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors.inc -O multicolors.inc
53 | cd multicolors
54 | wget https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/colors.inc -O colors.inc
55 | wget https://raw.githubusercontent.com/Bara/Multi-Colors/master/addons/sourcemod/scripting/include/multicolors/morecolors.inc -O morecolors.inc
56 | echo "Done downloading include files."
57 | ls -la
58 | working-directory: ${{ env.INCLUDES_PATH }}
59 |
60 | - name: Move Third-Party Includes
61 | run: |
62 | rsync -rv ${{ github.workspace }}/thirdparty/includes/* ${{ env.INCLUDES_PATH }}
63 |
64 | - name: Compile plugins
65 | run: |
66 | for file in customvotes.sp
67 | do
68 | echo -e "\nCompiling $file..."
69 | spcomp -v2 -i include $file
70 | done
71 | echo "===OUT FILES==="
72 | ls
73 | working-directory: ${{ env.SCRIPTS_PATH }}
74 |
75 | - name: Post Build
76 | run: |
77 | echo "Creating build folder"
78 | mkdir build
79 | mkdir -p build/addons/sourcemod/configs
80 | mkdir -p build/addons/sourcemod/plugins
81 | mkdir -p build/addons/sourcemod/translations
82 | mv $SCRIPTS_PATH/customvotes.smx $BUILD_PATH/addons/sourcemod/plugins/customvotes.smx
83 | mv $SOURCEMOD_PATH/configs/customvotes.cfg $BUILD_PATH/addons/sourcemod/configs/customvotes.cfg
84 | mv $SOURCEMOD_PATH/translations/* $BUILD_PATH/addons/sourcemod/translations/
85 | cd build
86 | ls -la
87 |
88 | - name: Upload Artifact
89 | uses: actions/upload-artifact@v4.6.2
90 | with:
91 | name: cv-redux-${{ matrix.SM_VERSION }}-${{ steps.short-sha.outputs.sha }}
92 | path: |
93 | ${{ env.BUILD_PATH }}/*
94 |
95 | - name: Create Release Package
96 | if: startsWith(github.ref, 'refs/tags/')
97 | working-directory: build
98 | run: |
99 | 7z a -bb3 -mx9 -r "cvredux-${{ matrix.SM_VERSION }}-${{ steps.short-sha.outputs.sha }}.zip" addons
100 | ls -la
101 |
102 | # Cache release package to be retreived by the create_release job
103 | - name: Cache Release Package
104 | if: startsWith(github.ref, 'refs/tags/')
105 | uses: actions/cache@v4.2.3
106 | id: cache
107 | with:
108 | path: |
109 | build/*.zip
110 | key: cvredux-${{ github.ref_name }}-${{ matrix.SM_VERSION }}
111 |
112 | # Job for creating a release, waits for build_main to finish
113 | create_release:
114 | name: Upload Release
115 | runs-on: ubuntu-latest
116 | if: startsWith(github.ref, 'refs/tags/')
117 | needs: [ build ]
118 |
119 | steps:
120 | - name: Retreive Release Package From Cache 1.12
121 | uses: actions/cache@v4.2.3
122 | id: cache2
123 | with:
124 | path: |
125 | build/*.zip
126 | key: cvredux-${{ github.ref_name }}-1.12.x
127 |
128 | - name: Retreive Release Package From Cache 1.13
129 | uses: actions/cache@v4.2.3
130 | id: cache3
131 | with:
132 | path: |
133 | build/*.zip
134 | key: cvredux-${{ github.ref_name }}-1.13.x
135 |
136 | - name: Create Release
137 | uses: softprops/action-gh-release@v2.2.1
138 | with:
139 | name: "Custom Votes Redux Modified ${{ github.ref_name }}"
140 | generate_release_notes: true
141 | files: |
142 | build/*.zip
--------------------------------------------------------------------------------
/addons/sourcemod/configs/customvotes.cfg:
--------------------------------------------------------------------------------
1 | // Custom Votes Redux
2 | // By: ReFlexPoison
3 | //
4 | // Thank you for downloading Custom Votes Redux. If you like my work and want to help out send me a donation. https://forums.alliedmods.net/member.php?u=149090
5 | //
6 | // For full plugin documentation, go to: https://forums.alliedmods.net/showthread.php?p=2097623
7 | //
8 | // How to edit this file: (Not all values will effect every type of vote. Ex: "currentmap" won't change anything in "players" type votes)
9 | //
10 | // "Custom Votes" <-- Leave this alone
11 | // { <-- Add all votes after first bracket (Leave this alone)
12 | // "Say something funny!" <-- Name of vote
13 | // {
14 | // "type" "list" <-- Type of vote (Valid types: players, map, list, simple)
15 | // players - Populates the vote with a list of the online players
16 | // map - Populates the vote with a list of maps from a specific map list
17 | // list - Populates the vote with a custom list of choices
18 | // simple - Doesn't populate the vote with anything
19 | //
20 | // "vote" "1" <-- Determine if a vote is called to determine the result of the selection, or if each selection is chosen manually by the players
21 | // "cvar" "sm_cvar" <-- Control variable being changed
22 | //
23 | // "options" <-- These are your list options
24 | // {
25 | // "lol" "LOL" <-- Option name: lol | Option result: LOL
26 | // "rofl" "ROFL" <-- Option name: rofl | Option result: ROFL
27 | // }
28 | //
29 | // "override" "sm_lol" <-- Admin override (Use this with admin_overrides.cfg to prohibit access from specific players)
30 | // "immunity" "0" <-- Admins with equal or higher immunity are removed from the vote
31 | //
32 | // "delay" "60" <-- Delay in seconds before players can cast a selecting after the map has changed
33 | // "cooldown" "5" <-- Delay in seconds before players can vote again after casting a selection | this applies only to the player who started the vote
34 | // "cooldownall" "5" <-- Delay is seconds before players can vote again after casting a selection | this applies to all players
35 | // "team" "0" <-- Restricts players to only casting selections on team members
36 | // "bots" "0" <-- Allows/disallows casting selections on bots
37 | // "ratio" "0.6" <-- Ratio of players required to cast a selection
38 | // "multiple" "0" <-- Allow/disallow players from casting a selection on more than one option
39 | // "minimum" "4" <-- Minimum votes required for the vote to pass (Overrides ratio)
40 | // "maxcalls" "3" <-- Maximum times a player can cast a selection (0 = No Limit)
41 | // "maxpasses" "3" <-- Maximum amount of times the vote can be passed
42 | // "command" "sm_csay {OPTION_RESULT}" <-- Command(s) ran when a vote is passed
43 | //
44 | // "start_notify" "Started vote. <-- Printed to everyone's chat when a player starts a vote
45 | // "call_notify" "Voted for {OPTION_NAME}. <-- Printed to everyone's chat when a player casts a selection
46 | // "pass_notify" "Vote passed!" <-- Printed to everyone's chat when the vote passes
47 | // "fail_notify" "Vote failed!" <-- Printed to everyone's chat when the vote fails to pass
48 | //
49 | // "maplist" "default" <-- List of maps to populate the selection list (See maplist.cfg)
50 | // "currentmap" "0" <-- Allows/disallows casting selections on the current map
51 | // "recentmaps" "3" <-- How many recent maps will be removed from the vote selections
52 | //
53 | // "chattrigger" "vote" <-- Chat trigger to open the vote selections (Do not include ! or / in the trigger)
54 | // } <-- Leave this alone
55 | // } <-- Leave this alone
56 | //
57 | // Formatting: Remember to add quotes as needed
58 | //
59 | // Place these in command, call_notify, pass_notify to your liking
60 | // {VOTE_AMOUNT} - Amount of votes called for that item
61 | // {VOTE_REQUIRED} - Required vote calls for that vote to pass
62 | //
63 | // {VOTER_INDEX} - Voter client index
64 | // {VOTER_ID} - Voter user id
65 | // {VOTER_STEAMID} - Voter SteamID32, encased in quotes
66 | // {VOTER_NAME} - Voter name, encased in quotes
67 | //
68 | // {TARGET_INDEX} - Target client index
69 | // {TARGET_ID} - Target user id
70 | // {TARGET_STEAMID} - Target SteamID 32, encased in quotes
71 | // {TARGET_NAME} - Target name, encased in quotes
72 | //
73 | // {MAP_NAME} - Map name
74 | // {CURRENT_MAP_NAME} - Current map name
75 | //
76 | // {OPTION_NAME} - Option name
77 | // {OPTION_RESULT} - Option result
78 | //
79 | // {On|Off} - Control variable is On or Off
80 | // {on|off} - Control variable is on or off
81 | //
82 | // {Yes|No} - Voter selected Yes or No
83 | // {yes|no} - Voter selected yes or no
84 | //
85 | // Formatting Examples:
86 | //
87 | // "call_notify" "{olive}[SM] {VOTER_NAME}{default} voted to kick {green}{TARGET_NAME}{default}."
88 | // "command" "kickid #{TARGET_ID};sm_csay Kicked {TARGET_NAME}"
89 | //
90 |
91 | "Custom Votes"
92 | {
93 | "Set map time"
94 | {
95 | "type" "list"
96 | "vote" "1"
97 | "ratio" "0.6"
98 | "minimum" "0"
99 | "command" "mp_timelimit {OPTION_RESULT}"
100 | "options"
101 | {
102 | "15 minutes" "15"
103 | "30 minutes" "30"
104 | "45 minutes" "45"
105 | "60 minutes" "60"
106 | }
107 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to set the map time to {OPTION_NAME}."
108 | "call_notify" "[SM] Vote cast for {yes|no}."
109 | "pass_notify" "[SM] Vote passed."
110 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
111 | "chattrigger" "votemaptime"
112 | }
113 | "Turn all talk {on|off}"
114 | {
115 | "type" "simple"
116 | "vote" "1"
117 | "ratio" "0.6"
118 | "minimum" "4"
119 | "command" "sv_alltalk {on|off}"
120 | "cvar" "sv_alltalk"
121 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to turn all-talk {on|off}."
122 | "call_notify" "[SM] Vote cast for {yes|no}."
123 | "pass_notify" "[SM] Vote passed."
124 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
125 | "chattrigger" "votealltalk"
126 | }
127 | "Change the map"
128 | {
129 | "type" "map"
130 | "vote" "1"
131 | "ratio" "0.5"
132 | "minimum" "4"
133 | "command" "sm_map {MAP_NAME}"
134 | "maplist" "default"
135 | "recentmaps" "3"
136 | "currentmap" "0"
137 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to change the map to {MAP_NAME}."
138 | "call_notify" "[SM] Vote cast for {yes|no}."
139 | "pass_notify" "[SM] Vote passed."
140 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
141 | "chattrigger" "votemap"
142 | }
143 | "Kick player"
144 | {
145 | "type" "players"
146 | "vote" "1"
147 | "minimum" "4"
148 | "ratio" "0.6"
149 | "command" "sm_kick #{TARGET_NAME}"
150 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to kick {TARGET_NAME}."
151 | "call_notify" "[SM] Vote cast for {yes|no}."
152 | "pass_notify" "[SM] Vote passed."
153 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
154 | "chattrigger" "votekick"
155 | }
156 | "Ban player"
157 | {
158 | "type" "players"
159 | "vote" "1"
160 | "minimum" "4"
161 | "ratio" "0.8"
162 | "command" "sm_ban #{TARGET_ID}"
163 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to ban {TARGET_NAME}."
164 | "call_notify" "[SM] Vote cast for {yes|no}."
165 | "pass_notify" "[SM] Vote passed."
166 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
167 | "chattrigger" "voteban"
168 | }
169 | "Mute player"
170 | {
171 | "type" "players"
172 | "vote" "1"
173 | "minimum" "4"
174 | "ratio" "0.6"
175 | "command" "sm_silence #{TARGET_ID}"
176 | "start_notify" "[SM] {VOTER_NAME} ({VOTER_STEAMID}) started a vote to mute {TARGET_NAME}."
177 | "call_notify" "[SM] Vote cast for {yes|no}."
178 | "pass_notify" "[SM] Vote passed."
179 | "fail_notify" "[SM] Vote failed. Received: {VOTE_AMOUNT} Required: {VOTE_REQUIRED}"
180 | "chattrigger" "votemute"
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) 2018 {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | customvotes Copyright (C) 2018 caxanga334
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
--------------------------------------------------------------------------------
/addons/sourcemod/scripting/customvotes.sp:
--------------------------------------------------------------------------------
1 | // ====[ INCLUDES ]============================================================
2 | #include
3 | #include
4 | #include
5 | #undef REQUIRE_PLUGIN
6 | #include
7 | #tryinclude
8 | #tryinclude
9 | // ====[ DEFINES ]=============================================================
10 | #define PLUGIN_NAME "Custom Votes"
11 | #define PLUGIN_VERSION "1.19.5U"
12 | #define MAX_VOTE_TYPES 32
13 | #define MAX_VOTE_MAPS 2048
14 | #define MAX_VOTE_OPTIONS 32
15 | #pragma semicolon 1;
16 | #pragma newdecls required;
17 |
18 | // ====[ HANDLES ]=============================================================
19 | Handle g_hArrayVotePlayerSteamID[MAXPLAYERS + 1][MAX_VOTE_TYPES];
20 | Handle g_hArrayVotePlayerIP[MAXPLAYERS + 1][MAX_VOTE_TYPES];
21 | Handle g_hArrayVoteOptionName[MAX_VOTE_TYPES];
22 | Handle g_hArrayVoteOptionResult[MAX_VOTE_TYPES];
23 | Handle g_hArrayVoteMapList[MAX_VOTE_TYPES];
24 | Handle g_hArrayRecentMaps;
25 | Handle g_hCheckEmptyTimer;
26 | Handle g_hLogFileTimer;
27 |
28 | // ====[ VARIABLES ]===========================================================
29 | int g_iMapTime;
30 | int g_iVoteCount;
31 | int g_iCurrentVoteIndex;
32 | int g_iCurrentVoteTarget;
33 | int g_iCurrentVoteMap;
34 | int g_iCurrentVoteOption;
35 | #if defined _afkmanager_included
36 | int iAfkTime;
37 | #endif
38 | int g_iVoteType[MAX_VOTE_TYPES];
39 | int g_iVoteDelay[MAX_VOTE_TYPES];
40 | int g_iVoteCooldown[MAX_VOTE_TYPES];
41 | int g_iVoteMinimum[MAX_VOTE_TYPES];
42 | int g_iVoteImmunity[MAX_VOTE_TYPES];
43 | int g_iVoteMaxCalls[MAX_VOTE_TYPES];
44 | int g_iVotePasses[MAX_VOTE_TYPES];
45 | int g_iVoteMaxPasses[MAX_VOTE_TYPES];
46 | int g_iVoteMapRecent[MAX_VOTE_TYPES];
47 | int g_iVoteCurrent[MAXPLAYERS + 1];
48 | int g_iVoteRemaining[MAXPLAYERS + 1][MAX_VOTE_TYPES];
49 | int g_iVoteLast[MAXPLAYERS + 1][MAX_VOTE_TYPES];
50 | int g_iVoteAllLast[MAX_VOTE_TYPES]; // same as g_iVoteLast but applies to all players
51 | int g_iVoteAllCooldown[MAX_VOTE_TYPES]; // cooldown between votes for ALL players
52 | bool g_bVoteCallVote[MAX_VOTE_TYPES];
53 | bool g_bVotePlayersBots[MAX_VOTE_TYPES];
54 | bool g_bVotePlayersTeam[MAX_VOTE_TYPES];
55 | bool g_bVoteMapCurrent[MAX_VOTE_TYPES];
56 | bool g_bVoteMultiple[MAX_VOTE_TYPES];
57 | bool g_bVoteForTarget[MAXPLAYERS + 1][MAX_VOTE_TYPES][MAXPLAYERS + 1];
58 | bool g_bVoteForMap[MAXPLAYERS + 1][MAX_VOTE_TYPES][MAX_VOTE_MAPS];
59 | bool g_bVoteForOption[MAXPLAYERS + 1][MAX_VOTE_TYPES][MAX_VOTE_OPTIONS];
60 | bool g_bVoteForSimple[MAXPLAYERS + 1][MAX_VOTE_TYPES];
61 | bool g_bKzTimer = false;
62 | bool g_bSourceBans = false;
63 | #if defined _afkmanager_included
64 | bool g_bAfkManager = false;
65 | #endif
66 | bool g_bMapEnded = false;
67 | float g_flVoteRatio[MAX_VOTE_TYPES];
68 | char g_strVoteName[MAX_VOTE_TYPES][MAX_NAME_LENGTH];
69 | char g_strVoteConVar[MAX_VOTE_TYPES][MAX_NAME_LENGTH];
70 | char g_strVoteOverride[MAX_VOTE_TYPES][MAX_NAME_LENGTH];
71 | char g_strVoteCommand[MAX_VOTE_TYPES][255];
72 | char g_strVoteChatTrigger[MAX_VOTE_TYPES][255];
73 | char g_strVoteStartNotify[MAX_VOTE_TYPES][255];
74 | char g_strVoteCallNotify[MAX_VOTE_TYPES][255];
75 | char g_strVotePassNotify[MAX_VOTE_TYPES][255];
76 | char g_strVoteFailNotify[MAX_VOTE_TYPES][255];
77 | char g_strVoteTargetIndex[255];
78 | char g_strVoteTargetId[255];
79 | char g_strVoteTargetAuth[255];
80 | char g_strVoteTargetName[255];
81 | char g_strConfigFile[PLATFORM_MAX_PATH];
82 | char g_sLogPath[PLATFORM_MAX_PATH];
83 | // extras
84 | Handle CvarDebugMode;
85 | Handle CvarResetOnWaveFailed;
86 | Handle CvarAutoBanEnabled;
87 | Handle CvarAutoBanWarning;
88 | Handle CvarAutoBanDuration;
89 | Handle CvarAutoBanType;
90 | #if defined _afkmanager_included
91 | Handle CvarAfkTime;
92 | Handle CvarAfkManager;
93 | #endif
94 | Handle CvarCancelVoteGameEnd;
95 | bool bDebugMode;
96 | bool bResetOnWaveFailed;
97 | bool bAutoBanEnabled;
98 | bool bAutoBanWarning;
99 | bool bAutoBanType;
100 | #if defined _afkmanager_included
101 | bool bAfkManagerEnable;
102 | #endif
103 | bool bCancelVoteGameEnd;
104 | int iAutoBanDuration;
105 | bool IsTF2 = false;
106 | bool IsCSGO = false;
107 | bool IsSyn = false;
108 | enum
109 | {
110 | VoteType_Players = 0,
111 | VoteType_Map,
112 | VoteType_List,
113 | VoteType_Simple,
114 | }
115 |
116 | // ====[ PLUGIN ]==============================================================
117 | public Plugin myinfo =
118 | {
119 | name = PLUGIN_NAME,
120 | author = "ReFlexPoison,Anonymous Player",
121 | description = PLUGIN_NAME,
122 | version = PLUGIN_VERSION,
123 | url = "http://www.sourcemod.net/"
124 | /* Special thanks to afk manager ( https://forums.alliedmods.net/showthread.php?p=708265 ) and sourcecmod anti-cheat developers.
125 | I looked at their's plugin in order to learn how to add file logging. */
126 | // Special thanks for those who contributed on github
127 | // sneak-it ( https://github.com/caxanga334/cvreduxmodified/pulls?utf8=%E2%9C%93&q=is%3Apr+user%3Asneak-it )
128 | }
129 |
130 | // ====[ FUNCTIONS ]===========================================================
131 | public void OnPluginStart()
132 | {
133 | CreateConVar("sm_customvotes_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_SPONLY | FCVAR_DONTRECORD | FCVAR_NOTIFY);
134 | CvarResetOnWaveFailed = CreateConVar("sm_cv_tf2_reset_wavefailed", "0", "Reset maxpasses on wave failed?", FCVAR_NONE, true, 0.0, true, 1.0); // reset max passes on wave failed
135 | HookConVarChange( CvarResetOnWaveFailed, OnConVarChanged );
136 | CvarAutoBanEnabled = CreateConVar("sm_cv_autoban", "0", "Should the plugin automatically ban players who evade votes?", FCVAR_NONE, true, 0.0, true, 1.0); // ban players evading votes?
137 | HookConVarChange( CvarAutoBanEnabled, OnConVarChanged );
138 | CvarAutoBanWarning = CreateConVar("sm_cv_autoban_warning", "1", "Should the plugin warn targeted players if they will be banned upon disconnecting? Requires sm_cv_autoban 1.", FCVAR_NONE, true, 0.0, true, 1.0); // warn players about ban evasion enforcement?
139 | HookConVarChange( CvarAutoBanWarning, OnConVarChanged );
140 | CvarAutoBanType = CreateConVar("sm_cv_bantype", "0", "Ban type to be used for vote evasion bans? 0 - Local | 1 - MySQL (SourceBans)", FCVAR_NONE, true, 0.0, true, 1.0); // should we use mysql banning?
141 | HookConVarChange( CvarAutoBanType, OnConVarChanged );
142 | CvarAutoBanDuration = CreateConVar("sm_cv_banduration", "15", "How long (in minutes) should the plugin ban someone for evading votes?", FCVAR_NONE, true, 0.0, true, 525600.0); // the ban duration
143 | HookConVarChange( CvarAutoBanDuration, OnConVarChanged );
144 | CvarCancelVoteGameEnd = CreateConVar("sm_cv_cancelvote", "1", "Cancel pending votes on round end?", FCVAR_NONE, true, 0.0, true, 1.0); // Cancel votes using events, default enabled due to OnMapEnd being fired after OnClientDisconnect
145 | HookConVarChange( CvarCancelVoteGameEnd, OnConVarChanged );
146 | CvarDebugMode = CreateConVar("sm_cv_debug", "0", "Enable debug mode?", FCVAR_NONE, true, 0.0, true, 1.0); // Debug mode
147 | HookConVarChange( CvarDebugMode, OnConVarChanged );
148 | #if defined _afkmanager_included
149 | CvarAfkManager = CreateConVar("sm_cv_afkenable", "0", "Enable Afk players are no longer counted in the total?", FCVAR_NONE, true, 0.0, true, 1.0); //Enable Afk players are no longer counted in the total?
150 | HookConVarChange( CvarAfkManager, OnConVarChanged );
151 | CvarAfkTime = CreateConVar("sm_cv_afktime", "30", "How long (Seconds) Afk players are no longer counted in the total?", FCVAR_NONE, true, 10.0, true, 1000.0); //How long Afk players are no longer counted in the total?
152 | HookConVarChange( CvarAfkTime, OnConVarChanged );
153 |
154 | iAfkTime = 30; // initialize the variable to the ConVar's default value. Will be overriden later by the ConVar value.
155 | #endif
156 |
157 | RegAdminCmd("sm_customvotes_reload", Command_Reload, ADMFLAG_ROOT, "Reloads the configuration file (Clears all votes)");
158 | RegAdminCmd("sm_votemenu", Command_ChooseVote, 0, "Opens the vote menu");
159 | RegConsoleCmd("changelevel", ChangeLevelCmdEnd);
160 |
161 | LoadTranslations("core.phrases");
162 | LoadTranslations("common.phrases");
163 | LoadTranslations("customvotes.phrases");
164 |
165 | BuildPath(Path_SM, g_strConfigFile, sizeof(g_strConfigFile), "configs/customvotes.cfg");
166 |
167 | AddCommandListener(OnClientSayCmd, "say");
168 | AddCommandListener(OnClientSayCmd, "say_team");
169 |
170 | g_bKzTimer = LibraryExists("KZTimer");
171 | g_hArrayRecentMaps = CreateArray(MAX_NAME_LENGTH);
172 |
173 | DetectGame();
174 |
175 | // hook required for resetting total passes when the wave is lost (MVM)
176 | // TF2 only
177 | if(IsTF2 == true)
178 | {
179 | HookEvent("mvm_wave_failed", TF_WaveFailed);
180 | HookEvent("teamplay_win_panel", TF_EventsEnded);
181 | HookEvent("arena_win_panel", TF_EventsEnded);
182 | HookEvent("pve_win_panel", TF_EventsEnded);
183 | }
184 | if(IsCSGO == true)
185 | {
186 | HookEvent("cs_win_panel_match", CSGO_MapEnd);
187 | }
188 |
189 | AutoExecConfig(true, "sm_customvotes_redux");
190 | }
191 |
192 | public void OnMapStart()
193 | {
194 | g_iMapTime = 0;
195 | g_bMapEnded = false; // map started, set it to false
196 |
197 | char strMap[MAX_NAME_LENGTH];
198 | GetCurrentMap(strMap, sizeof(strMap));
199 | int index = FindStringInArray(g_hArrayRecentMaps, strMap);
200 |
201 | if (index > -1)
202 | {
203 | RemoveFromArray(g_hArrayRecentMaps, index); // Remove duplicate map
204 | }
205 |
206 | PushArrayString(g_hArrayRecentMaps, strMap);
207 |
208 | if (IsSyn)
209 | {
210 | HookEntityOutput("trigger_changelevel","OnChangeLevel",ChangeLevelEnd);
211 | }
212 |
213 | Config_Load();
214 | CreateTimer(1.0, Timer_Second, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
215 | CreateLogFile();
216 | // Timer for servers that runs the same map 24/7, fires every 6 hours
217 | g_hLogFileTimer = CreateTimer(21600.0, Timer_ReCreateLogPath, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
218 | }
219 |
220 | public void OnMapEnd()
221 | {
222 | if(!g_bMapEnded)
223 | {
224 | g_bMapEnded = true;
225 | }
226 |
227 | if((!IsCSGO || !IsTF2) && !bCancelVoteGameEnd)
228 | {
229 | if(IsVoteInProgress()) // is vote in progress?
230 | {
231 | CancelVote(); // cancel any running votes on map end.
232 | LogToFileEx(g_sLogPath,
233 | "[Custom Votes] Map end while a vote was in progress, canceling vote.");
234 | }
235 | }
236 |
237 | delete g_hLogFileTimer;
238 | }
239 |
240 | public Action ChangeLevelEnd(const char[] output, int caller, int activator, float delay)
241 | {
242 | if ((IsValidEntity(caller)) && (IsEntNetworkable(caller)))
243 | {
244 | char clschk[32];
245 | GetEntityClassname(caller,clschk,sizeof(clschk));
246 | if (StrEqual(clschk,"trigger_changelevel",false))
247 | {
248 | if (!bCancelVoteGameEnd)
249 | {
250 | g_bMapEnded = true;
251 | if(IsVoteInProgress()) // is vote in progress?
252 | {
253 | CancelVote(); // cancel any running votes on map end.
254 | LogToFileEx(g_sLogPath, "[Custom Votes] Map end while a vote was in progress, canceling vote.");
255 | }
256 | }
257 | }
258 | }
259 |
260 | return Plugin_Continue;
261 | }
262 |
263 | public Action ChangeLevelCmdEnd(int client, int args)
264 | {
265 | if ((IsSyn) && (client == 0) && (args > 0))
266 | {
267 | if (!bCancelVoteGameEnd)
268 | {
269 | g_bMapEnded = true;
270 | if(IsVoteInProgress()) // is vote in progress?
271 | {
272 | CancelVote(); // cancel any running votes on map end.
273 | LogToFileEx(g_sLogPath, "[Custom Votes] Map end while a vote was in progress, canceling vote.");
274 | }
275 | }
276 | }
277 | return Plugin_Continue;
278 | }
279 |
280 | public void OnConVarChanged( Handle hConVar, const char[] strOldValue, const char[] strNewValue )
281 | {
282 | bResetOnWaveFailed = GetConVarBool( CvarResetOnWaveFailed );
283 | bAutoBanEnabled = GetConVarBool( CvarAutoBanEnabled );
284 | bAutoBanWarning = GetConVarBool( CvarAutoBanWarning );
285 | bAutoBanType = GetConVarBool( CvarAutoBanType );
286 | iAutoBanDuration = GetConVarInt( CvarAutoBanDuration );
287 | bCancelVoteGameEnd = GetConVarBool( CvarCancelVoteGameEnd );
288 | bDebugMode = GetConVarBool( CvarDebugMode );
289 | #if defined _afkmanager_included
290 | bAfkManagerEnable = GetConVarBool( CvarAfkManager );
291 | iAfkTime = GetConVarBool( CvarAfkTime );
292 | #endif
293 | }
294 |
295 | stock void DetectGame()
296 | {
297 | char game_mod[32];
298 | GetGameFolderName(game_mod, sizeof(game_mod));
299 |
300 | if (strcmp(game_mod, "tf", false) == 0)
301 | {
302 | IsTF2 = true;
303 | LogMessage("Game Detected: Team Fortress 2.");
304 | }
305 | else if (strcmp(game_mod, "csgo", false) == 0)
306 | {
307 | IsCSGO = true;
308 | LogMessage("Game Detected: Counter-Strike: Global Offensive.");
309 | }
310 | else if (strcmp(game_mod, "synergy", false) == 0)
311 | {
312 | IsSyn = true;
313 | LogMessage("Game Detected: Synergy.");
314 | }
315 | else
316 | {
317 | LogMessage("Game Detected: Other.");
318 | }
319 | }
320 |
321 | public void OnLibraryAdded(const char[] szName)
322 | {
323 | if (StrEqual(szName, "KZTimer"))
324 | {
325 | g_bKzTimer = true;
326 | }
327 |
328 | if (StrEqual(szName, "sourcebans++"))
329 | {
330 | g_bSourceBans = true;
331 | }
332 | #if defined _afkmanager_included
333 | if (StrEqual(szName, "afkmanager"))
334 | {
335 | g_bAfkManager = true;
336 | }
337 | #endif
338 | }
339 |
340 | public void OnLibraryRemoved(const char[] szName)
341 | {
342 | if (StrEqual(szName, "KZTimer"))
343 | {
344 | g_bKzTimer = false;
345 | }
346 |
347 | if (StrEqual(szName, "sourcebans++"))
348 | {
349 | g_bSourceBans = false;
350 | }
351 | #if defined _afkmanager_included
352 | if (StrEqual(szName, "afkmanager"))
353 | {
354 | g_bAfkManager = false;
355 | }
356 | #endif
357 | }
358 |
359 | public void OnClientConnected(int iTarget)
360 | {
361 | g_iVoteCurrent[iTarget] = -1;
362 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
363 | {
364 | g_iVoteRemaining[iTarget][iVote] = g_iVoteMaxCalls[iVote];
365 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
366 | {
367 | g_bVoteForTarget[iVoter][iVote][iTarget] = false;
368 | g_bVoteForTarget[iTarget][iVote][iVoter] = false;
369 | }
370 |
371 | for(int iMap = 0; iMap < MAX_VOTE_MAPS; iMap++)
372 | g_bVoteForMap[iTarget][iVote][iMap] = false;
373 |
374 | for(int iOption = 0; iOption < MAX_VOTE_OPTIONS; iOption++)
375 | g_bVoteForOption[iTarget][iVote][iOption] = false;
376 |
377 | g_bVoteForSimple[iTarget][iVote] = false;
378 |
379 | if(g_hArrayVotePlayerSteamID[iTarget][iVote] != INVALID_HANDLE)
380 | ClearArray(g_hArrayVotePlayerSteamID[iTarget][iVote]);
381 |
382 | if(g_hArrayVotePlayerIP[iTarget][iVote] != INVALID_HANDLE)
383 | ClearArray(g_hArrayVotePlayerIP[iTarget][iVote]);
384 | }
385 |
386 | char strClientIP[MAX_NAME_LENGTH];
387 | if(!GetClientIP(iTarget, strClientIP, sizeof(strClientIP)))
388 | return;
389 |
390 | char strSavedIP[MAX_NAME_LENGTH];
391 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
392 | {
393 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
394 | {
395 | if(g_bVoteForTarget[iVoter][iVote][iTarget])
396 | break;
397 |
398 | if(g_hArrayVotePlayerIP[iVoter][iVote] == INVALID_HANDLE)
399 | continue;
400 |
401 | for(int iIP = 0; iIP < GetArraySize(g_hArrayVotePlayerIP[iVoter][iVote]); iIP++)
402 | {
403 | GetArrayString(g_hArrayVotePlayerIP[iVoter][iVote], iIP, strSavedIP, sizeof(strSavedIP));
404 | if(StrEqual(strSavedIP, strClientIP))
405 | {
406 | g_bVoteForTarget[iVoter][iVote][iTarget] = true;
407 | break;
408 | }
409 | }
410 | }
411 | }
412 |
413 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
414 | CheckVotesForTarget(iVote, iTarget);
415 | }
416 |
417 | public void OnClientAuthorized(int iTarget, const char[] strTargetSteamId)
418 | {
419 | char strClientAuth[MAX_NAME_LENGTH];
420 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
421 | {
422 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
423 | {
424 | if(g_bVoteForTarget[iVoter][iVote][iTarget])
425 | break;
426 |
427 | if(g_hArrayVotePlayerSteamID[iVoter][iVote] == INVALID_HANDLE)
428 | continue;
429 |
430 | for(int iSteamId = 1; iSteamId < GetArraySize(g_hArrayVotePlayerSteamID[iVoter][iVote]); iSteamId++)
431 | {
432 | GetArrayString(g_hArrayVotePlayerSteamID[iVoter][iVote], iSteamId, strClientAuth, sizeof(strClientAuth));
433 | if(StrEqual(strTargetSteamId, strClientAuth))
434 | {
435 | g_bVoteForTarget[iVoter][iVote][iTarget] = true;
436 | break;
437 | }
438 | }
439 | }
440 | }
441 |
442 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
443 | CheckVotesForTarget(iVote, iTarget);
444 | }
445 |
446 | public void OnClientDisconnect(int iTarget)
447 | {
448 | g_iVoteCurrent[iTarget] = -1;
449 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
450 | {
451 | g_iVoteRemaining[iTarget][iVote] = g_iVoteMaxCalls[iVote];
452 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
453 | {
454 | g_bVoteForTarget[iVoter][iVote][iTarget] = false;
455 | g_bVoteForTarget[iTarget][iVote][iVoter] = false;
456 | }
457 |
458 | for(int iMap = 0; iMap < MAX_VOTE_MAPS; iMap++)
459 | g_bVoteForMap[iTarget][iVote][iMap] = false;
460 |
461 | for(int iOption = 0; iOption < MAX_VOTE_OPTIONS; iOption++)
462 | g_bVoteForOption[iTarget][iVote][iOption] = false;
463 |
464 | g_bVoteForSimple[iTarget][iVote] = false;
465 |
466 | if(g_hArrayVotePlayerSteamID[iTarget][iVote] != INVALID_HANDLE)
467 | ClearArray(g_hArrayVotePlayerSteamID[iTarget][iVote]);
468 |
469 | if(g_hArrayVotePlayerIP[iTarget][iVote] != INVALID_HANDLE)
470 | ClearArray(g_hArrayVotePlayerIP[iTarget][iVote]);
471 | }
472 |
473 | for(int iVote = 0; iVote < MAX_VOTE_TYPES; iVote++)
474 | {
475 | switch(g_iVoteType[iVote])
476 | {
477 | case VoteType_Players:
478 | {
479 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
480 | CheckVotesForTarget(iVote, iVoter);
481 | }
482 | case VoteType_Map:
483 | {
484 | for(int iMap = 0; iMap < MAX_VOTE_MAPS; iMap++)
485 | CheckVotesForMap(iVote, iMap);
486 | }
487 | case VoteType_List:
488 | {
489 | for(int iOption = 0; iOption < MAX_VOTE_OPTIONS; iOption++)
490 | CheckVotesForOption(iVote, iOption);
491 | }
492 | case VoteType_Simple:
493 | {
494 | for(int iSimple = 0; iSimple < MAX_VOTE_TYPES; iSimple++)
495 | CheckVotesForSimple(iVote);
496 | }
497 | }
498 | }
499 | if ((g_bMapEnded) && (IsVoteInProgress()))
500 | {
501 | CancelVote(); // cancel any running votes on map end.
502 | LogToFileEx(g_sLogPath,
503 | "[Custom Votes] Map end while a vote was in progress, canceling vote.");
504 | g_iCurrentVoteTarget = -1;
505 | }
506 | // Experimental vote evasion logging
507 | if(g_iCurrentVoteTarget >= 1 && IsVoteInProgress()) // Do we have a target and the vote is in progress
508 | {
509 | if(iTarget == g_iCurrentVoteTarget) // the id of the player who just disconnected matches the vote target id.
510 | {
511 | // get target's name
512 | char LogstrTargetName[MAX_NAME_LENGTH];
513 | GetClientName(iTarget, LogstrTargetName, sizeof(LogstrTargetName));
514 |
515 | // get target's SteamID
516 | char LstrTargetAuth[MAX_NAME_LENGTH];
517 | GetClientAuthId(iTarget, AuthId_Steam2, LstrTargetAuth, sizeof(LstrTargetAuth));
518 |
519 | // Logging Vote
520 | LogToFileEx(g_sLogPath,
521 | "[Custom Votes] Vote target disconnected while vote was in progress! The target was: %s ( %s ).",
522 | LogstrTargetName,
523 | LstrTargetAuth);
524 |
525 | if(bAutoBanEnabled)
526 | {
527 | if(g_bSourceBans && bAutoBanType) // SourceBans is available and the plugin is told to use it
528 | {
529 | SBPP_BanPlayer(0, iTarget, iAutoBanDuration, "Vote Evasion");
530 | }
531 | else
532 | {
533 | BanClient(iTarget, iAutoBanDuration, BANFLAG_AUTO, "Vote Evasion", "Vote Evasion", "CVR");
534 | }
535 |
536 | if(!g_bSourceBans && bAutoBanType) // Plugin is told to use sourcebans but sourcebans is not available
537 | {
538 | LogError("Unable to ban using SourceBans++.");
539 | }
540 | }
541 | }
542 | }
543 |
544 | if( g_hCheckEmptyTimer == null )
545 | {
546 | g_hCheckEmptyTimer = CreateTimer(0.2, Timer_CheckEmpty);
547 | }
548 |
549 |
550 | }
551 |
552 | void CreateLogFile() // creates the log file in the system
553 | {
554 | char cTime[64];
555 | FormatTime(cTime, sizeof(cTime), "%Y%m%d"); // add date to file name
556 | // Path used for logging.
557 | BuildPath(Path_SM, g_sLogPath, sizeof(g_sLogPath), "logs/customvotes_%s.log", cTime);
558 | }
559 |
560 | public Action Timer_CheckEmpty(Handle timer)
561 | {
562 | ResetVotesIfEmpty();
563 | g_hCheckEmptyTimer = null;
564 | return Plugin_Stop;
565 | }
566 |
567 | // reset all votes max passes if the server is empty
568 | void ResetVotesIfEmpty()
569 | {
570 | if( GetClientCount(true) == 0 )
571 | {
572 | for(int iVote = 0; iVote < MAX_VOTE_TYPES; iVote++)
573 | {
574 | g_iVotePasses[iVote] = 0;
575 | }
576 |
577 | LogToFileEx(g_sLogPath, "[Custom Votes] Server is empty, resetting all votes.");
578 | }
579 | }
580 |
581 | // ====[ COMMANDS ]============================================================
582 | public Action Command_Reload(int iClient, int iArgs)
583 | {
584 | Config_Load();
585 | return Plugin_Handled;
586 | }
587 |
588 | public Action Command_ChooseVote(int iClient, int iArgs)
589 | {
590 | if(!IsValidClient(iClient))
591 | return Plugin_Continue;
592 |
593 | if(IsVoteInProgress())
594 | {
595 | CReplyToCommand(iClient, "[SM] %t", "Vote in Progress");
596 | return Plugin_Handled;
597 | }
598 |
599 | Menu_ChooseVote(iClient);
600 | return Plugin_Handled;
601 | }
602 |
603 | public Action OnClientSayCmd(int iVoter, const char[] strCmd, int iArgc)
604 | {
605 | if(!IsValidClient(iVoter))
606 | return Plugin_Continue;
607 |
608 | char strText[255];
609 | GetCmdArgString(strText, sizeof(strText));
610 | StripQuotes(strText);
611 |
612 | ReplaceString(strText, sizeof(strText), "!", "");
613 | ReplaceString(strText, sizeof(strText), "/", "");
614 |
615 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
616 | {
617 | if(StrEqual(g_strVoteChatTrigger[iVote], strText))
618 | {
619 | g_iVoteCurrent[iVoter] = iVote;
620 | switch(g_iVoteType[iVote])
621 | {
622 | case VoteType_Players: Menu_PlayersVote(iVote, iVoter);
623 | case VoteType_Map: Menu_MapVote(iVote, iVoter);
624 | case VoteType_List: Menu_ListVote(iVote, iVoter);
625 | case VoteType_Simple: CastSimpleVote(iVote, iVoter);
626 | }
627 | break;
628 | }
629 | }
630 |
631 | return Plugin_Continue;
632 | }
633 |
634 | // ====[ MENUS ]===============================================================
635 | public void Menu_ChooseVote(int iVoter)
636 | {
637 | Handle hMenu = CreateMenu(MenuHandler_Vote);
638 | SetMenuTitle(hMenu, "Vote Menu:");
639 |
640 | char strIndex[4];
641 | int iTime = GetTime();
642 | for(int iVote = 0; iVote < g_iVoteCount; iVote++)
643 | {
644 | int iFlags;
645 |
646 | // Admin access
647 | if(g_strVoteOverride[iVote][0] && !CheckCommandAccess(iVoter, g_strVoteOverride[iVote], 0))
648 | iFlags = ITEMDRAW_DISABLED;
649 |
650 | // Max votes
651 | else if(g_iVoteRemaining[iVoter][iVote] <= 0 && g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
652 | iFlags = ITEMDRAW_DISABLED;
653 |
654 | // Max passes
655 | else if(g_iVotePasses[iVote] >= g_iVoteMaxPasses[iVote] && g_iVoteMaxPasses[iVote] > 0)
656 | iFlags = ITEMDRAW_DISABLED;
657 |
658 | // Cooldown
659 | else if(iTime - g_iVoteLast[iVoter][iVote] < g_iVoteCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
660 | iFlags = ITEMDRAW_DISABLED;
661 |
662 | // Cooldown (All Players)
663 | else if(iTime - g_iVoteAllLast[iVote] < g_iVoteAllCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
664 | iFlags = ITEMDRAW_DISABLED;
665 |
666 | IntToString(iVote, strIndex, sizeof(strIndex));
667 |
668 | char strName[56];
669 | strcopy(strName, sizeof(strName), g_strVoteName[iVote]);
670 |
671 | if(g_iVoteType[iVote] == VoteType_Simple)
672 | {
673 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
674 | {
675 | ReplaceString(strName, sizeof(strName), "{On|Off}", "Off", true);
676 | ReplaceString(strName, sizeof(strName), "{on|off}", "off", true);
677 | }
678 | else
679 | {
680 | ReplaceString(strName, sizeof(strName), "{On|Off}", "On", true);
681 | ReplaceString(strName, sizeof(strName), "{on|off}", "on", true);
682 | }
683 |
684 | if(!g_bVoteCallVote[iVote])
685 | Format(strName, sizeof(strName), "%s [%i/%i]", strName, GetVotesForSimple(iVote), GetRequiredVotes(iVote));
686 | }
687 |
688 | AddMenuItem(hMenu, strIndex, strName, iFlags);
689 | }
690 |
691 | for (int i=1; i<=MaxClients; i++)
692 | {
693 | if (IsClientInGame(i) && g_bKzTimer)
694 | {
695 | KZTimer_StopUpdatingOfClimbersMenu(i);
696 | }
697 | }
698 |
699 | DisplayMenu(hMenu, iVoter, 30);
700 | }
701 |
702 | public int MenuHandler_Vote(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
703 | {
704 | if(iAction == MenuAction_End)
705 | {
706 | CloseHandle(hMenu);
707 | return 0;
708 | }
709 |
710 | if(iAction == MenuAction_Select)
711 | {
712 | char strBuffer[8];
713 | GetMenuItem(hMenu, iParam2, strBuffer, sizeof(strBuffer));
714 |
715 | int iVote = StringToInt(strBuffer);
716 | g_iVoteCurrent[iVoter] = iVote;
717 |
718 | switch(g_iVoteType[iVote])
719 | {
720 | case VoteType_Players: Menu_PlayersVote(iVote, iVoter);
721 | case VoteType_Map: Menu_MapVote(iVote, iVoter);
722 | case VoteType_List: Menu_ListVote(iVote, iVoter);
723 | case VoteType_Simple: CastSimpleVote(iVote, iVoter);
724 | }
725 | }
726 | return 0;
727 | }
728 |
729 | public void Menu_PlayersVote(int iVote, int iVoter)
730 | {
731 | if(IsVoteInProgress())
732 | {
733 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
734 | return;
735 | }
736 |
737 | if(g_strVoteOverride[iVote][0] && !CheckCommandAccess(iVoter, g_strVoteOverride[iVote], 0))
738 | {
739 | CPrintToChat(iVoter, "[SM] %t", "No Access");
740 | return;
741 | }
742 |
743 | if(g_iVoteRemaining[iVoter][iVote] <= 0 && g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
744 | {
745 | CPrintToChat(iVoter, "%t", "No Votes Remaining");
746 | return;
747 | }
748 |
749 | if(g_iVotePasses[iVote] >= g_iVoteMaxPasses[iVote] && g_iVoteMaxPasses[iVote] > 0)
750 | {
751 | CPrintToChat(iVoter, "%t", "Voting No Longer Available");
752 | return;
753 | }
754 |
755 | if(g_iMapTime < g_iVoteDelay[iVote])
756 | {
757 | CPrintToChat(iVoter, "%t", "Vote Delay", g_iVoteDelay[iVote] - g_iMapTime);
758 | return;
759 | }
760 |
761 | int iTime = GetTime();
762 | if(iTime - g_iVoteLast[iVoter][iVote] < g_iVoteCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
763 | {
764 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteCooldown[iVote] - (iTime - g_iVoteLast[iVoter][iVote]));
765 | return;
766 | }
767 |
768 | if(iTime - g_iVoteAllLast[iVote] < g_iVoteAllCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
769 | {
770 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteAllCooldown[iVote] - (iTime - g_iVoteAllLast[iVote]));
771 | return;
772 | }
773 |
774 | Handle hMenu = CreateMenu(MenuHandler_PlayersVote);
775 | SetMenuTitle(hMenu, "%s:", g_strVoteName[iVote]);
776 | SetMenuExitBackButton(hMenu, true);
777 |
778 | int iCount;
779 | char strUserId[8];
780 | char strName[MAX_NAME_LENGTH + 12];
781 |
782 | int iVoterTeam = GetClientTeam(iVoter);
783 | for(int iTarget = 1; iTarget <= MaxClients; iTarget++) if(IsClientInGame(iTarget))
784 | {
785 | if(!g_bVotePlayersBots[iVote] && IsFakeClient(iTarget))
786 | continue;
787 |
788 | if(g_bVotePlayersTeam[iVote] && GetClientTeam(iTarget) != iVoterTeam)
789 | continue;
790 |
791 | int iFlags;
792 | if(iTarget == iVoter)
793 | iFlags = ITEMDRAW_DISABLED;
794 |
795 | AdminId idAdmin = GetUserAdmin(iTarget);
796 | if(idAdmin != INVALID_ADMIN_ID)
797 | {
798 | if(GetAdminImmunityLevel(idAdmin) >= g_iVoteImmunity[iVote])
799 | iFlags = ITEMDRAW_DISABLED;
800 | }
801 |
802 | IntToString(GetClientUserId(iTarget), strUserId, sizeof(strUserId));
803 |
804 | if(g_bVoteCallVote[iVote])
805 | GetClientName(iTarget, strName, sizeof(strName));
806 | else
807 | Format(strName, sizeof(strName), "%N [%i/%i]", iTarget, GetVotesForTarget(iVote, iTarget), GetRequiredVotes(iVote));
808 |
809 | AddVoteMenuOption(hMenu, GetVotesForTarget(iVote, iTarget), strUserId, strName, iFlags);
810 | iCount++;
811 | }
812 |
813 | if(iCount <= 0)
814 | {
815 | CPrintToChat(iVoter, "%t", "No Valid Clients");
816 | return;
817 | }
818 |
819 | for (int i=1; i<=MaxClients; i++)
820 | {
821 | if (IsClientInGame(i) && g_bKzTimer)
822 | {
823 | KZTimer_StopUpdatingOfClimbersMenu(i);
824 | }
825 | }
826 |
827 | DisplayMenu(hMenu, iVoter, 30);
828 | }
829 |
830 | public int MenuHandler_PlayersVote(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
831 | {
832 | if(iAction == MenuAction_End)
833 | {
834 | CloseHandle(hMenu);
835 | return 0;
836 | }
837 |
838 | if(iAction == MenuAction_Cancel && iParam2 == MenuCancel_ExitBack)
839 | {
840 | Menu_ChooseVote(iVoter);
841 | return 0;
842 | }
843 |
844 | if(iAction == MenuAction_Select)
845 | {
846 | char strBuffer[8];
847 | GetMenuItem(hMenu, iParam2, strBuffer, sizeof(strBuffer));
848 |
849 | int iVote = g_iVoteCurrent[iVoter];
850 | if(iVote == -1)
851 | return 0;
852 |
853 | if(IsVoteInProgress())
854 | {
855 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
856 | return 0;
857 | }
858 |
859 | int iTarget = GetClientOfUserId(StringToInt(strBuffer));
860 | if(!IsValidClient(iTarget))
861 | {
862 | CPrintToChat(iVoter, "%t", "Player no longer available");
863 | Menu_ChooseVote(iVoter);
864 | return 0;
865 | }
866 |
867 | if(g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
868 | {
869 | g_iVoteRemaining[iVoter][iVote]--;
870 | CPrintToChat(iVoter, "%t", "Votes Remaining", g_iVoteRemaining[iVoter][iVote]);
871 | }
872 |
873 | g_iVoteLast[iVoter][iVote] = GetTime();
874 | g_iVoteAllLast[iVote] = GetTime();
875 | if(g_bVoteCallVote[iVote])
876 | {
877 | Vote_Players(iVote, iVoter, iTarget);
878 | return 0;
879 | }
880 |
881 | g_bVoteForTarget[iVoter][iVote][iTarget] = true;
882 | if(!g_bVoteMultiple[iVote])
883 | {
884 | for(int iClient = 1; iClient <= MaxClients; iClient++)
885 | {
886 | if(iClient != iTarget)
887 | g_bVoteForTarget[iVoter][iVote][iClient] = false;
888 | }
889 | }
890 |
891 | if(g_strVoteCallNotify[iVote][0])
892 | {
893 | char strNotification[255];
894 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[iVote]);
895 |
896 | FormatVoteString(iVote, iTarget, strNotification, sizeof(strNotification));
897 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
898 | FormatTargetString(iVote, iTarget, strNotification, sizeof(strNotification));
899 |
900 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
901 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
902 |
903 | CPrintToChatAll("%s", strNotification);
904 | }
905 |
906 | if(!IsFakeClient(iTarget) && IsClientAuthorized(iTarget))
907 | {
908 | char strAuth[MAX_NAME_LENGTH];
909 | GetClientAuthId(iVoter, AuthId_Steam2, strAuth, sizeof(strAuth));
910 | PushArrayString(g_hArrayVotePlayerSteamID[iVoter][iVote], strAuth);
911 | }
912 |
913 | char strIP[MAX_NAME_LENGTH];
914 | if(GetClientIP(iTarget, strIP, sizeof(strIP)))
915 | PushArrayString(g_hArrayVotePlayerIP[iVoter][iVote], strIP);
916 |
917 | CheckVotesForTarget(iVote, iTarget);
918 | Menu_ChooseVote(iVoter);
919 | }
920 | return 0;
921 | }
922 |
923 | public void Vote_Players(int iVote, int iVoter, int iTarget)
924 | {
925 | if(IsVoteInProgress())
926 | {
927 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
928 | return;
929 | }
930 |
931 | int iPlayers[MAXPLAYERS + 1];
932 | int iTotal;
933 |
934 | for(int i = 1; i <= MaxClients; i++)
935 | {
936 | if (bDebugMode)
937 | {
938 | if (IsClientInGame(i))
939 | {
940 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Vote_Players - Player %L %s %s", i, IsFakeClient(i) ? "FAKE CLIENT" : "REAL CLIENT", IsAFKClient(i) ? "AFK" : "NOT AFK");
941 | }
942 | else
943 | {
944 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Ignoring client index %i, IsClientInGame == false!", i);
945 | }
946 | }
947 |
948 | g_bVoteForTarget[i][iVote][iTarget] = false;
949 | if(IsClientInGame(i) && !IsFakeClient(i) && i != iTarget && !IsAFKClient(i))
950 | {
951 | if(g_bVotePlayersTeam[iVote])
952 | {
953 | if(GetClientTeam(i) == GetClientTeam(iVoter))
954 | iPlayers[iTotal++] = i;
955 | }
956 | else
957 | iPlayers[iTotal++] = i;
958 | }
959 | }
960 |
961 | if(g_iVoteMinimum[iVote] > iTotal || iTotal <= 0)
962 | {
963 | CPrintToChat(iVoter, "%t", "Not Enough Valid Clients");
964 | return;
965 | }
966 | if(g_strVoteStartNotify[iVote][0]) // Vote Start Notify
967 | {
968 | char strNotification[255];
969 | strcopy(strNotification, sizeof(strNotification), g_strVoteStartNotify[iVote]);
970 |
971 | FormatVoteString(iVote, iTarget, strNotification, sizeof(strNotification));
972 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
973 | FormatTargetString(iVote, iTarget, strNotification, sizeof(strNotification));
974 |
975 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
976 | {
977 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
978 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
979 | }
980 | else
981 | {
982 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
983 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
984 | }
985 |
986 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
987 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
988 |
989 | CPrintToChatAll("%s", strNotification);
990 |
991 | // get vote name
992 | char LogstrName[56];
993 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
994 |
995 | // get voter's name
996 | char LogstrVoterName[MAX_NAME_LENGTH];
997 | GetClientName(iVoter, LogstrVoterName, sizeof(LogstrVoterName));
998 |
999 | // get voter's SteamID
1000 | char LstrVoterAuth[MAX_NAME_LENGTH];
1001 | GetClientAuthId(iVoter, AuthId_Steam2, LstrVoterAuth, sizeof(LstrVoterAuth));
1002 |
1003 | // get target's name
1004 | char LogstrTargetName[MAX_NAME_LENGTH];
1005 | GetClientName(iTarget, LogstrTargetName, sizeof(LogstrTargetName));
1006 |
1007 | // get target's SteamID
1008 | char LstrTargetAuth[MAX_NAME_LENGTH];
1009 | GetClientAuthId(iTarget, AuthId_Steam2, LstrTargetAuth, sizeof(LstrTargetAuth));
1010 |
1011 | // Print vote to console ( HLSW )
1012 | PrintToServer("[Custom Votes] Vote %s started by %s ( %s ) targeting %s ( %s ).",
1013 | LogstrName,
1014 | LogstrVoterName,
1015 | LstrVoterAuth,
1016 | LogstrTargetName,
1017 | LstrTargetAuth);
1018 |
1019 | // Logging Vote
1020 | LogToFileEx(g_sLogPath,
1021 | "[Custom Votes] Vote %s started by %s ( %s ) targeting %s ( %s ).",
1022 | LogstrName,
1023 | LogstrVoterName,
1024 | LstrVoterAuth,
1025 | LogstrTargetName,
1026 | LstrTargetAuth);
1027 | }
1028 |
1029 | Handle hMenu = CreateMenu(VoteHandler_Players);
1030 |
1031 | char strTarget[MAX_NAME_LENGTH];
1032 | char strBuffer[MAX_NAME_LENGTH + 12];
1033 |
1034 | GetClientName(iTarget, strTarget, sizeof(strTarget));
1035 | Format(strBuffer, sizeof(strBuffer), "%s (%s)", g_strVoteName[iVote], strTarget);
1036 |
1037 | SetMenuTitle(hMenu, "%s", strBuffer);
1038 | SetMenuExitButton(hMenu, false);
1039 |
1040 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1041 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1042 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1043 |
1044 | if(GetRandomInt(1, 2) == 1)
1045 | {
1046 | AddMenuItem(hMenu, "Yes", "Yes");
1047 | AddMenuItem(hMenu, "No", "No");
1048 | }
1049 | else
1050 | {
1051 | AddMenuItem(hMenu, "No", "No");
1052 | AddMenuItem(hMenu, "Yes", "Yes");
1053 | }
1054 | g_iCurrentVoteIndex = iVote;
1055 | g_iCurrentVoteTarget = iTarget;
1056 |
1057 | IntToString(iTarget, g_strVoteTargetIndex, sizeof(g_strVoteTargetIndex));
1058 | IntToString(GetClientUserId(iTarget), g_strVoteTargetId, sizeof(g_strVoteTargetId));
1059 | GetClientAuthId(iTarget, AuthId_Steam2, g_strVoteTargetAuth, sizeof(g_strVoteTargetAuth));
1060 | strcopy(g_strVoteTargetName, sizeof(g_strVoteTargetName), strTarget);
1061 |
1062 | if(bAutoBanEnabled && bAutoBanWarning)
1063 | {
1064 | CPrintToChat(iTarget, "%t", "Ban Warning");
1065 | }
1066 |
1067 | VoteMenu(hMenu, iPlayers, iTotal, 30);
1068 | }
1069 |
1070 | public int VoteHandler_Players(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
1071 | {
1072 | if(iAction == MenuAction_End)
1073 | {
1074 | CloseHandle(hMenu);
1075 | }
1076 | if(iAction == MenuAction_Select)
1077 | {
1078 | char strInfo[16];
1079 | GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));
1080 |
1081 | if(StrEqual(strInfo, "Yes", false))
1082 | {
1083 | g_bVoteForTarget[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteTarget] = true;
1084 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1085 | {
1086 | char strNotification[255];
1087 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1088 |
1089 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1090 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1091 | FormatTargetString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1092 |
1093 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1094 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1095 |
1096 | CPrintToChatAll("%s", strNotification);
1097 | }
1098 | }
1099 | else if(StrEqual(strInfo, "No", false))
1100 | {
1101 | g_bVoteForTarget[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteTarget] = false;
1102 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1103 | {
1104 | char strNotification[255];
1105 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1106 |
1107 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1108 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1109 | FormatTargetString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1110 |
1111 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "No", true);
1112 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "no", true);
1113 |
1114 | CPrintToChatAll("%s", strNotification);
1115 | }
1116 | }
1117 | }
1118 | else if(iAction == MenuAction_VoteEnd)
1119 | {
1120 | if(!CheckVotesForTarget(g_iCurrentVoteIndex, g_iCurrentVoteTarget) && g_strVoteFailNotify[g_iCurrentVoteIndex][0])
1121 | {
1122 | char strNotification[255];
1123 | strcopy(strNotification, sizeof(strNotification), g_strVoteFailNotify[g_iCurrentVoteIndex]);
1124 |
1125 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1126 | FormatTargetString(g_iCurrentVoteIndex, g_iCurrentVoteTarget, strNotification, sizeof(strNotification));
1127 |
1128 | CPrintToChatAll("%s", strNotification);
1129 | }
1130 |
1131 | for (int i = 1; i <= MaxClients; ++i)
1132 | {
1133 | g_bVoteForTarget[i][g_iCurrentVoteIndex][g_iCurrentVoteTarget] = false;
1134 | }
1135 |
1136 | g_iCurrentVoteIndex = g_iCurrentVoteTarget = -1;
1137 | strcopy(g_strVoteTargetIndex, sizeof(g_strVoteTargetIndex), "");
1138 | strcopy(g_strVoteTargetId, sizeof(g_strVoteTargetId), "");
1139 | strcopy(g_strVoteTargetAuth, sizeof(g_strVoteTargetAuth), "");
1140 | strcopy(g_strVoteTargetName, sizeof(g_strVoteTargetName), "");
1141 | }
1142 | return 0;
1143 | }
1144 |
1145 | public void Menu_MapVote(int iVote, int iVoter)
1146 | {
1147 | if(IsVoteInProgress())
1148 | {
1149 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1150 | return;
1151 | }
1152 |
1153 | if(g_strVoteOverride[iVote][0] && !CheckCommandAccess(iVoter, g_strVoteOverride[iVote], 0))
1154 | {
1155 | CPrintToChat(iVoter, "[SM] %t", "No Access");
1156 | return;
1157 | }
1158 |
1159 | if(g_iVoteRemaining[iVoter][iVote] <= 0 && g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1160 | {
1161 | CPrintToChat(iVoter, "%t", "No Votes Remaining");
1162 | return;
1163 | }
1164 |
1165 | if(g_iVotePasses[iVote] >= g_iVoteMaxPasses[iVote] && g_iVoteMaxPasses[iVote] > 0)
1166 | {
1167 | CPrintToChat(iVoter, "%t", "Voting No Longer Available");
1168 | return;
1169 | }
1170 |
1171 | if(g_iMapTime < g_iVoteDelay[iVote])
1172 | {
1173 | CPrintToChat(iVoter, "%t", "Vote Delay", g_iVoteDelay[iVote] - g_iMapTime);
1174 | return;
1175 | }
1176 |
1177 | int iTime = GetTime();
1178 | if(iTime - g_iVoteLast[iVoter][iVote] < g_iVoteCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1179 | {
1180 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteCooldown[iVote] - (iTime - g_iVoteLast[iVoter][iVote]));
1181 | return;
1182 | }
1183 |
1184 | if(iTime - g_iVoteAllLast[iVote] < g_iVoteAllCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1185 | {
1186 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteAllCooldown[iVote] - (iTime - g_iVoteAllLast[iVote]));
1187 | return;
1188 | }
1189 |
1190 | Handle hMenu = CreateMenu(MenuHandler_MapVote);
1191 | SetMenuTitle(hMenu, "%s:", g_strVoteName[iVote]);
1192 | SetMenuExitBackButton(hMenu, true);
1193 |
1194 | char strMap[MAX_NAME_LENGTH];
1195 | char strCurrentMap[MAX_NAME_LENGTH];
1196 | char strRecentMap[MAX_NAME_LENGTH];
1197 | char strBuffer[MAX_NAME_LENGTH + 12];
1198 | GetCurrentMap(strCurrentMap, sizeof(strCurrentMap));
1199 |
1200 | int iActiveMap = GetArraySize(g_hArrayRecentMaps) - 1;
1201 | int iOldestRecentMap = iActiveMap - g_iVoteMapRecent[iVote];
1202 | iOldestRecentMap = (iOldestRecentMap > 0) ? iOldestRecentMap : 0;
1203 |
1204 | int iMapCount = GetArraySize(g_hArrayVoteMapList[iVote]);
1205 | if(iMapCount > MAX_VOTE_MAPS)
1206 | iMapCount = MAX_VOTE_MAPS;
1207 |
1208 | for(int iMap = 0; iMap < iMapCount; ++iMap)
1209 | {
1210 | GetArrayString(g_hArrayVoteMapList[iVote], iMap, strMap, sizeof(strMap));
1211 | bool isMapAllowed = true;
1212 |
1213 | // Loop over the maps that must be excluded
1214 | for(int iRecentMap = iActiveMap - view_as(g_bVoteMapCurrent[iVote]);
1215 | iRecentMap >= iOldestRecentMap; --iRecentMap)
1216 | {
1217 | GetArrayString(g_hArrayRecentMaps, iRecentMap, strRecentMap, sizeof(strRecentMap));
1218 |
1219 | if(StrEqual(strMap, strRecentMap))
1220 | {
1221 | isMapAllowed = false;
1222 | break;
1223 | }
1224 | }
1225 |
1226 | if (isMapAllowed)
1227 | {
1228 | if(g_bVoteCallVote[iVote])
1229 | Format(strBuffer, sizeof(strBuffer), "%s", strMap);
1230 | else
1231 | Format(strBuffer, sizeof(strBuffer), "%s [%i/%i]", strMap, GetVotesForMap(iVote, iMap),
1232 | GetRequiredVotes(iVote));
1233 |
1234 | AddVoteMenuOption(hMenu, GetVotesForMap(iVote, iMap), strMap, strBuffer);
1235 | }
1236 | }
1237 |
1238 | for (int i=1; i<=MaxClients; i++)
1239 | {
1240 | if (IsClientInGame(i) && g_bKzTimer)
1241 | {
1242 | KZTimer_StopUpdatingOfClimbersMenu(i);
1243 | }
1244 | }
1245 |
1246 | DisplayMenu(hMenu, iVoter, 30);
1247 | }
1248 |
1249 | public int MenuHandler_MapVote(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
1250 | {
1251 | if(iAction == MenuAction_End)
1252 | {
1253 | CloseHandle(hMenu);
1254 | return 0;
1255 | }
1256 |
1257 | if(iAction == MenuAction_Cancel && iParam2 == MenuCancel_ExitBack)
1258 | {
1259 | Menu_ChooseVote(iVoter);
1260 | return 0;
1261 | }
1262 |
1263 | if(iAction == MenuAction_Select)
1264 | {
1265 | char strBuffer[MAX_NAME_LENGTH];
1266 | GetMenuItem(hMenu, iParam2, strBuffer, sizeof(strBuffer));
1267 |
1268 | int iVote = g_iVoteCurrent[iVoter];
1269 | if(iVote == -1)
1270 | return 0;
1271 |
1272 | if(IsVoteInProgress())
1273 | {
1274 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1275 | return 0;
1276 | }
1277 |
1278 | if(g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1279 | {
1280 | g_iVoteRemaining[iVoter][iVote]--;
1281 | CPrintToChat(iVoter, "%t", "Votes Remaining", g_iVoteRemaining[iVoter][iVote]);
1282 | }
1283 |
1284 | int iMap = -1;
1285 | char strMapName[MAX_NAME_LENGTH];
1286 | for(int iMapList = 0; iMapList < GetArraySize(g_hArrayVoteMapList[iVote]); iMapList++)
1287 | {
1288 | GetArrayString(g_hArrayVoteMapList[iVote], iMapList, strMapName, sizeof(strMapName));
1289 | if(StrEqual(strMapName, strBuffer))
1290 | {
1291 | iMap = iMapList;
1292 | break;
1293 | }
1294 | }
1295 |
1296 | if(iMap == -1)
1297 | {
1298 | Menu_ChooseVote(iVoter);
1299 | return 0;
1300 | }
1301 |
1302 | if(g_bVoteCallVote[iVote])
1303 | {
1304 | Vote_Map(iVote, iVoter, iMap);
1305 | return 0;
1306 | }
1307 |
1308 | if(g_bVoteForMap[iVoter][iVote][iMap])
1309 | {
1310 | CPrintToChat(iVoter, "%t", "Already Voted");
1311 | Menu_ChooseVote(iVoter);
1312 | return 0;
1313 | }
1314 |
1315 | g_bVoteForMap[iVoter][iVote][iMap] = true;
1316 | if(!g_bVoteMultiple[iVote])
1317 | {
1318 | for(int iSavedMap = 0; iSavedMap < GetArraySize(g_hArrayVoteMapList[iVote]); iSavedMap++)
1319 | {
1320 | if(iSavedMap != iMap)
1321 | g_bVoteForMap[iVoter][iVote][iSavedMap] = false;
1322 | }
1323 | }
1324 |
1325 | if(g_strVoteCallNotify[iVote][0])
1326 | {
1327 | char strNotification[255];
1328 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[iVote]);
1329 |
1330 | FormatVoteString(iVote, iMap, strNotification, sizeof(strNotification));
1331 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
1332 | FormatMapString(iVote, iMap, strNotification, sizeof(strNotification));
1333 |
1334 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1335 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1336 |
1337 | CPrintToChatAll("%s", strNotification);
1338 | }
1339 |
1340 | g_iVoteLast[iVoter][iVote] = GetTime();
1341 | g_iVoteAllLast[iVote] = GetTime();
1342 | CheckVotesForMap(iVote, iMap);
1343 | Menu_ChooseVote(iVoter);
1344 | }
1345 | return 0;
1346 | }
1347 |
1348 | public void Vote_Map(int iVote, int iVoter, int iMap)
1349 | {
1350 | if(IsVoteInProgress())
1351 | {
1352 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1353 | return;
1354 | }
1355 |
1356 | int iPlayers[MAXPLAYERS + 1];
1357 | int iTotal;
1358 |
1359 | for(int i = 1; i <= MaxClients; i++)
1360 | {
1361 | if (bDebugMode)
1362 | {
1363 | if (IsClientInGame(i))
1364 | {
1365 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Vote_Map - Player %L %s %s", i, IsFakeClient(i) ? "FAKE CLIENT" : "REAL CLIENT", IsAFKClient(i) ? "AFK" : "NOT AFK");
1366 | }
1367 | else
1368 | {
1369 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Ignoring client index %i, IsClientInGame == false!", i);
1370 | }
1371 | }
1372 |
1373 | g_bVoteForMap[i][iVote][iMap] = false;
1374 | if(IsClientInGame(i) && !IsFakeClient(i) && !IsAFKClient(i))
1375 | {
1376 | iPlayers[iTotal++] = i;
1377 | }
1378 | }
1379 |
1380 | if(g_iVoteMinimum[iVote] > iTotal || iTotal <= 0)
1381 | {
1382 | CPrintToChat(iVoter, "%t", "Not Enough Valid Clients");
1383 | return;
1384 | }
1385 |
1386 | if(g_strVoteStartNotify[iVote][0])
1387 | {
1388 | char strNotification[255];
1389 | strcopy(strNotification, sizeof(strNotification), g_strVoteStartNotify[iVote]);
1390 |
1391 | FormatVoteString(iVote, iMap, strNotification, sizeof(strNotification));
1392 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
1393 | FormatMapString(iVote, iMap, strNotification, sizeof(strNotification));
1394 |
1395 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
1396 | {
1397 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
1398 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
1399 | }
1400 | else
1401 | {
1402 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
1403 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
1404 | }
1405 |
1406 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1407 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1408 |
1409 | CPrintToChatAll("%s", strNotification);
1410 |
1411 | // get vote name
1412 | char LogstrName[56];
1413 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
1414 |
1415 | // get voter's name
1416 | char LogstrVoterName[MAX_NAME_LENGTH];
1417 | GetClientName(iVoter, LogstrVoterName, sizeof(LogstrVoterName));
1418 |
1419 | // get voter's SteamID
1420 | char LstrVoterAuth[MAX_NAME_LENGTH];
1421 | GetClientAuthId(iVoter, AuthId_Steam2, LstrVoterAuth, sizeof(LstrVoterAuth));
1422 |
1423 | // get current map name
1424 | char LogstrCurrentMap[MAX_NAME_LENGTH];
1425 | GetCurrentMap(LogstrCurrentMap, sizeof(LogstrCurrentMap));
1426 |
1427 | // get target map name
1428 | char LogstrMap[MAX_NAME_LENGTH];
1429 | GetArrayString(g_hArrayVoteMapList[iVote], iMap, LogstrMap, sizeof(LogstrMap));
1430 |
1431 | // Print vote to console ( HLSW )
1432 | PrintToServer("[Custom Votes] Vote %s started by %s ( %s ). To change map from %s to %s.",
1433 | LogstrName,
1434 | LogstrVoterName,
1435 | LstrVoterAuth,
1436 | LogstrCurrentMap,
1437 | LogstrMap);
1438 |
1439 | // Logging Vote
1440 | LogToFileEx(g_sLogPath,
1441 | "[Custom Votes] Vote %s started by %s ( %s ). To change map from %s to %s.",
1442 | LogstrName,
1443 | LogstrVoterName,
1444 | LstrVoterAuth,
1445 | LogstrCurrentMap,
1446 | LogstrMap);
1447 | }
1448 |
1449 | Handle hMenu = CreateMenu(VoteHandler_Map);
1450 |
1451 | char strMap[MAX_NAME_LENGTH];
1452 | char strBuffer[MAX_NAME_LENGTH + 12];
1453 |
1454 | GetArrayString(g_hArrayVoteMapList[iVote], iMap, strMap, sizeof(strMap));
1455 | Format(strBuffer, sizeof(strBuffer), "%s (%s)", g_strVoteName[iVote], strMap);
1456 |
1457 | SetMenuTitle(hMenu, "%s", strBuffer);
1458 | SetMenuExitButton(hMenu, false);
1459 |
1460 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1461 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1462 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1463 | if(GetRandomInt(1, 2) == 1)
1464 | {
1465 | AddMenuItem(hMenu, "Yes", "Yes");
1466 | AddMenuItem(hMenu, "No", "No");
1467 | }
1468 | else
1469 | {
1470 | AddMenuItem(hMenu, "No", "No");
1471 | AddMenuItem(hMenu, "Yes", "Yes");
1472 | }
1473 |
1474 | g_iCurrentVoteIndex = iVote;
1475 | g_iCurrentVoteMap = iMap;
1476 | VoteMenu(hMenu, iPlayers, iTotal, 30);
1477 | }
1478 |
1479 | public int VoteHandler_Map(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
1480 | {
1481 | if(iAction == MenuAction_End)
1482 | {
1483 | CloseHandle(hMenu);
1484 | return 0;
1485 | }
1486 |
1487 | if(iAction == MenuAction_Select)
1488 | {
1489 | char strInfo[16];
1490 | GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));
1491 |
1492 | if(StrEqual(strInfo, "Yes"))
1493 | {
1494 | g_bVoteForMap[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteMap] = true;
1495 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1496 | {
1497 | char strNotification[255];
1498 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1499 |
1500 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1501 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1502 | FormatMapString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1503 |
1504 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1505 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1506 |
1507 | CPrintToChatAll("%s", strNotification);
1508 | }
1509 | }
1510 | else if(StrEqual(strInfo, "No"))
1511 | {
1512 | g_bVoteForMap[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteMap] = false;
1513 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1514 | {
1515 | char strNotification[255];
1516 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1517 |
1518 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1519 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1520 | FormatMapString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1521 |
1522 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "No", true);
1523 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "no", true);
1524 |
1525 | CPrintToChatAll("%s", strNotification);
1526 | }
1527 | }
1528 | }
1529 | else if(iAction == MenuAction_VoteEnd)
1530 | {
1531 | if(!CheckVotesForMap(g_iCurrentVoteIndex, g_iCurrentVoteMap) && g_strVoteFailNotify[g_iCurrentVoteIndex][0])
1532 | {
1533 | char strNotification[255];
1534 | strcopy(strNotification, sizeof(strNotification), g_strVoteFailNotify[g_iCurrentVoteIndex]);
1535 |
1536 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1537 | FormatMapString(g_iCurrentVoteIndex, g_iCurrentVoteMap, strNotification, sizeof(strNotification));
1538 |
1539 | CPrintToChatAll("%s", strNotification);
1540 | }
1541 |
1542 | for (int i = 1; i <= MaxClients; ++i)
1543 | {
1544 | g_bVoteForMap[i][g_iCurrentVoteIndex][g_iCurrentVoteMap] = false;
1545 | }
1546 |
1547 | g_iCurrentVoteIndex = g_iCurrentVoteMap = -1;
1548 | }
1549 | return 0;
1550 | }
1551 |
1552 | public void Menu_ListVote(int iVote, int iVoter)
1553 | {
1554 | if(IsVoteInProgress())
1555 | {
1556 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1557 | return;
1558 | }
1559 |
1560 | if(g_strVoteOverride[iVote][0] && !CheckCommandAccess(iVoter, g_strVoteOverride[iVote], 0))
1561 | {
1562 | CPrintToChat(iVoter, "[SM] %t", "No Access");
1563 | return;
1564 | }
1565 |
1566 | if(g_iVoteRemaining[iVoter][iVote] <= 0 && g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1567 | {
1568 | CPrintToChat(iVoter, "%t", "No Votes Remaining");
1569 | return;
1570 | }
1571 |
1572 | if(g_iVotePasses[iVote] >= g_iVoteMaxPasses[iVote] && g_iVoteMaxPasses[iVote] > 0)
1573 | {
1574 | CPrintToChat(iVoter, "%t", "Voting No Longer Available");
1575 | return;
1576 | }
1577 |
1578 | if(g_iMapTime < g_iVoteDelay[iVote])
1579 | {
1580 | CPrintToChat(iVoter, "%t", "Vote Delay", g_iVoteDelay[iVote] - g_iMapTime);
1581 | return;
1582 | }
1583 |
1584 | int iTime = GetTime();
1585 | if(iTime - g_iVoteLast[iVoter][iVote] < g_iVoteCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1586 | {
1587 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteCooldown[iVote] - (iTime - g_iVoteLast[iVoter][iVote]));
1588 | return;
1589 | }
1590 |
1591 | if(iTime - g_iVoteAllLast[iVote] < g_iVoteAllCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1592 | {
1593 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteAllCooldown[iVote] - (iTime - g_iVoteAllLast[iVote]));
1594 | return;
1595 | }
1596 |
1597 | Handle hMenu = CreateMenu(MenuHandler_ListVote);
1598 | SetMenuTitle(hMenu, "%s:", g_strVoteName[iVote]);
1599 | SetMenuExitBackButton(hMenu, true);
1600 |
1601 | char strIndex[MAX_NAME_LENGTH];
1602 | char strBuffer[MAX_NAME_LENGTH + 12];
1603 | char strOptionName[MAX_NAME_LENGTH];
1604 | for(int iOption = 0; iOption < GetArraySize(g_hArrayVoteOptionName[iVote]); iOption++)
1605 | {
1606 | GetArrayString(g_hArrayVoteOptionName[iVote], iOption, strOptionName, sizeof(strOptionName));
1607 | if(g_bVoteCallVote[iVote])
1608 | Format(strBuffer, sizeof(strBuffer), "%s", strOptionName, GetVotesForOption(iVote, iOption), GetRequiredVotes(iVote));
1609 | else
1610 | Format(strBuffer, sizeof(strBuffer), "%s [%i/%i]", strOptionName, GetVotesForOption(iVote, iOption), GetRequiredVotes(iVote));
1611 |
1612 | IntToString(iOption, strIndex, sizeof(strIndex));
1613 | AddVoteMenuOption(hMenu, GetVotesForOption(iVote, iOption), strIndex, strBuffer);
1614 | }
1615 |
1616 | for (int i=1; i<=MaxClients; i++)
1617 | {
1618 | if (IsClientInGame(i) && g_bKzTimer)
1619 | {
1620 | KZTimer_StopUpdatingOfClimbersMenu(i);
1621 | }
1622 | }
1623 |
1624 | DisplayMenu(hMenu, iVoter, 30);
1625 | }
1626 |
1627 | public int MenuHandler_ListVote(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
1628 | {
1629 | if(iAction == MenuAction_End)
1630 | {
1631 | CloseHandle(hMenu);
1632 | return 0;
1633 | }
1634 |
1635 | if(iAction == MenuAction_Cancel && iParam2 == MenuCancel_ExitBack)
1636 | {
1637 | Menu_ChooseVote(iVoter);
1638 | return 0;
1639 | }
1640 |
1641 | if(iAction == MenuAction_Select)
1642 | {
1643 | char strBuffer[MAX_NAME_LENGTH];
1644 | GetMenuItem(hMenu, iParam2, strBuffer, sizeof(strBuffer));
1645 |
1646 | int iVote = g_iVoteCurrent[iVoter];
1647 | if(iVote == -1)
1648 | return 0;
1649 |
1650 | if(IsVoteInProgress())
1651 | {
1652 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1653 | return 0;
1654 | }
1655 |
1656 | if(g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1657 | {
1658 | g_iVoteRemaining[iVoter][iVote]--;
1659 | CPrintToChat(iVoter, "%t", "Votes Remaining", g_iVoteRemaining[iVoter][iVote]);
1660 | }
1661 |
1662 | int iOption = StringToInt(strBuffer);
1663 | if(g_bVoteCallVote[iVote])
1664 | {
1665 | Vote_List(iVote, iVoter, iOption);
1666 | return 0;
1667 | }
1668 |
1669 | if(g_bVoteForOption[iVoter][iVote][iOption])
1670 | {
1671 | CPrintToChat(iVoter, "%t", "Already Voted");
1672 | Menu_ChooseVote(iVoter);
1673 | return 0;
1674 | }
1675 |
1676 | g_bVoteForOption[iVoter][iVote][iOption] = true;
1677 | if(!g_bVoteMultiple[iVote])
1678 | {
1679 | for(int iOptionList = 0; iOptionList < GetArraySize(g_hArrayVoteOptionName[iVote]); iOptionList++)
1680 | {
1681 | if(iOptionList != iOption)
1682 | g_bVoteForOption[iVoter][iVote][iOptionList] = false;
1683 | }
1684 | }
1685 |
1686 | if(g_strVoteCallNotify[iVote][0])
1687 | {
1688 | char strNotification[255];
1689 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[iVote]);
1690 |
1691 | FormatVoteString(iVote, iOption, strNotification, sizeof(strNotification));
1692 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
1693 | FormatOptionString(iVote, iOption, strNotification, sizeof(strNotification));
1694 |
1695 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1696 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1697 |
1698 | CPrintToChatAll("%s", strNotification);
1699 | }
1700 |
1701 | g_iVoteLast[iVoter][iVote] = GetTime();
1702 | g_iVoteAllLast[iVote] = GetTime();
1703 | CheckVotesForOption(iVote, iOption);
1704 | Menu_ChooseVote(iVoter);
1705 | }
1706 | return 0;
1707 | }
1708 |
1709 | public void Vote_List(int iVote, int iVoter, int iOption)
1710 | {
1711 | if(IsVoteInProgress())
1712 | {
1713 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1714 | return;
1715 | }
1716 |
1717 | int iPlayers[MAXPLAYERS + 1];
1718 | int iTotal;
1719 |
1720 |
1721 | for(int i = 1; i <= MaxClients; i++)
1722 | {
1723 | if (bDebugMode)
1724 | {
1725 | if (IsClientInGame(i))
1726 | {
1727 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Vote_List - Player %L %s %s", i, IsFakeClient(i) ? "FAKE CLIENT" : "REAL CLIENT", IsAFKClient(i) ? "AFK" : "NOT AFK");
1728 | }
1729 | else
1730 | {
1731 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Ignoring client index %i, IsClientInGame == false!", i);
1732 | }
1733 | }
1734 |
1735 | g_bVoteForOption[i][iVote][iOption] = false;
1736 | if(IsClientInGame(i) && !IsFakeClient(i) && !IsAFKClient(i))
1737 | {
1738 | iPlayers[iTotal++] = i;
1739 | }
1740 | }
1741 |
1742 |
1743 | if(g_iVoteMinimum[iVote] > iTotal || iTotal <= 0)
1744 | {
1745 | CPrintToChat(iVoter, "%t", "Not Enough Valid Clients");
1746 | return;
1747 | }
1748 |
1749 | if(g_strVoteStartNotify[iVote][0])
1750 | {
1751 | char strNotification[255];
1752 | strcopy(strNotification, sizeof(strNotification), g_strVoteStartNotify[iVote]);
1753 |
1754 | FormatVoteString(iVote, iOption, strNotification, sizeof(strNotification));
1755 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
1756 | FormatOptionString(iVote, iOption, strNotification, sizeof(strNotification));
1757 |
1758 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
1759 | {
1760 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
1761 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
1762 | }
1763 | else
1764 | {
1765 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
1766 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
1767 | }
1768 |
1769 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1770 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1771 |
1772 | CPrintToChatAll("%s", strNotification);
1773 |
1774 | // get vote name
1775 | char LogstrName[56];
1776 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
1777 |
1778 | // get voter's name
1779 | char LogstrVoterName[MAX_NAME_LENGTH];
1780 | GetClientName(iVoter, LogstrVoterName, sizeof(LogstrVoterName));
1781 |
1782 | // get voter's SteamID
1783 | char LstrVoterAuth[MAX_NAME_LENGTH];
1784 | GetClientAuthId(iVoter, AuthId_Steam2, LstrVoterAuth, sizeof(LstrVoterAuth));
1785 |
1786 | // get vote option
1787 | char LstrOptionResult[MAX_NAME_LENGTH];
1788 | GetArrayString(g_hArrayVoteOptionResult[iVote], iOption, LstrOptionResult, sizeof(LstrOptionResult));
1789 |
1790 | // Print vote to console ( HLSW )
1791 | PrintToServer("[Custom Votes] Vote %s started by %s ( %s ). Selected Option: %s",
1792 | LogstrName,
1793 | LogstrVoterName,
1794 | LstrVoterAuth,
1795 | LstrOptionResult);
1796 |
1797 | // Logging Vote
1798 | LogToFileEx(g_sLogPath,
1799 | "[Custom Votes] Vote %s started by %s ( %s ). Selected Option: %s",
1800 | LogstrName,
1801 | LogstrVoterName,
1802 | LstrVoterAuth,
1803 | LstrOptionResult);
1804 | }
1805 |
1806 | Handle hMenu = CreateMenu(VoteHandler_List);
1807 |
1808 | char strOption[MAX_NAME_LENGTH];
1809 | char strBuffer[MAX_NAME_LENGTH + 12];
1810 |
1811 | GetArrayString(g_hArrayVoteOptionName[iVote], iOption, strOption, sizeof(strOption));
1812 | Format(strBuffer, sizeof(strBuffer), "%s (%s)", g_strVoteName[iVote], strOption);
1813 |
1814 | SetMenuTitle(hMenu, "%s", strBuffer);
1815 | SetMenuExitButton(hMenu, false);
1816 |
1817 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1818 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1819 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
1820 | if(GetRandomInt(1, 2) == 1)
1821 | {
1822 | AddMenuItem(hMenu, "Yes", "Yes");
1823 | AddMenuItem(hMenu, "No", "No");
1824 | }
1825 | else
1826 | {
1827 | AddMenuItem(hMenu, "No", "No");
1828 | AddMenuItem(hMenu, "Yes", "Yes");
1829 | }
1830 |
1831 | g_iCurrentVoteIndex = iVote;
1832 | g_iCurrentVoteOption = iOption;
1833 | VoteMenu(hMenu, iPlayers, iTotal, 30);
1834 | return;
1835 | }
1836 |
1837 | public int VoteHandler_List(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
1838 | {
1839 | if(iAction == MenuAction_End)
1840 | {
1841 | CloseHandle(hMenu);
1842 | return 0;
1843 | }
1844 |
1845 | if(iAction == MenuAction_Select)
1846 | {
1847 | char strInfo[16];
1848 | GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));
1849 |
1850 | if(StrEqual(strInfo, "Yes"))
1851 | {
1852 | g_bVoteForOption[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteOption] = true;
1853 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1854 | {
1855 | char strNotification[255];
1856 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1857 |
1858 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1859 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1860 | FormatOptionString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1861 |
1862 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1863 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1864 |
1865 | CPrintToChatAll("%s", strNotification);
1866 | }
1867 | }
1868 | else if(StrEqual(strInfo, "No"))
1869 | {
1870 | g_bVoteForOption[iVoter][g_iCurrentVoteIndex][g_iCurrentVoteOption] = false;
1871 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
1872 | {
1873 | char strNotification[255];
1874 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
1875 |
1876 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1877 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
1878 | FormatOptionString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1879 |
1880 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "No", true);
1881 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "no", true);
1882 |
1883 | CPrintToChatAll("%s", strNotification);
1884 | }
1885 | }
1886 | }
1887 | else if(iAction == MenuAction_VoteEnd)
1888 | {
1889 | if(!CheckVotesForOption(g_iCurrentVoteIndex, g_iCurrentVoteOption) && g_strVoteFailNotify[g_iCurrentVoteIndex][0])
1890 | {
1891 | char strNotification[255];
1892 | strcopy(strNotification, sizeof(strNotification), g_strVoteFailNotify[g_iCurrentVoteIndex]);
1893 |
1894 | FormatVoteString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1895 | FormatOptionString(g_iCurrentVoteIndex, g_iCurrentVoteOption, strNotification, sizeof(strNotification));
1896 |
1897 | CPrintToChatAll("%s", strNotification);
1898 | }
1899 |
1900 | for (int i = 1; i <= MaxClients; ++i)
1901 | {
1902 | g_bVoteForOption[i][g_iCurrentVoteIndex][g_iCurrentVoteOption] = false;
1903 | }
1904 |
1905 | g_iCurrentVoteIndex = g_iCurrentVoteOption = -1;
1906 | }
1907 | return 0;
1908 | }
1909 |
1910 | public void CastSimpleVote(int iVote, int iVoter)
1911 | {
1912 | if(IsVoteInProgress())
1913 | {
1914 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
1915 | return;
1916 | }
1917 |
1918 | if(g_strVoteOverride[iVote][0] && !CheckCommandAccess(iVoter, g_strVoteOverride[iVote], 0))
1919 | {
1920 | CPrintToChat(iVoter, "[SM] %t", "No Access");
1921 | return;
1922 | }
1923 |
1924 | if(g_iVoteRemaining[iVoter][iVote] <= 0 && g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1925 | {
1926 | CPrintToChat(iVoter, "%t", "No Votes Remaining");
1927 | return;
1928 | }
1929 |
1930 | if(g_iVotePasses[iVote] >= g_iVoteMaxPasses[iVote] && g_iVoteMaxPasses[iVote] > 0)
1931 | {
1932 | CPrintToChat(iVoter, "%t", "Voting No Longer Available");
1933 | return;
1934 | }
1935 |
1936 | if(g_iMapTime < g_iVoteDelay[iVote])
1937 | {
1938 | CPrintToChat(iVoter, "%t", "Vote Delay", g_iVoteDelay[iVote] - g_iMapTime);
1939 | return;
1940 | }
1941 |
1942 | int iTime = GetTime();
1943 | if(iTime - g_iVoteLast[iVoter][iVote] < g_iVoteCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1944 | {
1945 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteCooldown[iVote] - (iTime - g_iVoteLast[iVoter][iVote]));
1946 | return;
1947 | }
1948 |
1949 | if(iTime - g_iVoteAllLast[iVote] < g_iVoteAllCooldown[iVote] && !CheckCommandAccess(iVoter, "customvotes_cooldown", ADMFLAG_GENERIC))
1950 | {
1951 | CPrintToChat(iVoter, "%t", "Vote Cooldown", g_iVoteAllCooldown[iVote] - (iTime - g_iVoteAllLast[iVote]));
1952 | return;
1953 | }
1954 |
1955 | if(g_iVoteMaxCalls[iVote] > 0 && !CheckCommandAccess(iVoter, "customvotes_maxvotes", ADMFLAG_GENERIC))
1956 | {
1957 | g_iVoteRemaining[iVoter][iVote]--;
1958 | CPrintToChat(iVoter, "%t", "Votes Remaining", g_iVoteRemaining[iVoter][iVote]);
1959 | }
1960 |
1961 | g_iVoteLast[iVoter][iVote] = iTime;
1962 | g_iVoteAllLast[iVote] = iTime;
1963 | if(g_bVoteCallVote[iVote])
1964 | {
1965 | Vote_Simple(iVote, iVoter);
1966 | return;
1967 | }
1968 |
1969 | g_bVoteForSimple[iVoter][iVote] = true;
1970 | if(g_strVoteCallNotify[iVote][0])
1971 | {
1972 | char strNotification[255];
1973 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[iVote]);
1974 |
1975 | FormatVoteString(iVote, _, strNotification, sizeof(strNotification));
1976 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
1977 |
1978 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
1979 | {
1980 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
1981 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
1982 | }
1983 | else
1984 | {
1985 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
1986 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
1987 | }
1988 |
1989 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
1990 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
1991 |
1992 | CPrintToChatAll("%s", strNotification);
1993 | }
1994 |
1995 | CheckVotesForSimple(iVote);
1996 | Menu_ChooseVote(iVoter);
1997 | return;
1998 | }
1999 |
2000 | public void Vote_Simple(int iVote, int iVoter)
2001 | {
2002 | if(IsVoteInProgress())
2003 | {
2004 | CPrintToChat(iVoter, "[SM] %t", "Vote in Progress");
2005 | return;
2006 | }
2007 |
2008 | int iPlayers[MAXPLAYERS + 1];
2009 | int iTotal;
2010 |
2011 |
2012 | for(int i = 1; i <= MaxClients; i++)
2013 | {
2014 | if (bDebugMode)
2015 | {
2016 | if (IsClientInGame(i))
2017 | {
2018 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Vote_Simple - Player %L %s %s", i, IsFakeClient(i) ? "FAKE CLIENT" : "REAL CLIENT", IsAFKClient(i) ? "AFK" : "NOT AFK");
2019 | }
2020 | else
2021 | {
2022 | LogToFileEx(g_sLogPath, "[Custom Votes] DEBUG: Ignoring client index %i, IsClientInGame == false!", i);
2023 | }
2024 | }
2025 |
2026 | g_bVoteForSimple[i][iVote] = false;
2027 | if(IsClientInGame(i) && !IsFakeClient(i) && !IsAFKClient(i))
2028 | {
2029 | iPlayers[iTotal++] = i;
2030 | }
2031 | }
2032 |
2033 | if(g_iVoteMinimum[iVote] > iTotal || iTotal <= 0)
2034 | {
2035 | CPrintToChat(iVoter, "%t", "Not Enough Valid Clients");
2036 | return;
2037 | }
2038 |
2039 | if(g_strVoteStartNotify[iVote][0])
2040 | {
2041 | char strNotification[255];
2042 | strcopy(strNotification, sizeof(strNotification), g_strVoteStartNotify[iVote]);
2043 |
2044 | FormatVoteString(iVote, _, strNotification, sizeof(strNotification));
2045 | FormatVoterString(iVote, iVoter, strNotification, sizeof(strNotification));
2046 |
2047 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
2048 | {
2049 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
2050 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
2051 | }
2052 | else
2053 | {
2054 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
2055 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
2056 | }
2057 |
2058 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
2059 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
2060 |
2061 | CPrintToChatAll("%s", strNotification);
2062 |
2063 | // get vote name
2064 | char LogstrName[56];
2065 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
2066 |
2067 | // get voter's name
2068 | char LogstrVoterName[MAX_NAME_LENGTH];
2069 | GetClientName(iVoter, LogstrVoterName, sizeof(LogstrVoterName));
2070 |
2071 | // get voter's SteamID
2072 | char LstrVoterAuth[MAX_NAME_LENGTH];
2073 | GetClientAuthId(iVoter, AuthId_Steam2, LstrVoterAuth, sizeof(LstrVoterAuth));
2074 |
2075 | // Print vote to console ( HLSW )
2076 | PrintToServer("[Custom Votes] Vote %s started by %s ( %s ).",
2077 | LogstrName,
2078 | LogstrVoterName,
2079 | LstrVoterAuth);
2080 |
2081 | // Logging Vote
2082 | LogToFileEx(g_sLogPath,
2083 | "[Custom Votes] Vote %s started by %s ( %s ).",
2084 | LogstrName,
2085 | LogstrVoterName,
2086 | LstrVoterAuth);
2087 | }
2088 |
2089 | Handle hMenu = CreateMenu(VoteHandler_Simple);
2090 |
2091 | char strName[56];
2092 | strcopy(strName, sizeof(strName), g_strVoteName[iVote]);
2093 |
2094 | if(g_iVoteType[iVote] == VoteType_Simple)
2095 | {
2096 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
2097 | {
2098 | ReplaceString(strName, sizeof(strName), "{On|Off}", "Off", true);
2099 | ReplaceString(strName, sizeof(strName), "{on|off}", "off", true);
2100 | }
2101 | else
2102 | {
2103 | ReplaceString(strName, sizeof(strName), "{On|Off}", "On", true);
2104 | ReplaceString(strName, sizeof(strName), "{on|off}", "on", true);
2105 | }
2106 | }
2107 |
2108 | SetMenuTitle(hMenu, "%s", strName);
2109 | SetMenuExitButton(hMenu, false);
2110 |
2111 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
2112 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
2113 | AddMenuItem(hMenu, "", " ", ITEMDRAW_NOTEXT);
2114 | if(GetRandomInt(1, 2) == 1)
2115 | {
2116 | AddMenuItem(hMenu, "Yes", "Yes");
2117 | AddMenuItem(hMenu, "No", "No");
2118 | }
2119 | else
2120 | {
2121 | AddMenuItem(hMenu, "No", "No");
2122 | AddMenuItem(hMenu, "Yes", "Yes");
2123 | }
2124 |
2125 | g_iCurrentVoteIndex = iVote;
2126 | VoteMenu(hMenu, iPlayers, iTotal, 30);
2127 | return;
2128 | }
2129 |
2130 | public int VoteHandler_Simple(Handle hMenu, MenuAction iAction, int iVoter, int iParam2)
2131 | {
2132 | if(iAction == MenuAction_End)
2133 | {
2134 | CloseHandle(hMenu);
2135 | return 0;
2136 | }
2137 |
2138 | if(iAction == MenuAction_Select)
2139 | {
2140 | char strInfo[16];
2141 | GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));
2142 |
2143 | if(StrEqual(strInfo, "Yes"))
2144 | {
2145 | g_bVoteForSimple[iVoter][g_iCurrentVoteIndex] = true;
2146 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
2147 | {
2148 | char strNotification[255];
2149 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
2150 |
2151 | FormatVoteString(g_iCurrentVoteIndex, _, strNotification, sizeof(strNotification));
2152 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
2153 |
2154 | // prevents GetConVarBool if we don't have a convar
2155 | if(!StrEqual(g_strVoteConVar[g_iCurrentVoteIndex],""))
2156 | {
2157 |
2158 | if(GetConVarBool(FindConVar(g_strVoteConVar[g_iCurrentVoteIndex])))
2159 | {
2160 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
2161 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
2162 | }
2163 | else
2164 | {
2165 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
2166 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
2167 | }
2168 |
2169 | // Added missing PrintToChat call
2170 | CPrintToChatAll("%s", strNotification);
2171 | }
2172 | else
2173 | {
2174 |
2175 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "Yes", true);
2176 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "yes", true);
2177 |
2178 | CPrintToChatAll("%s", strNotification);
2179 |
2180 | }
2181 | }
2182 | }
2183 | else if(StrEqual(strInfo, "No"))
2184 | {
2185 | g_bVoteForSimple[iVoter][g_iCurrentVoteIndex] = false;
2186 | if(g_strVoteCallNotify[g_iCurrentVoteIndex][0])
2187 | {
2188 | char strNotification[255];
2189 | strcopy(strNotification, sizeof(strNotification), g_strVoteCallNotify[g_iCurrentVoteIndex]);
2190 |
2191 | FormatVoteString(g_iCurrentVoteIndex, _, strNotification, sizeof(strNotification));
2192 | FormatVoterString(g_iCurrentVoteIndex, iVoter, strNotification, sizeof(strNotification));
2193 |
2194 | if(!StrEqual(g_strVoteConVar[g_iCurrentVoteIndex],""))
2195 | {
2196 |
2197 | if(GetConVarBool(FindConVar(g_strVoteConVar[g_iCurrentVoteIndex])))
2198 | {
2199 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
2200 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
2201 | }
2202 | else
2203 | {
2204 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
2205 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
2206 | }
2207 |
2208 | // Added missing PrintToChat call
2209 | CPrintToChatAll("%s", strNotification);
2210 | }
2211 | else
2212 | {
2213 |
2214 | ReplaceString(strNotification, sizeof(strNotification), "{Yes|No}", "No", true);
2215 | ReplaceString(strNotification, sizeof(strNotification), "{yes|no}", "no", true);
2216 |
2217 | CPrintToChatAll("%s", strNotification);
2218 |
2219 | }
2220 | }
2221 | }
2222 | }
2223 | else if(iAction == MenuAction_VoteEnd)
2224 | {
2225 | if(!CheckVotesForSimple(g_iCurrentVoteIndex) && g_strVoteFailNotify[g_iCurrentVoteIndex][0])
2226 | {
2227 | char strNotification[255];
2228 | strcopy(strNotification, sizeof(strNotification), g_strVoteFailNotify[g_iCurrentVoteIndex]);
2229 |
2230 | FormatVoteString(g_iCurrentVoteIndex, _, strNotification, sizeof(strNotification));
2231 |
2232 | CPrintToChatAll("%s", strNotification);
2233 | }
2234 |
2235 | for (int i = 1; i <= MaxClients; ++i)
2236 | {
2237 | g_bVoteForSimple[i][g_iCurrentVoteIndex] = false;
2238 | }
2239 |
2240 | g_iCurrentVoteIndex = -1;
2241 | }
2242 | return 0;
2243 | }
2244 |
2245 | // Reset vote at wave failure.
2246 | public Action TF_WaveFailed(Handle event, const char[] name, bool dontBroadcast)
2247 | {
2248 | if(bResetOnWaveFailed)
2249 | {
2250 | for(int iVote = 0; iVote < MAX_VOTE_TYPES; iVote++)
2251 | {
2252 | g_iVotePasses[iVote] = 0;
2253 | }
2254 | }
2255 |
2256 | if(bDebugMode) // If debug is enabled, log events
2257 | {
2258 | LogToFileEx(g_sLogPath,
2259 | "[Custom Votes] DEBUG: Event TF_WaveFailed.");
2260 | }
2261 |
2262 | return Plugin_Continue;
2263 | }
2264 | // Cancel votes on Game Over (TF2)
2265 | public Action TF_EventsEnded(Handle event, const char[] name, bool dontBroadcast)
2266 | {
2267 | if(bCancelVoteGameEnd)
2268 | {
2269 | //g_bMapEnded = true;
2270 | if(IsVoteInProgress()) // is vote in progress?
2271 | {
2272 | CancelVote(); // cancel any running votes on map end.
2273 | LogToFileEx(g_sLogPath,
2274 | "[Custom Votes] Map end while a vote was in progress, canceling vote.");
2275 | }
2276 | }
2277 |
2278 | if(bDebugMode) // If debug is enabled, log events
2279 | {
2280 | LogToFileEx(g_sLogPath,
2281 | "[Custom Votes] DEBUG: Event %s. bCancelVoteGameEnd: %d IsVoteInProgress: %d", name, bCancelVoteGameEnd, IsVoteInProgress());
2282 | }
2283 |
2284 | return Plugin_Continue;
2285 | }
2286 | // Cancel votes on round end (CSGO)
2287 | public Action CSGO_MapEnd(Handle event, const char[] name, bool dontBroadcast)
2288 | {
2289 | if(bCancelVoteGameEnd)
2290 | {
2291 | //g_bMapEnded = true;
2292 | if(IsVoteInProgress()) // is vote in progress?
2293 | {
2294 | CancelVote(); // cancel any running votes on map end.
2295 | LogToFileEx(g_sLogPath,
2296 | "[Custom Votes] Map end while a vote was in progress, canceling vote.");
2297 | }
2298 | }
2299 |
2300 | if(bDebugMode) // If debug is enabled, log events
2301 | {
2302 | LogToFileEx(g_sLogPath,
2303 | "[Custom Votes] DEBUG: Event CSGO_MapEnd. bCancelVoteGameEnd: %d IsVoteInProgress: %d", bCancelVoteGameEnd, IsVoteInProgress());
2304 | }
2305 |
2306 | return Plugin_Continue;
2307 | }
2308 |
2309 | public Action OnLogAction(Handle source, Identity ident, int client, int target, const char[] message)
2310 | {
2311 | if((StrContains(message, "changed map to") != -1) && bCancelVoteGameEnd && IsVoteInProgress())
2312 | {
2313 | //g_bMapEnded = true;
2314 | CancelVote(); // cancel any running votes on map end.
2315 | LogToFileEx(g_sLogPath,
2316 | "[Custom Votes] Map manually changed while a vote was in progress, canceling vote.");
2317 | }
2318 |
2319 | return Plugin_Continue;
2320 | }
2321 |
2322 | // ====[ FUNCTIONS ]===========================================================
2323 | public void Config_Load()
2324 | {
2325 | if(!FileExists(g_strConfigFile))
2326 | {
2327 | SetFailState("Configuration file %s not found!", g_strConfigFile);
2328 | return;
2329 | }
2330 |
2331 | Handle hKeyValues = CreateKeyValues("Custom Votes");
2332 | if(!FileToKeyValues(hKeyValues, g_strConfigFile) || !KvGotoFirstSubKey(hKeyValues))
2333 | {
2334 | SetFailState("Improper structure for configuration file %s!", g_strConfigFile);
2335 | return;
2336 | }
2337 |
2338 | g_iVoteCount = 0;
2339 | g_iCurrentVoteIndex = -1;
2340 | g_iCurrentVoteTarget = -1;
2341 | g_iCurrentVoteMap = -1;
2342 | g_iCurrentVoteOption = -1;
2343 |
2344 | strcopy(g_strVoteTargetIndex, sizeof(g_strVoteTargetIndex), "");
2345 | strcopy(g_strVoteTargetId, sizeof(g_strVoteTargetId), "");
2346 | strcopy(g_strVoteTargetAuth, sizeof(g_strVoteTargetAuth), "");
2347 | strcopy(g_strVoteTargetName, sizeof(g_strVoteTargetName), "");
2348 |
2349 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2350 | g_iVoteCurrent[iVoter] = -1;
2351 |
2352 | for(int iVote = 0; iVote < MAX_VOTE_TYPES; iVote++)
2353 | {
2354 | g_iVoteDelay[iVote] = 0;
2355 | g_iVoteMinimum[iVote] = 0;
2356 | g_iVoteImmunity[iVote] = 0;
2357 | g_iVoteMaxCalls[iVote] = 0;
2358 | g_iVotePasses[iVote] = 0;
2359 | g_iVoteMaxPasses[iVote] = 0;
2360 | g_iVoteMapRecent[iVote] = 0;
2361 | g_bVoteCallVote[iVote] = false;
2362 | g_bVotePlayersBots[iVote] = false;
2363 | g_bVotePlayersTeam[iVote] = false;
2364 | g_bVoteMapCurrent[iVote] = false;
2365 | g_bVoteMultiple[iVote] = false;
2366 | g_flVoteRatio[iVote] = 0.0;
2367 | strcopy(g_strVoteName[iVote], sizeof(g_strVoteName[]), "");
2368 | strcopy(g_strVoteConVar[iVote], sizeof(g_strVoteConVar[]), "");
2369 | strcopy(g_strVoteOverride[iVote], sizeof(g_strVoteOverride[]), "");
2370 | strcopy(g_strVoteCommand[iVote], sizeof(g_strVoteCommand[]), "");
2371 | strcopy(g_strVoteChatTrigger[iVote], sizeof(g_strVoteChatTrigger[]), "");
2372 | strcopy(g_strVoteStartNotify[iVote], sizeof(g_strVoteStartNotify[]), "");
2373 | strcopy(g_strVoteCallNotify[iVote], sizeof(g_strVoteCallNotify[]), "");
2374 | strcopy(g_strVotePassNotify[iVote], sizeof(g_strVotePassNotify[]), "");
2375 | strcopy(g_strVoteFailNotify[iVote], sizeof(g_strVoteFailNotify[]), "");
2376 |
2377 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2378 | {
2379 | g_iVoteRemaining[iVoter][iVote] = 0;
2380 | g_iVoteLast[iVoter][iVote] = 0;
2381 | g_iVoteAllLast[iVote] = 0;
2382 | for(int iTarget = 1; iTarget <= MaxClients; iTarget++)
2383 | {
2384 | g_bVoteForTarget[iTarget][iVote][iVoter] = false;
2385 | g_bVoteForTarget[iVoter][iVote][iTarget] = false;
2386 | }
2387 |
2388 | for(int iMap = 0; iMap < MAX_VOTE_MAPS; iMap++)
2389 | g_bVoteForMap[iVoter][iVote][iMap] = false;
2390 |
2391 | for(int iOption = 0; iOption < MAX_VOTE_OPTIONS; iOption++)
2392 | g_bVoteForOption[iVoter][iVote][iOption] = false;
2393 |
2394 | g_bVoteForSimple[iVoter][iVote] = false;
2395 |
2396 | if(g_hArrayVotePlayerSteamID[iVoter][iVote] != INVALID_HANDLE)
2397 | {
2398 | CloseHandle(g_hArrayVotePlayerSteamID[iVoter][iVote]);
2399 | g_hArrayVotePlayerSteamID[iVoter][iVote] = INVALID_HANDLE;
2400 | }
2401 |
2402 | if(g_hArrayVotePlayerIP[iVoter][iVote] != INVALID_HANDLE)
2403 | {
2404 | CloseHandle(g_hArrayVotePlayerIP[iVoter][iVote]);
2405 | g_hArrayVotePlayerIP[iVoter][iVote] = INVALID_HANDLE;
2406 | }
2407 | }
2408 |
2409 | if(g_hArrayVoteOptionName[iVote] != INVALID_HANDLE)
2410 | {
2411 | CloseHandle(g_hArrayVoteOptionName[iVote]);
2412 | g_hArrayVoteOptionName[iVote] = INVALID_HANDLE;
2413 | }
2414 |
2415 | if(g_hArrayVoteOptionResult[iVote] != INVALID_HANDLE)
2416 | {
2417 | CloseHandle(g_hArrayVoteOptionResult[iVote]);
2418 | g_hArrayVoteOptionResult[iVote] = INVALID_HANDLE;
2419 | }
2420 |
2421 | if(g_hArrayVoteMapList[iVote] != INVALID_HANDLE)
2422 | {
2423 | CloseHandle(g_hArrayVoteMapList[iVote]);
2424 | g_hArrayVoteMapList[iVote] = INVALID_HANDLE;
2425 | }
2426 | }
2427 |
2428 | int iVote;
2429 | do
2430 | {
2431 | // Name of vote
2432 | KvGetSectionName(hKeyValues, g_strVoteName[iVote], sizeof(g_strVoteName[]));
2433 |
2434 | // Type of vote (Valid types: players, map, list)
2435 | char strType[24];
2436 | KvGetString(hKeyValues, "type", strType, sizeof(strType));
2437 |
2438 | if(StrEqual(strType, "players"))
2439 | g_iVoteType[iVote] = VoteType_Players;
2440 | else if(StrEqual(strType, "map"))
2441 | g_iVoteType[iVote] = VoteType_Map;
2442 | else if(StrEqual(strType, "list"))
2443 | g_iVoteType[iVote] = VoteType_List;
2444 | else if(StrEqual(strType, "simple"))
2445 | g_iVoteType[iVote] = VoteType_Simple;
2446 | else
2447 | {
2448 | LogError("Invalid vote type for vote %s", g_strVoteName[iVote]);
2449 | continue;
2450 | }
2451 |
2452 | // Determine if a vote is called to determine the result of the selection, or if each selection is chosen manually by the players
2453 | g_bVoteCallVote[iVote] = view_as(KvGetNum(hKeyValues, "vote"));
2454 |
2455 | // Delay in seconds before players vote after the map has changed
2456 | g_iVoteDelay[iVote] = KvGetNum(hKeyValues, "delay");
2457 |
2458 | // Delay in seconds before players can vote again after casting a selection
2459 | g_iVoteCooldown[iVote] = KvGetNum(hKeyValues, "cooldown");
2460 |
2461 | // Delay in seconds before all players can vote again after casting a selection
2462 | g_iVoteAllCooldown[iVote] = KvGetNum(hKeyValues, "cooldownall");
2463 |
2464 | // Minimum votes required for the vote to pass (Overrides ratio)
2465 | g_iVoteMinimum[iVote] = KvGetNum(hKeyValues, "minimum");
2466 |
2467 | // Admins with equal or higher immunity are removed from the vote
2468 | g_iVoteImmunity[iVote] = KvGetNum(hKeyValues, "immunity");
2469 |
2470 | // Maximum times a player can vote
2471 | g_iVoteMaxCalls[iVote] = KvGetNum(hKeyValues, "maxcalls");
2472 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2473 | g_iVoteRemaining[iVoter][iVote] = g_iVoteMaxCalls[iVote];
2474 |
2475 | // Maximum times a player can cast a selection
2476 | g_iVoteMaxPasses[iVote] = KvGetNum(hKeyValues, "maxpasses");
2477 |
2478 | // Allow/disallow players from casting a selection on more than one option
2479 | g_bVoteMultiple[iVote] = view_as(KvGetNum(hKeyValues, "multiple"));
2480 |
2481 | // Ratio of players required to cast a selection for the vote to pass
2482 | g_flVoteRatio[iVote] = KvGetFloat(hKeyValues, "ratio");
2483 |
2484 | // Control variable being changed
2485 | KvGetString(hKeyValues, "cvar", g_strVoteConVar[iVote], sizeof(g_strVoteConVar[]));
2486 |
2487 | // Admin override (Use this with admin_overrides.cfg to prohibit access from specific players)
2488 | KvGetString(hKeyValues, "override", g_strVoteOverride[iVote], sizeof(g_strVoteOverride[]));
2489 |
2490 | // Command(s) ran when a vote is passed
2491 | KvGetString(hKeyValues, "command", g_strVoteCommand[iVote], sizeof(g_strVoteCommand[]));
2492 |
2493 | // Chat trigger to open the vote selections (Do not include ! or / in the trigger)
2494 | KvGetString(hKeyValues, "chattrigger", g_strVoteChatTrigger[iVote], sizeof(g_strVoteChatTrigger[]));
2495 |
2496 | // Printed to everyone's chat when a player starts a vote
2497 | KvGetString(hKeyValues, "start_notify", g_strVoteStartNotify[iVote], sizeof(g_strVoteStartNotify[]));
2498 |
2499 | // Printed to everyone's chat when a player casts a selection
2500 | KvGetString(hKeyValues, "call_notify", g_strVoteCallNotify[iVote], sizeof(g_strVoteCallNotify[]));
2501 |
2502 | // Printed to everyone's chat when the vote passes
2503 | KvGetString(hKeyValues, "pass_notify", g_strVotePassNotify[iVote], sizeof(g_strVotePassNotify[]));
2504 |
2505 | // Printed to everyone's chat when the vote fails to pass
2506 | KvGetString(hKeyValues, "fail_notify", g_strVoteFailNotify[iVote], sizeof(g_strVoteFailNotify[]));
2507 |
2508 | switch(g_iVoteType[iVote])
2509 | {
2510 | case VoteType_Players:
2511 | {
2512 | // Allows/disallows casting selections on bots
2513 | g_bVotePlayersBots[iVote] = view_as(KvGetNum(hKeyValues, "bots"));
2514 |
2515 | // Restricts players to only casting selections on team members
2516 | g_bVotePlayersTeam[iVote] = view_as(KvGetNum(hKeyValues, "team"));
2517 |
2518 | for(int iTarget = 1; iTarget <= MaxClients; iTarget++)
2519 | {
2520 | g_hArrayVotePlayerSteamID[iTarget][iVote] = CreateArray(MAX_NAME_LENGTH);
2521 | g_hArrayVotePlayerIP[iTarget][iVote] = CreateArray(MAX_NAME_LENGTH);
2522 | }
2523 | }
2524 | case VoteType_Map:
2525 | {
2526 | // How many recent maps will be removed from the vote selections
2527 | g_iVoteMapRecent[iVote] = KvGetNum(hKeyValues, "recentmaps");
2528 |
2529 | // Allows/disallows casting selections on the current map
2530 | g_bVoteMapCurrent[iVote] = view_as(KvGetNum(hKeyValues, "currentmap"));
2531 |
2532 | // List of maps to populate the selection list
2533 | char strMapList[24];
2534 | KvGetString(hKeyValues, "maplist", strMapList, sizeof(strMapList), "default");
2535 |
2536 | g_hArrayVoteMapList[iVote] = CreateArray(MAX_NAME_LENGTH);
2537 | ReadMapList(g_hArrayVoteMapList[iVote], _, strMapList, MAPLIST_FLAG_CLEARARRAY);
2538 | }
2539 | case VoteType_List:
2540 | {
2541 | if(!KvJumpToKey(hKeyValues, "Options"))
2542 | continue;
2543 |
2544 | if(KvGotoFirstSubKey(hKeyValues, false))
2545 | {
2546 | g_hArrayVoteOptionName[iVote] = CreateArray(16);
2547 | g_hArrayVoteOptionResult[iVote] = CreateArray(16);
2548 |
2549 | do
2550 | {
2551 | // Vote option name
2552 | char strOptionName[MAX_NAME_LENGTH];
2553 | KvGetSectionName(hKeyValues, strOptionName, sizeof(strOptionName));
2554 | PushArrayString(g_hArrayVoteOptionName[iVote], strOptionName);
2555 |
2556 | // Vote option result
2557 | char strOptionResult[MAX_NAME_LENGTH];
2558 | KvGetString(hKeyValues, NULL_STRING, strOptionResult, sizeof(strOptionResult));
2559 | PushArrayString(g_hArrayVoteOptionResult[iVote], strOptionResult);
2560 | }
2561 | while(KvGotoNextKey(hKeyValues, false));
2562 |
2563 | KvGoBack(hKeyValues);
2564 | }
2565 |
2566 | KvGoBack(hKeyValues);
2567 | }
2568 | }
2569 |
2570 | ++iVote;
2571 | }
2572 | while(KvGotoNextKey(hKeyValues));
2573 |
2574 | CloseHandle(hKeyValues);
2575 |
2576 | g_iVoteCount = iVote;
2577 | LogMessage("Configuration file %s loaded.", g_strConfigFile);
2578 | }
2579 |
2580 | public bool CheckVotesForTarget(int iVote, int iTarget)
2581 | {
2582 |
2583 | int iVotes = GetVotesForTarget(iVote, iTarget);
2584 | int iRequired = GetRequiredVotes(iVote);
2585 |
2586 | /* if(g_bMapEnded) // if the map ended, always return false
2587 | {
2588 | return false;
2589 | } */
2590 |
2591 | if(iVotes >= iRequired)
2592 | {
2593 | g_iVotePasses[iVote]++;
2594 |
2595 | if(g_strVoteCommand[iVote][0])
2596 | {
2597 | char strCommand[255];
2598 | strcopy(strCommand, sizeof(strCommand), g_strVoteCommand[iVote]);
2599 |
2600 | FormatTargetString(iVote, iTarget, strCommand, sizeof(strCommand));
2601 | ServerCommand(strCommand);
2602 | }
2603 |
2604 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2605 | g_bVoteForTarget[iVoter][iVote][iTarget] = false;
2606 |
2607 | if(g_strVotePassNotify[iVote][0])
2608 | {
2609 | char strNotification[255];
2610 | strcopy(strNotification, sizeof(strNotification), g_strVotePassNotify[iVote]);
2611 |
2612 | FormatTargetString(iVote, iTarget, strNotification, sizeof(strNotification));
2613 | CPrintToChatAll("%s", strNotification);
2614 |
2615 | // get vote name
2616 | char LogstrName[56];
2617 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
2618 |
2619 | // get target's name
2620 | char LogstrTargetName[MAX_NAME_LENGTH];
2621 | GetClientName(iTarget, LogstrTargetName, sizeof(LogstrTargetName));
2622 |
2623 | // get target's SteamID
2624 | char LstrTargetAuth[MAX_NAME_LENGTH];
2625 | GetClientAuthId(iTarget, AuthId_Steam2, LstrTargetAuth, sizeof(LstrTargetAuth));
2626 |
2627 | // Logging Vote
2628 | LogToFileEx(g_sLogPath,
2629 | "[Custom Votes] Last vote ( %s ) passed. The Target was: %s ( %s ).",
2630 | LogstrName,
2631 | LogstrTargetName,
2632 | LstrTargetAuth);
2633 | }
2634 | return true;
2635 | }
2636 | return false;
2637 | }
2638 |
2639 | public bool CheckVotesForMap(int iVote, int iMap)
2640 | {
2641 | int iVotes = GetVotesForMap(iVote, iMap);
2642 | int iRequired = GetRequiredVotes(iVote);
2643 |
2644 | /* if(g_bMapEnded) // if the map ended, always return false
2645 | {
2646 | return false;
2647 | } */
2648 |
2649 | if(iVotes >= iRequired)
2650 | {
2651 | g_iVotePasses[iVote]++;
2652 |
2653 | if(g_strVoteCommand[iVote][0])
2654 | {
2655 | char strCommand[255];
2656 | strcopy(strCommand, sizeof(strCommand), g_strVoteCommand[iVote]);
2657 |
2658 | FormatMapString(iVote, iMap, strCommand, sizeof(strCommand));
2659 | ServerCommand(strCommand);
2660 | }
2661 |
2662 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2663 | g_bVoteForMap[iVoter][iVote][iMap] = false;
2664 |
2665 | if(g_strVotePassNotify[iVote][0])
2666 | {
2667 | char strNotification[255];
2668 | strcopy(strNotification, sizeof(strNotification), g_strVotePassNotify[iVote]);
2669 |
2670 | FormatMapString(iVote, iMap, strNotification, sizeof(strNotification));
2671 | CPrintToChatAll("%s", strNotification);
2672 |
2673 | // get vote name
2674 | char LogstrName[56];
2675 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
2676 |
2677 | // get current map name
2678 | char LogstrCurrentMap[MAX_NAME_LENGTH];
2679 | GetCurrentMap(LogstrCurrentMap, sizeof(LogstrCurrentMap));
2680 |
2681 | // get target map name
2682 | char LogstrMap[MAX_NAME_LENGTH];
2683 | GetArrayString(g_hArrayVoteMapList[iVote], iMap, LogstrMap, sizeof(LogstrMap));
2684 |
2685 | // Logging Vote
2686 | LogToFileEx(g_sLogPath,
2687 | "[Custom Votes] Last vote ( %s ) passed. Map was changed from %s to %s.",
2688 | LogstrName,
2689 | LogstrCurrentMap,
2690 | LogstrMap);
2691 | }
2692 | return true;
2693 | }
2694 | return false;
2695 | }
2696 |
2697 | public bool CheckVotesForOption(int iVote, int iOption)
2698 | {
2699 | int iVotes = GetVotesForOption(iVote, iOption);
2700 | int iRequired = GetRequiredVotes(iVote);
2701 |
2702 | /* if(g_bMapEnded) // if the map ended, always return false
2703 | {
2704 | return false;
2705 | } */
2706 |
2707 | if(iVotes >= iRequired)
2708 | {
2709 | g_iVotePasses[iVote]++;
2710 |
2711 | if(g_strVoteCommand[iVote][0])
2712 | {
2713 | char strCommand[255];
2714 | strcopy(strCommand, sizeof(strCommand), g_strVoteCommand[iVote]);
2715 |
2716 | FormatOptionString(iVote, iOption, strCommand, sizeof(strCommand));
2717 | ServerCommand(strCommand);
2718 | }
2719 |
2720 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2721 | g_bVoteForOption[iVoter][iVote][iOption] = false;
2722 |
2723 | if(g_strVotePassNotify[iVote][0])
2724 | {
2725 | char strNotification[255];
2726 | strcopy(strNotification, sizeof(strNotification), g_strVotePassNotify[iVote]);
2727 |
2728 | FormatOptionString(iVote, iOption, strNotification, sizeof(strNotification));
2729 | CPrintToChatAll("%s", strNotification);
2730 |
2731 | // get vote name
2732 | char LogstrName[56];
2733 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
2734 |
2735 | // get vote option
2736 | char LstrOptionResult[MAX_NAME_LENGTH];
2737 | GetArrayString(g_hArrayVoteOptionResult[iVote], iOption, LstrOptionResult, sizeof(LstrOptionResult));
2738 |
2739 | // Logging Vote
2740 | LogToFileEx(g_sLogPath,
2741 | "[Custom Votes] Last vote ( %s ) passed. ( Option: %s ).",
2742 | LogstrName,
2743 | LstrOptionResult);
2744 | }
2745 | return true;
2746 | }
2747 | return false;
2748 | }
2749 |
2750 | public bool CheckVotesForSimple(int iVote)
2751 | {
2752 | int iVotes = GetVotesForSimple(iVote);
2753 | int iRequired = GetRequiredVotes(iVote);
2754 |
2755 | /* if(g_bMapEnded) // if the map ended, always return false
2756 | {
2757 | return false;
2758 | } */
2759 |
2760 | if(iVotes >= iRequired)
2761 | {
2762 | g_iVotePasses[iVote]++;
2763 |
2764 | if(g_strVoteCommand[iVote][0])
2765 | {
2766 | char strCommand[255];
2767 | strcopy(strCommand, sizeof(strCommand), g_strVoteCommand[iVote]);
2768 |
2769 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
2770 | ReplaceString(strCommand, sizeof(strCommand), "{On|Off}", "0", false);
2771 | else
2772 | ReplaceString(strCommand, sizeof(strCommand), "{On|Off}", "1", false);
2773 |
2774 | FormatVoteString(iVote, _, strCommand, sizeof(strCommand));
2775 | ServerCommand(strCommand);
2776 | }
2777 |
2778 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++)
2779 | g_bVoteForSimple[iVoter][iVote] = false;
2780 |
2781 | if(g_strVotePassNotify[iVote][0])
2782 | {
2783 | char strNotification[255];
2784 | strcopy(strNotification, sizeof(strNotification), g_strVotePassNotify[iVote]);
2785 |
2786 | if(g_strVoteConVar[iVote][0] && GetConVarBool(FindConVar(g_strVoteConVar[iVote])))
2787 | {
2788 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "Off", true);
2789 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "off", true);
2790 | }
2791 | else
2792 | {
2793 | ReplaceString(strNotification, sizeof(strNotification), "{On|Off}", "On", true);
2794 | ReplaceString(strNotification, sizeof(strNotification), "{on|off}", "on", true);
2795 | }
2796 |
2797 | FormatVoteString(iVote, _, strNotification, sizeof(strNotification));
2798 | CPrintToChatAll("%s", strNotification);
2799 |
2800 | // get vote name
2801 | char LogstrName[56];
2802 | strcopy(LogstrName, sizeof(LogstrName), g_strVoteName[iVote]);
2803 |
2804 | // Logging Vote
2805 | LogToFileEx(g_sLogPath,
2806 | "[Custom Votes] Last vote ( %s ) passed.",
2807 | LogstrName);
2808 | }
2809 | return true;
2810 | }
2811 | return false;
2812 | }
2813 |
2814 | public int GetVotesForTarget(int iVote, int iTarget)
2815 | {
2816 | int iCount;
2817 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
2818 | {
2819 | if(g_bVoteForTarget[iVoter][iVote][iTarget])
2820 | iCount++;
2821 | }
2822 | return iCount;
2823 | }
2824 |
2825 | public int GetVotesForMap(int iVote, int iMap)
2826 | {
2827 | int iCount;
2828 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
2829 | {
2830 | if(g_bVoteForMap[iVoter][iVote][iMap])
2831 | iCount++;
2832 | }
2833 | return iCount;
2834 | }
2835 |
2836 | public int GetVotesForOption(int iVote, int iOption)
2837 | {
2838 | int iCount;
2839 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
2840 | {
2841 | if(g_bVoteForOption[iVoter][iVote][iOption])
2842 | iCount++;
2843 | }
2844 | return iCount;
2845 | }
2846 |
2847 | public int GetVotesForSimple(int iVote)
2848 | {
2849 | int iCount;
2850 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
2851 | {
2852 | if(g_bVoteForSimple[iVoter][iVote])
2853 | iCount++;
2854 | }
2855 | return iCount;
2856 | }
2857 |
2858 | public int GetRequiredVotes(int iVote)
2859 | {
2860 | int iCount;
2861 | for(int iVoter = 1; iVoter <= MaxClients; iVoter++) if(IsClientInGame(iVoter))
2862 | {
2863 | if(!IsFakeClient(iVoter))
2864 | iCount++;
2865 | }
2866 |
2867 | int iRequired = RoundToCeil(float(iCount) * g_flVoteRatio[iVote]);
2868 | if(iRequired < g_iVoteMinimum[iVote])
2869 | iRequired = g_iVoteMinimum[iVote];
2870 |
2871 | if(iRequired < 1)
2872 | iRequired = 1;
2873 |
2874 | return iRequired;
2875 | }
2876 |
2877 | void AddVoteMenuOption(Handle menu, int curVotesCount, const char[] info,
2878 | const char[] display, int style = ITEMDRAW_DEFAULT)
2879 | {
2880 | if (curVotesCount > 0 && GetMenuItemCount(menu) > 0)
2881 | {
2882 | InsertMenuItem(menu, 0, info, display, style);
2883 | return;
2884 | }
2885 |
2886 | AddMenuItem(menu, info, display, style);
2887 | }
2888 |
2889 | // ====[ TIMERS ]==============================================================
2890 | public Action Timer_Second(Handle hTimer)
2891 | {
2892 | g_iMapTime++;
2893 | return Plugin_Continue;
2894 | }
2895 |
2896 | public Action Timer_ReCreateLogPath(Handle timer)
2897 | {
2898 | CreateLogFile();
2899 | return Plugin_Continue;
2900 | }
2901 |
2902 | // ====[ STOCKS ]==============================================================
2903 | stock bool IsValidClient(int iClient)
2904 | {
2905 | if(iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient))
2906 | return false;
2907 | return true;
2908 | }
2909 |
2910 | bool IsAFKClient(int iClient)
2911 | {
2912 | #if defined _afkmanager_included
2913 |
2914 | if (g_bAfkManager && bAfkManagerEnable)
2915 | {
2916 | if (AFKM_GetClientAFKTime(iClient) >= iAfkTime)
2917 | {
2918 | return true;
2919 | }
2920 |
2921 | return false;
2922 | }
2923 | #else
2924 | #pragma unused iClient
2925 | #endif
2926 |
2927 | return false;
2928 | }
2929 |
2930 | stock void FormatVoterString(int iVote, int iVoter, char[] strBuffer, int iBufferSize)
2931 | {
2932 | char strVoter[MAX_NAME_LENGTH];
2933 | IntToString(iVoter, strVoter, sizeof(strVoter));
2934 |
2935 | ReplaceString(strBuffer, iBufferSize, "{VOTER_INDEX}", strVoter, false);
2936 |
2937 | char strVoterId[MAX_NAME_LENGTH];
2938 | IntToString(GetClientUserId(iVoter), strVoterId, sizeof(strVoterId));
2939 |
2940 | ReplaceString(strBuffer, iBufferSize, "{VOTER_ID}", strVoterId, false);
2941 |
2942 | char strVoterSteamId[MAX_NAME_LENGTH];
2943 | GetClientAuthId(iVoter, AuthId_Steam2, strVoterSteamId, sizeof(strVoterSteamId));
2944 |
2945 | QuoteString(strVoterSteamId, sizeof(strVoterSteamId));
2946 | ReplaceString(strBuffer, iBufferSize, "{VOTER_STEAMID}", strVoterSteamId, false);
2947 |
2948 | char strVoterName[MAX_NAME_LENGTH];
2949 | GetClientName(iVoter, strVoterName, sizeof(strVoterName));
2950 |
2951 | QuoteString(strVoterName, sizeof(strVoterName));
2952 | ReplaceString(strBuffer, iBufferSize, "{VOTER_NAME}", strVoterName, false);
2953 | }
2954 |
2955 | stock void FormatVoteString(int iVote, int iChoice = -1, char[] strBuffer, int iBufferSize)
2956 | {
2957 | char strVoteAmount[MAX_NAME_LENGTH];
2958 | switch(g_iVoteType[iVote])
2959 | {
2960 | case VoteType_Players: IntToString(GetVotesForTarget(iVote, iChoice), strVoteAmount, sizeof(strVoteAmount));
2961 | case VoteType_Map: IntToString(GetVotesForMap(iVote, iChoice), strVoteAmount, sizeof(strVoteAmount));
2962 | case VoteType_List: IntToString(GetVotesForOption(iVote, iChoice), strVoteAmount, sizeof(strVoteAmount));
2963 | case VoteType_Simple: IntToString(GetVotesForSimple(iVote), strVoteAmount, sizeof(strVoteAmount));
2964 | }
2965 |
2966 | QuoteString(strVoteAmount, sizeof(strVoteAmount));
2967 | ReplaceString(strBuffer, iBufferSize, "{VOTE_AMOUNT}", strVoteAmount, false);
2968 |
2969 | char strVoteRequired[MAX_NAME_LENGTH];
2970 | IntToString(GetRequiredVotes(iVote), strVoteRequired, sizeof(strVoteRequired));
2971 |
2972 | QuoteString(strVoteRequired, sizeof(strVoteRequired));
2973 | ReplaceString(strBuffer, iBufferSize, "{VOTE_REQUIRED}", strVoteRequired, false);
2974 | }
2975 |
2976 | stock void FormatTargetString(int iVote, int iTarget, char[] strBuffer, int iBufferSize)
2977 | {
2978 | // Check if target disconnected (Anti-Grief)
2979 | if(!IsValidClient(iTarget))
2980 | {
2981 | char strAntiGrief[255];
2982 | strcopy(strAntiGrief, sizeof(strAntiGrief), g_strVoteTargetIndex);
2983 | ReplaceString(strBuffer, iBufferSize, "{TARGET_INDEX}", g_strVoteTargetIndex, false);
2984 |
2985 | strcopy(strAntiGrief, sizeof(strAntiGrief), g_strVoteTargetId);
2986 | ReplaceString(strBuffer, iBufferSize, "{TARGET_ID}", g_strVoteTargetId, false);
2987 |
2988 | strcopy(strAntiGrief, sizeof(strAntiGrief), g_strVoteTargetAuth);
2989 | QuoteString(strAntiGrief, sizeof(strAntiGrief));
2990 | ReplaceString(strBuffer, iBufferSize, "{TARGET_STEAMID}", g_strVoteTargetAuth, false);
2991 |
2992 | strcopy(strAntiGrief, sizeof(strAntiGrief), g_strVoteTargetName);
2993 | QuoteString(strAntiGrief, sizeof(strAntiGrief));
2994 | ReplaceString(strBuffer, iBufferSize, "{TARGET_NAME}", g_strVoteTargetName, false);
2995 | return;
2996 | }
2997 |
2998 | char strTarget[MAX_NAME_LENGTH];
2999 | IntToString(iTarget, strTarget, sizeof(strTarget));
3000 |
3001 | ReplaceString(strBuffer, iBufferSize, "{TARGET_INDEX}", strTarget, false);
3002 |
3003 | char strTargetId[MAX_NAME_LENGTH];
3004 | IntToString(GetClientUserId(iTarget), strTargetId, sizeof(strTargetId));
3005 |
3006 | ReplaceString(strBuffer, iBufferSize, "{TARGET_ID}", strTargetId, false);
3007 |
3008 | char strTargetSteamId[MAX_NAME_LENGTH];
3009 | GetClientAuthId(iTarget, AuthId_Steam2, strTargetSteamId, sizeof(strTargetSteamId));
3010 |
3011 | QuoteString(strTargetSteamId, sizeof(strTargetSteamId));
3012 | ReplaceString(strBuffer, iBufferSize, "{TARGET_STEAMID}", strTargetSteamId, false);
3013 |
3014 | char strTargetName[MAX_NAME_LENGTH];
3015 | GetClientName(iTarget, strTargetName, sizeof(strTargetName));
3016 |
3017 | QuoteString(strTargetName, sizeof(strTargetName));
3018 | ReplaceString(strBuffer, iBufferSize, "{TARGET_NAME}", strTargetName, false);
3019 | }
3020 |
3021 | stock void FormatMapString(int iVote, int iMap, char[] strBuffer, int iBufferSize)
3022 | {
3023 | char strMap[MAX_NAME_LENGTH];
3024 | GetArrayString(g_hArrayVoteMapList[iVote], iMap, strMap, sizeof(strMap));
3025 |
3026 | QuoteString(strMap, sizeof(strMap));
3027 | ReplaceString(strBuffer, iBufferSize, "{MAP_NAME}", strMap, false);
3028 |
3029 | char strCurrentMap[MAX_NAME_LENGTH];
3030 | GetCurrentMap(strCurrentMap, sizeof(strCurrentMap));
3031 |
3032 | QuoteString(strCurrentMap, sizeof(strCurrentMap));
3033 | ReplaceString(strBuffer, iBufferSize, "{CURRENT_MAP_NAME}", strCurrentMap, false);
3034 | }
3035 |
3036 | stock void FormatOptionString(int iVote, int iOption, char[] strBuffer, int iBufferSize)
3037 | {
3038 | char strOptionName[MAX_NAME_LENGTH];
3039 | GetArrayString(g_hArrayVoteOptionName[iVote], iOption, strOptionName, sizeof(strOptionName));
3040 |
3041 | QuoteString(strOptionName, sizeof(strOptionName));
3042 | ReplaceString(strBuffer, iBufferSize, "{OPTION_NAME}", strOptionName, false);
3043 |
3044 | char strOptionResult[MAX_NAME_LENGTH];
3045 | GetArrayString(g_hArrayVoteOptionResult[iVote], iOption, strOptionResult, sizeof(strOptionResult));
3046 |
3047 | QuoteString(strOptionResult, sizeof(strOptionResult));
3048 | ReplaceString(strBuffer, iBufferSize, "{OPTION_RESULT}", strOptionResult, false);
3049 | }
3050 |
3051 | stock void QuoteString(char[] strBuffer, int iBuffersize)
3052 | {
3053 | Format(strBuffer, iBuffersize + 4, "\"%s\"", strBuffer);
3054 | }
3055 |
--------------------------------------------------------------------------------