targetex.inc
Original include source with line numbers.
| 1 | #if defined _targetex_included |
| 2 | #endinput |
| 3 | #endif |
| 4 | #define _targetex_included |
| 5 | |
| 6 | #define TARGETEX_VERSION 1.0 |
| 7 | |
| 8 | #include <amxmisc> |
| 9 | #include <engine> |
| 10 | |
| 11 | #if !defined MAX_PLAYERS |
| 12 | const MAX_PLAYERS = 32 |
| 13 | #endif |
| 14 | |
| 15 | #if !defined MAX_NAME_LENGTH |
| 16 | const MAX_NAME_LENGTH = 32 |
| 17 | #endif |
| 18 | |
| 19 | #if !defined GetPlayersFlags |
| 20 | enum GetPlayersFlags (<<= 1) |
| 21 | { |
| 22 | GetPlayers_None = 0, // No filter (Default) |
| 23 | GetPlayers_ExcludeDead = 1, // Do not include dead clients |
| 24 | GetPlayers_ExcludeAlive, // Do not include alive clients |
| 25 | GetPlayers_ExcludeBots, // Do not include bots |
| 26 | GetPlayers_ExcludeHuman, // Do not include human clients |
| 27 | GetPlayers_MatchTeam, // Match with team |
| 28 | GetPlayers_MatchNameSubstring, // Match with part of name |
| 29 | GetPlayers_CaseInsensitive, // Match case insensitive |
| 30 | GetPlayers_ExcludeHLTV, // Do not include HLTV proxies |
| 31 | GetPlayers_IncludeConnecting // Include connecting clients |
| 32 | }; |
| 33 | |
| 34 | stock get_players_ex(players[MAX_PLAYERS] = {}, &num, GetPlayersFlags:flags = GetPlayers_None, const team[] = "") |
| 35 | { |
| 36 | new strFlags[10]; |
| 37 | get_flags(_:flags, strFlags, charsmax(strFlags)); |
| 38 | get_players(players, num, strFlags, team); |
| 39 | } |
| 40 | #endif |
| 41 | |
| 42 | /** |
| 43 | * Selectors used with cmd_targetex() |
| 44 | */ |
| 45 | #define TARGETEX_ARG_AIM "aim" |
| 46 | #define TARGETEX_ARG_ALL "all" |
| 47 | #define TARGETEX_ARG_ALIVE "alive" |
| 48 | #define TARGETEX_ARG_BOTS "bots" |
| 49 | #define TARGETEX_ARG_DEAD "dead" |
| 50 | #define TARGETEX_ARG_HUMANS "humans" |
| 51 | #define TARGETEX_ARG_ME "me" |
| 52 | #define TARGETEX_ARG_SPECTATING "spectating" |
| 53 | #define TARGETEX_ARG_VIEW "view" |
| 54 | |
| 55 | static const TargetEx_GroupArguments[][] = { TARGETEX_ARG_ALL, TARGETEX_ARG_ALIVE, TARGETEX_ARG_BOTS, TARGETEX_ARG_DEAD, TARGETEX_ARG_HUMANS, TARGETEX_ARG_VIEW }; |
| 56 | static const TargetEx_NoExcludeArguments[][] = { TARGETEX_ARG_AIM, TARGETEX_ARG_BOTS, TARGETEX_ARG_ME, TARGETEX_ARG_SPECTATING, TARGETEX_ARG_VIEW }; |
| 57 | static TargetEx_GroupArgsLens[sizeof(TargetEx_GroupArguments)]; |
| 58 | static TargetEx_NoExcludeArgsLens[sizeof(TargetEx_NoExcludeArguments)]; |
| 59 | |
| 60 | /** |
| 61 | * Flags used with cmd_targetex() |
| 62 | */ |
| 63 | enum TargetexFlags (<<= 1) |
| 64 | { |
| 65 | TARGETEX_NONE = 0, |
| 66 | TARGETEX_OBEY_IMM_SINGLE = 1, |
| 67 | TARGETEX_OBEY_IMM_GROUP, |
| 68 | TARGETEX_NO_SELF, |
| 69 | TARGETEX_NO_GROUPS, |
| 70 | TARGETEX_NO_BOTS, |
| 71 | TARGETEX_NO_ALIVE, |
| 72 | TARGETEX_NO_DEAD |
| 73 | } |
| 74 | |
| 75 | enum TargetExTeamStructure |
| 76 | { |
| 77 | TARGETEX_TEAM_CODE[5], |
| 78 | TARGETEX_TEAM_NAME[10], |
| 79 | TARGETEX_TEAM_LEN |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * List of teams used with cmd_targetex() |
| 84 | */ |
| 85 | static TargetEx_Teams[][TargetExTeamStructure] = |
| 86 | { |
| 87 | { "ct", "CT" }, |
| 88 | { "t", "TERRORIST" }, |
| 89 | { "spec", "SPECTATOR" } |
| 90 | } |
| 91 | |
| 92 | static TargetEx_TeamStart, TargetEx_TeamEnd; |
| 93 | |
| 94 | /** |
| 95 | * Processes a generic target pattern and tries to match it to a client or a |
| 96 | * group of clients based on filtering flags and the usage of special arguments. |
| 97 | * |
| 98 | * @note If no clients were matched an appropriate message is displayed |
| 99 | * to the admin. |
| 100 | * @note If no group matching symbol is used, the function will use the |
| 101 | * cmd_target() stock instead. |
| 102 | * @note In order to use the special arguments provided by this function, |
| 103 | * the admin must start the <player> argument with the "@" symbol. |
| 104 | * @note This is a list of all the available arguments that admins can use: |
| 105 | * @aim - targets the player that the admin is aiming at |
| 106 | * @all - targets all players |
| 107 | * @alive - targets alive players |
| 108 | * @bots - targets all bots |
| 109 | * @dead - targets dead players |
| 110 | * @humans - targets all humans |
| 111 | * @me - targets the admin himself |
| 112 | * @spectating - targets the client that the admin is spectating |
| 113 | * @view - targets all clients in the admin's field of view |
| 114 | * @note For arguments that are used to target more than one client, |
| 115 | * the admin can also specify a team name after the argument itself, |
| 116 | * e.g. @alivect will target all alive counter-terrorists. All the |
| 117 | * valid team names and argument codes can be found in the TargetEx_Teams |
| 118 | * constant in targetex.inc. |
| 119 | * @note When using an argument that targets a group of clients, the admin |
| 120 | * can also exclude himself from the command by adding a "!" symbol |
| 121 | * right after the "@" one, e.g. @!humans will target all humans |
| 122 | * except the admin that used the command. |
| 123 | * @note If only one client is matched, the client's name will be copied |
| 124 | * in the "name" buffer, otherwise a translated string using one |
| 125 | * of the language keys found in cmdtarget.txt will be copied in the |
| 126 | * same buffer. |
| 127 | * |
| 128 | * @param id Client index of admin performing the action |
| 129 | * @param arg Target pattern |
| 130 | * @param players Array to store the matched clients in |
| 131 | * @param name Buffer to store the client name or argument translation |
| 132 | * @param len Maximum buffer length |
| 133 | * @param flags Optional filtering flags (enum TargetexFlags); valid flags are: |
| 134 | * TARGETEX_NONE - no filter (default) |
| 135 | * TARGETEX_OBEY_IMM_SINGLE - immunity will be obeyed when using arguments that target a single client |
| 136 | * TARGETEX_OBEY_IMM_GROUP - immunity will be obeyed when using arguments that target a group of clients |
| 137 | * TARGETEX_NO_SELF - doesn't allow the admin to target himself |
| 138 | * TARGETEX_NO_GROUPS - doesn't allow usage of arguments that target a group of clients |
| 139 | * TARGETEX_NO_BOTS - doesn't allow targeting bots |
| 140 | * TARGETEX_NO_ALIVE - doesn't allow targeting alive clients |
| 141 | * TARGETEX_NO_DEAD - doesn't allow targeting dead clients |
| 142 | * |
| 143 | * @return Number of clients matched |
| 144 | */ |
| 145 | stock cmd_targetex(id, const arg[], players[MAX_PLAYERS], name[], len, TargetexFlags:flags = TARGETEX_OBEY_IMM_SINGLE) |
| 146 | { |
| 147 | static bool:bDontInit; |
| 148 | |
| 149 | if(!bDontInit) |
| 150 | { |
| 151 | bDontInit = true; |
| 152 | |
| 153 | new szModName[32]; |
| 154 | get_modname(szModName, charsmax(szModName)); |
| 155 | |
| 156 | if(equal(szModName, "cstrike")) |
| 157 | { |
| 158 | TargetEx_TeamStart = 0; |
| 159 | TargetEx_TeamEnd = 2; |
| 160 | } |
| 161 | |
| 162 | TargetEx_TeamEnd++; |
| 163 | |
| 164 | for(new i = TargetEx_TeamStart; i < TargetEx_TeamEnd; i++) |
| 165 | { |
| 166 | TargetEx_Teams[i][TARGETEX_TEAM_LEN] = strlen(TargetEx_Teams[i][TARGETEX_TEAM_CODE]); |
| 167 | } |
| 168 | |
| 169 | for(new i; i < sizeof(TargetEx_GroupArguments); i++) |
| 170 | { |
| 171 | TargetEx_GroupArgsLens[i] = strlen(TargetEx_GroupArguments[i]); |
| 172 | } |
| 173 | |
| 174 | for(new i; i < sizeof(TargetEx_NoExcludeArgsLens); i++) |
| 175 | { |
| 176 | TargetEx_NoExcludeArgsLens[i] = strlen(TargetEx_NoExcludeArgsLens[i]); |
| 177 | } |
| 178 | |
| 179 | register_dictionary("targetex.txt"); |
| 180 | } |
| 181 | |
| 182 | if(arg[0] == '@') |
| 183 | { |
| 184 | new bool:bExceptMe = arg[1] == '!', iStartArg = bExceptMe ? 2 : 1; |
| 185 | new bool:bIsGroupArg = targetex_is_group_argument(arg[iStartArg]); |
| 186 | |
| 187 | if(bIsGroupArg) |
| 188 | { |
| 189 | if(flags & TARGETEX_NO_GROUPS) |
| 190 | { |
| 191 | console_print(id, "%L", id, "TARGETEX_NO_GROUPS"); |
| 192 | return 0; |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | if(bExceptMe && targetex_is_no_exclude_argument(arg[iStartArg])) |
| 197 | { |
| 198 | targetex_cant_exclude(id, arg); |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | new iPlayers[MAX_PLAYERS], iPnum; |
| 203 | new szMatchingString[10], GetPlayersFlags:iMatchingFlags, iMatchedPlayers; |
| 204 | |
| 205 | if(flags & TARGETEX_NO_ALIVE) |
| 206 | { |
| 207 | iMatchingFlags |= GetPlayers_ExcludeAlive; |
| 208 | } |
| 209 | else if(flags & TARGETEX_NO_DEAD) |
| 210 | { |
| 211 | iMatchingFlags |= GetPlayers_ExcludeDead; |
| 212 | } |
| 213 | |
| 214 | if(flags & TARGETEX_NO_BOTS) |
| 215 | { |
| 216 | iMatchingFlags |= GetPlayers_ExcludeBots|GetPlayers_ExcludeHLTV; |
| 217 | } |
| 218 | |
| 219 | new szLangKey[32], szSuffix[6], iArgLen = strlen(arg) |
| 220 | new iArgLen2 = iArgLen - (bExceptMe ? 1 : 0); |
| 221 | |
| 222 | for(new i = TargetEx_TeamStart; i < TargetEx_TeamEnd; i++) |
| 223 | { |
| 224 | if(equal(arg[iArgLen - TargetEx_Teams[i][TARGETEX_TEAM_LEN]], TargetEx_Teams[i][TARGETEX_TEAM_CODE])) |
| 225 | { |
| 226 | szSuffix = "_TEAM"; |
| 227 | iMatchingFlags |= GetPlayers_MatchTeam; |
| 228 | copy(szMatchingString, charsmax(szMatchingString), TargetEx_Teams[i][TARGETEX_TEAM_NAME]); |
| 229 | |
| 230 | if(iArgLen2 == TargetEx_Teams[i][TARGETEX_TEAM_LEN] + 1) |
| 231 | { |
| 232 | szLangKey = "TARGETEX_ARG_ALL"; |
| 233 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 234 | goto @AFTER_ARGS; |
| 235 | } |
| 236 | |
| 237 | break; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | if(equal(arg[iStartArg], TARGETEX_ARG_AIM, 3)) |
| 242 | { |
| 243 | new iTarget, iBody; |
| 244 | get_user_aiming(id, iTarget, iBody); |
| 245 | |
| 246 | if(iTarget) |
| 247 | { |
| 248 | iPlayers[iPnum++] = iTarget; |
| 249 | } |
| 250 | } |
| 251 | else if(equal(arg[iStartArg], TARGETEX_ARG_ALL, 3)) |
| 252 | { |
| 253 | szLangKey = "TARGETEX_ARG_ALL"; |
| 254 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 255 | } |
| 256 | else if(equal(arg[iStartArg], TARGETEX_ARG_ALIVE, 5)) |
| 257 | { |
| 258 | if(flags & TARGETEX_NO_ALIVE) |
| 259 | { |
| 260 | console_print(id, "%L", id, "TARGETEX_NO_ALIVE") |
| 261 | return 0; |
| 262 | } |
| 263 | |
| 264 | szLangKey = "TARGETEX_ARG_ALIVE"; |
| 265 | iMatchingFlags |= GetPlayers_ExcludeDead; |
| 266 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 267 | } |
| 268 | else if(equal(arg[iStartArg], TARGETEX_ARG_BOTS, 4)) |
| 269 | { |
| 270 | if(flags & TARGETEX_NO_BOTS) |
| 271 | { |
| 272 | console_print(id, "%L", id, "TARGETEX_NO_BOTS") |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | szLangKey = "TARGETEX_ARG_BOTS"; |
| 277 | iMatchingFlags |= GetPlayers_ExcludeHuman; |
| 278 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 279 | } |
| 280 | else if(equal(arg[iStartArg], TARGETEX_ARG_DEAD, 4)) |
| 281 | { |
| 282 | if(flags & TARGETEX_NO_DEAD) |
| 283 | { |
| 284 | console_print(id, "%L", id, "TARGETEX_NO_DEAD") |
| 285 | return 0; |
| 286 | } |
| 287 | |
| 288 | szLangKey = "TARGETEX_ARG_DEAD"; |
| 289 | iMatchingFlags |= GetPlayers_ExcludeAlive; |
| 290 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 291 | } |
| 292 | else if(equal(arg[iStartArg], TARGETEX_ARG_HUMANS, 6)) |
| 293 | { |
| 294 | szLangKey = "TARGETEX_ARG_HUMANS"; |
| 295 | iMatchingFlags |= GetPlayers_ExcludeBots|GetPlayers_ExcludeHLTV; |
| 296 | get_players_ex(iPlayers, iPnum, iMatchingFlags, szMatchingString); |
| 297 | } |
| 298 | else if(equal(arg[iStartArg], TARGETEX_ARG_ME, 2)) |
| 299 | { |
| 300 | iPlayers[iPnum++] = id; |
| 301 | } |
| 302 | else if(equal(arg[iStartArg], TARGETEX_ARG_SPECTATING, 10)) |
| 303 | { |
| 304 | new iTarget = entity_get_int(id, EV_INT_iuser2); |
| 305 | |
| 306 | if(iTarget) |
| 307 | { |
| 308 | iPlayers[iPnum++] = iTarget; |
| 309 | } |
| 310 | } |
| 311 | else if(equal(arg[iStartArg], TARGETEX_ARG_VIEW, 4)) |
| 312 | { |
| 313 | new iViewPlayers[MAX_PLAYERS], iViewPnum; |
| 314 | get_players_ex(iViewPlayers, iViewPnum, iMatchingFlags, szMatchingString); |
| 315 | |
| 316 | for(new Float:fOrigin[3], iPlayer, i; i < iViewPnum; i++) |
| 317 | { |
| 318 | iPlayer = iViewPlayers[i]; |
| 319 | entity_get_vector(iPlayer, EV_VEC_origin, fOrigin); |
| 320 | |
| 321 | if(is_in_viewcone(id, fOrigin, 1)) |
| 322 | { |
| 323 | iPlayers[iPnum++] = iPlayer; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | szLangKey = "TARGETEX_ARG_VIEW"; |
| 328 | } |
| 329 | |
| 330 | @AFTER_ARGS: |
| 331 | |
| 332 | for(new iPlayer, i; i < iPnum; i++) |
| 333 | { |
| 334 | iPlayer = iPlayers[i]; |
| 335 | |
| 336 | if(id == iPlayer) |
| 337 | { |
| 338 | if(bExceptMe || flags & TARGETEX_NO_SELF) |
| 339 | { |
| 340 | continue; |
| 341 | } |
| 342 | } |
| 343 | else if(get_user_flags(iPlayer) & ADMIN_IMMUNITY) |
| 344 | { |
| 345 | if(flags & TARGETEX_OBEY_IMM_GROUP && bIsGroupArg) |
| 346 | { |
| 347 | continue; |
| 348 | } |
| 349 | |
| 350 | if(flags & TARGETEX_OBEY_IMM_SINGLE && !bIsGroupArg) |
| 351 | { |
| 352 | continue; |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | players[iMatchedPlayers++] = iPlayer; |
| 357 | } |
| 358 | |
| 359 | switch(iMatchedPlayers) |
| 360 | { |
| 361 | case 0: |
| 362 | { |
| 363 | console_print(id, "%L", id, "TARGETEX_NO_MATCHES"); |
| 364 | } |
| 365 | case 1: |
| 366 | { |
| 367 | get_user_name(players[0], name, len); |
| 368 | } |
| 369 | default: |
| 370 | { |
| 371 | if(szSuffix[0]) |
| 372 | { |
| 373 | add(szLangKey, charsmax(szLangKey), szSuffix); |
| 374 | formatex(name, len, "%L", LANG_PLAYER, szLangKey, szMatchingString); |
| 375 | } |
| 376 | else |
| 377 | { |
| 378 | formatex(name, len, "%L", LANG_PLAYER, szLangKey); |
| 379 | } |
| 380 | |
| 381 | if(bExceptMe) |
| 382 | { |
| 383 | format(name, len, "%s %L", name, LANG_PLAYER, "TARGETEX_EXCEPT_HIMSELF"); |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | return iMatchedPlayers; |
| 389 | } |
| 390 | else |
| 391 | { |
| 392 | new iSingleFlags; |
| 393 | |
| 394 | if(flags & TARGETEX_OBEY_IMM_SINGLE) |
| 395 | { |
| 396 | iSingleFlags |= CMDTARGET_OBEY_IMMUNITY; |
| 397 | } |
| 398 | |
| 399 | if(~flags & TARGETEX_NO_SELF) |
| 400 | { |
| 401 | iSingleFlags |= CMDTARGET_ALLOW_SELF; |
| 402 | } |
| 403 | |
| 404 | if(flags & TARGETEX_NO_DEAD) |
| 405 | { |
| 406 | iSingleFlags |= CMDTARGET_ONLY_ALIVE; |
| 407 | } |
| 408 | |
| 409 | if(flags & TARGETEX_NO_BOTS) |
| 410 | { |
| 411 | iSingleFlags |= CMDTARGET_NO_BOTS; |
| 412 | } |
| 413 | |
| 414 | players[0] = cmd_target(id, arg, iSingleFlags); |
| 415 | |
| 416 | if(players[0]) |
| 417 | { |
| 418 | if(flags & TARGETEX_NO_ALIVE && is_user_alive(players[0])) |
| 419 | { |
| 420 | console_print(id, "%L", id, "TARGETEX_NO_ALIVE"); |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | get_user_name(players[0], name, len); |
| 425 | return 1; |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | return 0; |
| 430 | } |
| 431 | |
| 432 | static stock targetex_cant_exclude(id, const arg[]) |
| 433 | { |
| 434 | console_print(id, "%L", id, "TARGETEX_NO_EXCLUDE", arg[2]); |
| 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Checks whether a specified argument is a team argument. |
| 439 | * |
| 440 | * @param arg Target pattern |
| 441 | * |
| 442 | * @return True if it is, false otherwise |
| 443 | */ |
| 444 | stock bool:targetex_is_team_argument(const arg[]) |
| 445 | { |
| 446 | for(new i = TargetEx_TeamStart; i < TargetEx_TeamEnd; i++) |
| 447 | { |
| 448 | if(equal(arg, TargetEx_Teams[i][TARGETEX_TEAM_CODE], TargetEx_Teams[i][TARGETEX_TEAM_LEN])) |
| 449 | { |
| 450 | return true; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | return false; |
| 455 | } |
| 456 | |
| 457 | /** |
| 458 | * Checks whether a specified argument is a group argument. |
| 459 | * |
| 460 | * @param arg Target pattern |
| 461 | * |
| 462 | * @return True if it is, false otherwise |
| 463 | */ |
| 464 | stock bool:targetex_is_group_argument(const arg[]) |
| 465 | { |
| 466 | for(new i; i < sizeof(TargetEx_GroupArguments); i++) |
| 467 | { |
| 468 | if(equal(arg, TargetEx_GroupArguments[i], TargetEx_GroupArgsLens[i])) |
| 469 | { |
| 470 | return true; |
| 471 | } |
| 472 | } |
| 473 | |
| 474 | return targetex_is_team_argument(arg); |
| 475 | } |
| 476 | |
| 477 | /** |
| 478 | * Checks whether a specified argument is a type of argument that doesn't |
| 479 | * allow the admin to exclude himself. |
| 480 | * |
| 481 | * @param arg Target pattern |
| 482 | * |
| 483 | * @return True if it is, false otherwise |
| 484 | */ |
| 485 | stock bool:targetex_is_no_exclude_argument(const arg[]) |
| 486 | { |
| 487 | for(new i; i < sizeof(TargetEx_NoExcludeArguments); i++) |
| 488 | { |
| 489 | if(equal(arg, TargetEx_NoExcludeArguments[i], TargetEx_NoExcludeArgsLens[i])) |
| 490 | { |
| 491 | return true; |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | return false; |
| 496 | } |