/* AMX Mod X * Dropped Bomb Defuse * * (c) Copyright 2006 by VEN * * This file is provided as is (no warranties) * * DESCRIPTION * Plugin allows to CTs defuse the dropped bomb. Once bomb is defused CT team will win * the round (can be disabled) and CT defuser will gain 1 frag (configurable). Plugin * emits standard sounds like defuse start/end/denied, draws progress bar, plays/shows * "Bomb has been defused" audio/text. Defuser kit requirement can be forced with a CVAR. * * INSTALLATION * Install(/enable) "Force Team Win": http://forums.alliedmods.net/showthread.php?t=40287 * Download/copy ftw.inc into amxmodx/scripting/include folder. * Note: if you do not wish to perform the above steeps, you can skip that but then you * should disable(comment) the FORCE_TEAM_WIN option. This will prevent CT win on defuse. * Compile(/install/enable) droppedbombdefuse.sma using compile.exe or amxxpc.exe. * Enable fakemeta module and change the map or restart the server. * Also make sure that you have AMX Mod X version 1.71 or higher. * * CVARs * dbd_time (N: seconds, default: 10, 0: disable the plugin) - defuse time * dbd_defkit_required (0: OFF, 1: ON, default: 0) - controls defkit requirement * Below with an examples is explained how CVARs works, remember it's just an examples. * By default defuse time is 10 seconds, but with defuser kit it will be 5 seconds. * If defuser kit requirement is forced with the CVAR, defuse time with a defkit will be * not 5 but 10 seconds. If you still want 5 seconds, comment IGNORE_MULTIPLIER option. * You can alter 5 seconds defuse w/ a defkit time by the DEFKIT_TIME_MULTIPLIER option. * So if multiplier is 0.4 and dbd_time is 15, you will get 0.4 * 15, i.e. 6 seconds. * * OPTIONS * The following options can be altered at the top of the plugin's source code. * Bomb map check, defuse with a defkit time multiplier, ignore multiplier on defkit * requirement, frags bonus, player/bomb max. distance/view angle diff. and a few more. * * VERSIONS * 0.3.1 now frags on the scoreboard updated immediately * 0.3 added an option to disable the CT's team win on dropped bomb defuse * now bomb can be defused even when CT's team win can't be forced (server is full) * 0.2 defkit requirement can be forced with the dbd_defkit_required CVAR (default: 0) * 0.1 first release */ #include // AMX Mod X 1.71+ required, check your addons/metamod/plugins.ini #include // fakemeta module required, check your configs/modules.ini #include // this is not a module! // plugin's main information #define PLUGIN_NAME "Dropped Bomb Defuse" #define PLUGIN_VERSION "0.3.1" #define PLUGIN_AUTHOR "VEN" // OPTIONS BELOW // comment to disable force team win on dropped bomb defuse #define FORCE_TEAM_WIN // comment to disable check for bomb maps #define BOMB_MAP_CHECK // multiplier * dbd_time == defuse time with a defkit #define DEFKIT_TIME_MULTIPLIER 0.5 // comment to not ignore the above multiplier when dbd_defkit_required CVAR is 1 #define IGNORE_MULTIPLIER // defuser frag bonus, comment or set to 0 to disable this feature #define DEFUSER_FRAG_BONUS 1 // max. allowed distance from a CT to a dropped bomb #define MAX_DISTANCE 70 // max. allowed angle difference between CT's view vector and bomb position #define MAX_VIEWANGLE_DIFF 35 // name of the CVAR which controls dropped bomb defuse time #define CVAR_TIME_NAME "dbd_time" // default value of the "dbd_time" CVAR #define CVAR_TIME_DEF "10" // name of the CVAR which controls defkit requirement #define CVAR_DEFKIT_NAME "dbd_defkit_required" // default value of the "dbd_defkit_required" CVAR #define CVAR_DEFKIT_DEF "0" // requirement center text message new g_requirement[] = "The defuser kit is required!" // uncomment to disable automatic 32/64bit processor detection // possible values are <0: 32bit | 1: 64bit> //#define PROCESSOR_TYPE 0 // OPTIONS ABOVE // private data deaths offset #define OFFSET_DEATHS_32BIT 444 #define OFFSET_DEATHS_64BIT 493 // deaths offset linux difference #define OFFSET_DEATHS_LINUXDIFF 5 // determination of actual offsets #if !defined PROCESSOR_TYPE // is automatic 32/64bit processor detection? #if cellbits == 32 // is the size of a cell are 32 bits? // then considering processor as 32bit #define OFFSET_DEATHS OFFSET_DEATHS_32BIT #else // in other case considering the size of a cell as 64 bits // and then considering processor as 64bit #define OFFSET_DEATHS OFFSET_DEATHS_64BIT #endif #else // processor type specified by PROCESSOR_TYPE define #if PROCESSOR_TYPE == 0 // 32bit processor defined #define OFFSET_DEATHS OFFSET_DEATHS_32BIT #else // considering that 64bit processor defined #define OFFSET_DEATHS OFFSET_DEATHS_64BIT #endif #endif #define CS_GET_USER_DEATHS_(%1) get_pdata_int(%1, OFFSET_DEATHS, OFFSET_DEATHS_LINUXDIFF) // checks whether force team win is enabled and if so includes a native #if defined FORCE_TEAM_WIN #include // ftw.inc, includes cs_force_team_win native, uses "Force Team Win" #endif #define TASK_INTERVAL 0.2 #define TASK_INDEX 349576 #define IN_USE (1<<5) #define SOLID_NOT 0 #define EF_NODRAW 128 new CsTeams:CS_TEAM_CT = CsTeams:2 new g_wpn_denyselect[] = "common/wpn_denyselect.wav" new g_wpn_select[] = "common/wpn_select.wav" new g_classname[] = "classname" new g_weapon_c4[] = "weapon_c4" new g_disarm[] = "weapons/c4_disarm.wav" new g_disarmed[] = "weapons/c4_disarmed.wav" new g_defused_audio[] = "%!MRAD_BOMBDEF" new g_defused_text[] = "#Bomb_Defused" new g_pcvar_time new g_pcvar_defkit new g_msgid_bartime new g_msgid_scoreinfo new g_msgid_bombpickup new g_msgid_audio new g_msgid_text new g_maxplayers new g_defuser new Float:g_time_left #define MAX_PLAYERS 32 new bool:g_has_defuser[MAX_PLAYERS + 1] public plugin_init() { register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR) g_pcvar_time = register_cvar(CVAR_TIME_NAME, CVAR_TIME_DEF) g_pcvar_defkit = register_cvar(CVAR_DEFKIT_NAME, CVAR_DEFKIT_DEF) #if defined BOMB_MAP_CHECK if (!engfunc(EngFunc_FindEntityByString, -1, g_classname, "func_bomb_target")) return #endif register_event("StatusIcon", "event_deficon", "b", "2=defuser") register_event("DeathMsg", "event_death", "a") register_forward(FM_EmitSound, "forward_emit_sound") g_msgid_bartime = get_user_msgid("BarTime") g_msgid_scoreinfo = get_user_msgid("ScoreInfo") g_msgid_bombpickup = get_user_msgid("BombPickup") g_msgid_audio = get_user_msgid("SendAudio") g_msgid_text = get_user_msgid("TextMsg") g_maxplayers = get_maxplayers() } /* *************************************************** Base **************************************************** */ public event_deficon(id) { g_has_defuser[id] = bool:read_data(1) } public event_death() { new id = read_data(2) if (g_defuser == id && task_exists(TASK_INDEX)) { remove_task(TASK_INDEX) msg_bartime(id, 0) } } public client_disconnect(id) { if (task_exists(TASK_INDEX) && g_defuser == id) remove_task(TASK_INDEX) } public forward_emit_sound(id, channel, sound[]) { if (!id || !equali(sound, g_wpn_denyselect) || !is_user_alive(id) || !(pev(id, pev_button) & IN_USE) || get_user_team(id) != _:CS_TEAM_CT) return FMRES_IGNORED new itime = get_pcvar_num(g_pcvar_time) if (!itime || !can_disarm(id)) return FMRES_IGNORED if (task_exists(TASK_INDEX)) { if (id != g_defuser) emit_sound(id, CHAN_ITEM, g_wpn_select, 0.4, ATTN_NORM, 0, PITCH_NORM) return FMRES_SUPERCEDE } new bool:dk_required = bool:get_pcvar_num(g_pcvar_defkit) if (g_has_defuser[id]) { #if defined IGNORE_MULTIPLIER if (dk_required) g_time_left = float(itime) else { g_time_left = itime * DEFKIT_TIME_MULTIPLIER itime = floatround(g_time_left) } #else g_time_left = itime * DEFKIT_TIME_MULTIPLIER itime = floatround(g_time_left) #endif } else { if (dk_required) { client_print(id, print_center, g_requirement) return FMRES_IGNORED } g_time_left = float(itime) } g_defuser = id msg_bartime(id, itime) emit_sound(id, CHAN_ITEM, g_disarm, VOL_NORM, ATTN_NORM, 0, PITCH_NORM) set_task(TASK_INTERVAL, "task_check", TASK_INDEX, _, _, "a", floatround(g_time_left / TASK_INTERVAL)) return FMRES_SUPERCEDE } public task_check() { g_time_left -= TASK_INTERVAL new bomb = can_disarm(g_defuser) if (!bomb || !(pev(g_defuser, pev_button) & IN_USE)) { msg_bartime(g_defuser, 0) remove_task(TASK_INDEX) } else if (g_time_left < TASK_INTERVAL) { remove_task(TASK_INDEX) msg_bartime(g_defuser, 0) emit_sound(g_defuser, CHAN_WEAPON, g_disarmed, VOL_NORM, ATTN_NORM, 0, PITCH_NORM) #if defined DEFUSER_FRAG_BONUS #if DEFUSER_FRAG_BONUS new Float:frags pev(g_defuser, pev_frags, frags) frags += DEFUSER_FRAG_BONUS set_pev(g_defuser, pev_frags, frags) message_begin(MSG_ALL, g_msgid_scoreinfo) write_byte(g_defuser) write_short(floatround(frags)) write_short(CS_GET_USER_DEATHS_(g_defuser)) write_short(0) write_short(get_user_team(g_defuser)) message_end() #endif #endif set_pev(bomb, pev_solid, SOLID_NOT) set_pev(bomb, pev_effects, EF_NODRAW) message_begin(MSG_ALL, g_msgid_bombpickup) message_end() #if defined FORCE_TEAM_WIN cs_force_team_win(CS_TEAM_CT) #endif message_begin(MSG_BROADCAST, g_msgid_audio) write_byte(0) write_string(g_defused_audio) write_short(100) message_end() message_begin(MSG_ALL, g_msgid_text) write_byte(print_center) write_string(g_defused_text) message_end() } } can_disarm(id) { new c4 = engfunc(EngFunc_FindEntityByString, -1, g_classname, g_weapon_c4) if (!c4) return 0 new bomb = pev(c4, pev_owner) if (bomb <= g_maxplayers || !pev(bomb, pev_solid)) return 0 new Float:origin[3], Float:bombpos[3] pev(id, pev_origin, origin) pev(bomb, pev_origin, bombpos) if (get_distance_f(origin, bombpos) > MAX_DISTANCE || !fm_is_visible(id, bombpos) || get_view_angle_diff(id, bombpos) > MAX_VIEWANGLE_DIFF) return 0 return bomb } msg_bartime(index, scale) { message_begin(MSG_ONE, g_msgid_bartime, _, index) write_short(scale) message_end() } /* ************************************************** Stocks *************************************************** */ // checks whether no obstacles between eyes and given point stock bool:fm_is_visible(index, const Float:point[3]) { new Float:origin[3], Float:view_ofs[3], Float:eyespos[3] pev(index, pev_origin, origin) pev(index, pev_view_ofs, view_ofs) xs_vec_add(origin, view_ofs, eyespos) engfunc(EngFunc_TraceLine, eyespos, point, 0, index) new Float:fraction global_get(glb_trace_fraction, fraction) if (fraction == 1.0) return true return false } // returns a float degree angle between view vector and given point stock Float:get_view_angle_diff(index, Float:vec_c[3]) { new Float:vec_a[3], Float:vec_b[3], viewend[3] new Float:origin[3], Float:view_ofs[3] pev(index, pev_origin, origin) pev(index, pev_view_ofs, view_ofs) xs_vec_add(origin, view_ofs, vec_a) get_user_origin(index, viewend, 3) vec_b[0] = float(viewend[0]) vec_b[1] = float(viewend[1]) vec_b[2] = float(viewend[2]) new Float:a = get_distance_f(vec_b, vec_c) new Float:b = get_distance_f(vec_a, vec_c) new Float:c = get_distance_f(vec_a, vec_b) return floatacos((b*b + c*c - a*a) / (2 * b * c), _:degrees) } /* **************************************************** EOF **************************************************** */