Junior Member
Join Date: Apr 2021
Location: brazil
|
04-23-2021
, 08:25
Re: Molotov Cocktail v3.30 - Released April 13, 2014
|
#62
|
Quote:
Originally Posted by condoriano90
Sorry about that, this should work now:
PHP Code:
/******************************************************************************* * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you must extend this exception * to your version of the file. * ********************************************************************************
Molotov Cocktail Version 3.30 Maintained by: DynamicBits (Andy)
* Commands: - say molotov - Buy a Molotov - say /molotov - Buy a Molotov - molotov_give <player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give Molotov(s) to a player, a team, or everyone - molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status) - molotov_override [0|1] - Enable/disable the standard grenade override (If no arguments, show the status)
* Cvars - molotov_enabled <0|1> - (Default: 1) Enable(1)/disable(0) the plugin - molotov_price <N> - (Default: 300) Set the Molotov price (Counter-Strike only) - molotov_damage <N> - (Default: 50.0) Set the damage done by initial Molotov explosion - molotov_radius <N> - (Default: 150.0) Set the radius of Molotov damage - molotov_firetime <N> - (Default: 6) Duration (in seconds) of fire effects, sounds, etc. - molotov_firedamage <N> - (Default: 3) Amount of damage done by fire effects (every 0.2 secs) - molotov_ff <0|1|-1|-2> - (Default: 1) Set Molotov friendly fire status (Was molotov_tk) * 0 - Disable friendly fire for Molotovs (regardless of mp_friendlyfire) * 1 - Enable friendly fire for Molotovs (regardless of mp_friendlyfire) * -1 - Use mp_friendlyfire value * -2 - Check bit 5 (decimal: 16) of mp_teamplay (DOD and TFC only) - molotov_override_he <0|1> - (Default: 0) Override the mod's standard grenade automatically with Molotov (Was molotov_tempoverride) - molotov_max <N> - (Default: 1) Limit carried Molotovs to this amount * (Recommended: CSTRIKE: ≤ 10; DOD: ≤ 9; TFC: ≤ 4;) - molotov_buyzone <0|1> - (Default: 1) Limit Molotov buying to buyzone (Counter-Strike only) - molotov_menu <0|1> - (Default: 0) Enable menu at beginning of each round (Was amx_molotovmenu)
* Required Modules: - Fakemeta - Cstrike (Counter-Strike only) - Csx (Counter-Strike only) - Dodfun (Day of Defeat only) - Dodx (Day of Defeat only) - Tfcx (Team Fortress Classic only) - Engine (TFC with debugging only)
* Changelog/Credit: - DynamicBits * Version 3.30 (2014-04-13) - (Beta) Day of Defeat support was added - (Beta) Team Fortress Classic support was added - (Untested) Stats logging support was added - New values for molotov_ff were added - Friction/velocity after explosion was adjusted for realism - Bottle breaking sound was added - Molotov suicides no longer reward extra score points - Frag count calculations were fixed - Money calculations were fixed - Override no longer sets a negative number of Molotovs - Default price was changed to match standard grenades - Console text now goes to correct console - Text (non-VGUI) buy menu support was removed - Converted fun functions to fakemeta_util functions (removed fun include) - Various optimizations - A few typos were fixed * Version 3.20 (2008-11-20) - My first public release - Finally tracked down and fixed the intermittent crashing problem (I hope!) - Modified default damage values - molotov_cocktail/molotov_override commands now change settings *or* display status - Broken Molotov model stays closer to the explosion (looks more realistic) - Task IDs are now guaranteed to be unique - Modified anti-lag calculations to be more accurate (less likely to lag) - Changed amx_molotovmenu CVAR to molotov_menu - Changed molotov_tk CVAR to molotov_ff - Changed molotov_tempoverride CVAR to molotov_override_he - Preparation for support of mods other than Counter-Strike - Fixed lots of coding mistakes - Optimized several sections of code - Corrected grammar/typos - Clean up code (Removed unused code/unhelpful comments, fixed formatting, and semicolons!) - Raffe (CantShoot) * (Unversioned release) - Originally fixed plugin to run on Linux servers - Added optional menu to purchase Molotov cocktails each round - Moved models and sounds into proper molotov/ subdirectories - Fixed Molotovs not being reset upon player disconnect - (Almost) fixed Molotovs not being removed for new round - Added @all/@ct/@t arguments to molotov_give command - Changed some models/sound - [ --<-@ ] Black Rose * Version 3.0-3.1c ? - Unknown changes - SAMURAI * Original plugin author
*/
#pragma semicolon 1
// Uncomment only the define that applies to your mod #define CSTRIKE //#define DOD //#define TFC
// Uncomment the following line to enable debug logging. //#define MOLOTOV_DEBUG
#include <amxmodx> #include <amxmisc> //#include <fakemeta> // (runtime only) #include <fakemeta_util> #if defined CSTRIKE #include <cstrike> #include <csx> // Used only for custom_weapon_* functions #endif #if defined DOD #include <dodfun> #include <dodx> // Used only for custom_weapon_* functions #endif #if defined TFC #include <tfcx> #include <engine> // Used only with debugging enabled #endif
// If you really want to same some memory and know you won't have 32 players, you can change this. #define MAX_PLAYERS 32 #define ADMIN_ACCESS ADMIN_KICK
#define ANTI_LAGG 7 // Defines max calculations before a flame is spawned without check if on ground // This is to prevent lag at really narrow ents where you could end up with 400 calculations per flame. Suggested: <= 10
#define MOLOTOV_HARD_LIMIT 10 // Maximum Molotov cocktails this code is capable of handling without bugs (per player)
#define ID_TO_INDEX(%0) %0 - 1 // Use this macro rather than dim the arrays with 33
#define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2 // Choices to look for with optional menu
// Task IDs #define MOLOTOV_TASKID_RESET 1000 // Set g_bReset to false after a short delay (task is TFC only) #define MOLOTOV_TASKID_OFFSET MOLOTOV_HARD_LIMIT // These task IDs are dynamically set per-Molotov #define MOLOTOV_TASKID_BASE1 2000 // By default, with 32 players, task ids #define MOLOTOV_TASKID_BASE2 MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // from 2000 to 2959 can #define MOLOTOV_TASKID_BASE3 MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // potentially be used used.
#define TEAM_UNASSIGNED 0 #define TEAM_ONE 1 #define TEAM_TWO 2 #define TEAM_THREE 3 #define TEAM_FOUR 4 #define MC_TFC_PC_CIVILIAN 11 // Temporary workaround for AMXX bug 6042
new const g_PLUGIN[] = "Molotov Cocktail"; new const g_AUTHORS[] = "DynamicBits"; new const g_VERSION[] = "3.30";
new pEnabled; // Pointer to molotov_enabled new pMlDamage; // Pointer to molotov_damage new pMlRadius; // Pointer to molotov_radius new pFireTime; // Pointer to molotov_firetime new pOverride; // Pointer to molotov_override_he new pMFF; // Pointer to molotov_ff new pFriendlyFire; // Pointer to mp_friendlyfire new pFireDmg; // Pointer to molotov_firedamage new pMaxMolotovs; // Pointer to molotov_max #if defined DOD || defined TFC new pTeamPlay; // Pointer to mp_teamplay #endif #if defined CSTRIKE new pBuyZone; // Pointer to molotov_buyzone new pMolotovMenu; // Pointer to molotov_menu new pPrice; // Pointer to molotov_price
new g_msgScoreInfo; // ScoreInfo message ID #endif new g_msgDeathMsg; // DeathMsg message ID
new g_NumMolotov[MAX_PLAYERS]; // How many Molotovs each player has #if defined CSTRIKE new bool:g_bRestarted; // Reset Molotovs after first round restart #endif new g_MaxPlayers; // Max players (calculated at runtime to make loops more efficient) new g_wpnMolotov; // Custom weapon ID new bool:g_bReset; // Reset and stop explosions after round ends; Stop reset_tasks() from getting called once per player
new g_iFireSprite, g_iSmokeSprite[2]; // Handles to the precached sprites new g_iMolotovOffset[MAX_PLAYERS]; // Offset used for a player's task ID
// The Pawn compiler does not optimize the DATA section. Any string that appears multiple times should be optimized with a global constant. // *Unused* constants do not affect the compiled size, however they create compiler warnings. (#pragma unused is a quick fix for the warnings.) new const EVENT_ROUND_END[] = "event_round_end"; #if defined CSTRIKE new const BUY_MOLOTOV[] = "buy_molotov"; new const WEAPON_NAME[] = "weapon_smokegrenade"; #endif #if defined DOD new const WEAPON_HANDGRENADE[] = "weapon_handgrenade"; new const WEAPON_STICKGRENADE[] = "weapon_stickgrenade"; #endif #if defined DOD || defined TFC new const HUDTEXT[] = "HudText"; #endif #if defined CSTRIKE || defined TFC new const TEXTMSG[] = "TextMsg"; #endif
// Check for outdated tfcconst.inc file (and likely outdated AMX Mod X core/modules). // My patch for AMXX bug 6042 was accepted, but I think I'll wait for a new final release of AMXX to enable this check. // In the meantime, I created the MC_TFC_PC_CIVILIAN define. //#if defined TFC && TFC_PC_CIVILIAN != 11 // TFC_PC_CIVILIAN was (incorrectly) 10 in older versions // #error TFC_PC_CIVILIAN != 11. Update your tfcconst.inc include file. Get the latest AMX Mod X snapshots at www.amxmodx.org/snapshots.php //#endif
// Initialize the plugin public plugin_init() {
register_plugin(g_PLUGIN, g_VERSION, g_AUTHORS); server_print("[MC] ---- Molotov Cocktail %s loaded ----", g_VERSION); register_cvar("MolotovCocktail", g_VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#if defined CSTRIKE register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");
#if defined MOLOTOV_DEBUG register_clcmd("molotov_menutest", "show_molotov_menu"); #endif
register_clcmd("say /molotov", BUY_MOLOTOV); register_clcmd("say molotov", BUY_MOLOTOV); #endif register_concmd("molotov_give", "molotov_give", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give free Molotov cocktails"); register_concmd("molotov_override", "molotov_override", ADMIN_ACCESS, "[0|1] - Enable/disable the standard grenade override (If no arguments, show the status)"); register_concmd("molotov_cocktail", "molotov_cocktail", ADMIN_ACCESS, "[0|1] - Enable/disable the plugin (If no arguments, show the status)");
pEnabled = register_cvar("molotov_enabled", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pOverride = register_cvar("molotov_override_he", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pFireTime = register_cvar("molotov_firetime", "6", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pMFF = register_cvar("molotov_ff", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pFriendlyFire = register_cvar("mp_friendlyfire", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); #if defined CSTRIKE pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); pPrice = register_cvar("molotov_price", "300", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); #endif #if defined DOD pTeamPlay = register_cvar("mp_teamplay", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); #endif #if defined TFC pTeamPlay = register_cvar("mp_teamplay", "21", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY); #endif
register_event("DeathMsg", "event_deathmsg", "a", "2>0"); // For some reason, arg2 (Victim) is sometimes -1 (at least in TFC on Windows HLDS with FoxBot). #if defined CSTRIKE || defined DOD register_event("CurWeapon", "event_curweapon", "be", "1=1"); register_event("HLTV", "event_new_round", "a", "1=0", "2=0"); // cstrike/dod new round; So far, I haven't found a TFC equivalent #endif #if defined CSTRIKE register_event(TEXTMSG, "event_gamerestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in"); #endif #if defined DOD register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&VICTORY"); // Sent once per player on round end #endif #if defined TFC // Since TFC doesn't have any generic end of round event/message, specific messages need to be caught for certain maps. // Maps that don't have traditional rounds (2fort, badlands, casbah, crossover2, cz2, ravelin, skate2, well, etc.) don't apply here. // All of the default maps are accounted for. If there is demand for specific custom maps, I will add the appropriate message(s). new sCurrentMap[32]; get_mapname(sCurrentMap, charsmax(sCurrentMap)); if (!strcmp(sCurrentMap, "avanti")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#italy_endround_win"); // Was Avanti originally called Italy? } else if ((!strcmp(sCurrentMap, "dustbowl")) || (!strcmp(sCurrentMap, "castleargh")) || (!strcmp(sCurrentMap, "castleargh2"))) { register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_one"); // Technically these are "stages," not "rounds" register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_two"); //register_event(TEXTMSG, "event_round_end", "b", "2=#dustbowl_blue_caps"); // The map ends after this cap } else if (!strcmp(sCurrentMap, "epicenter")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#dblmint_you_capped_flag"); // dblmint?! } else if (!strcmp(sCurrentMap, "flagrun")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&you won this round!"); } else if (!strcmp(sCurrentMap, "hunted")) { register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#hunted_target_killed"); } else if (!strcmp(sCurrentMap, "push")) { register_event(TEXTMSG, EVENT_ROUND_END, "b", "2&_netname_scores"); } else if (!strcmp(sCurrentMap, "rock2")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=1 . . .^n"); } else if (!strcmp(sCurrentMap, "warpath")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_red_wins"); register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_blue_wins"); // ---------- Custom Maps ---------- } else if (!strcmp(sCurrentMap, "castleargh3")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&You have secured"); // This works for all four stages } else if (!strcmp(sCurrentMap, "hwguyz2")) { register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#2fort_you_capped_flag"); register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&Time Ran Out"); }
//#if defined MOLOTOV_DEBUG //register_event(TEXTMSG, "event_textmsg_a", "a"); //register_event(TEXTMSG, "event_textmsg_b", "b"); //register_event(HUDTEXT, "event_hudtext_a", "a"); //register_event(HUDTEXT, "event_hudtext_b", "b"); //#endif #endif
#if defined CSTRIKE register_logevent(EVENT_ROUND_END, 2, "1=Round_End"); #endif
register_forward(FM_EmitSound, "fw_emitsound"); #if defined TFC register_forward(FM_SetModel, "fw_setmodel_post", 1); #endif
g_MaxPlayers = get_maxplayers();
#if defined CSTRIKE g_msgScoreInfo = get_user_msgid("ScoreInfo"); #endif g_msgDeathMsg = get_user_msgid("DeathMsg");
g_wpnMolotov = custom_weapon_add("molotov", 0, "molotov"); // I can hardly find any documentation or sample code for this. I have no // idea if I'm using it correctly or not. I'm not even sure what it affects. }
// These are primarily for catching messages in TFC to add custom round end triggers. /* #if defined MOLOTOV_DEBUG public event_textmsg_a() { new sArg2[64]; read_data(2, sArg2, charsmax(sArg2)); client_print(0, print_chat, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2); console_print(0, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2); }
public event_textmsg_b() { new sArg2[64]; read_data(2, sArg2, charsmax(sArg2));
client_print(0, print_chat, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2); console_print(0, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2); }
public event_hudtext_a() { new sArg1[64]; read_data(1, sArg1, charsmax(sArg1));
client_print(0, print_chat, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1)); console_print(0, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1)); }
public event_hudtext_b() { new sArg1[64]; read_data(1, sArg1, charsmax(sArg1));
client_print(0, print_chat, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1)); console_print(0, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1)); } #endif */
// Precache models and sound(s) public plugin_precache() {
g_iFireSprite = precache_model("sprites/flame.spr");
g_iSmokeSprite[0] = precache_model("sprites/black_smoke3.spr"); #if defined DOD g_iSmokeSprite[1] = g_iSmokeSprite[0]; // steam1.spr shows a black background in dod #else g_iSmokeSprite[1] = precache_model("sprites/steam1.spr"); #endif
#if defined CSTRIKE || defined DOD precache_model("models/molotov/p_molotov.mdl"); precache_model("models/molotov/v_molotov.mdl"); #endif precache_model("models/molotov/w_molotov.mdl"); precache_model("models/molotov/w_broke_molotov.mdl");
precache_sound("molotov/molotov_fire.wav");
}
// Reset Molotovs so that a new player doesn't have any public client_disconnect(id) { g_NumMolotov[ID_TO_INDEX(id)] = 0; }
// Catch the first impact of the Molotov and start the sound/explosion // A Molotov cocktail should "explode" on impact, not after a set time. public fw_emitsound(ent, channel, sample[]) { #if defined CSTRIKE if (equal(sample[8], "grenade_hit", 11)) { #else #if defined DOD || defined TFC // DOD: debris/bustglass2.wav and debris/bustglass1.wav are played for breaking glass, but ent is not the grenade, so Molotovs "disappear" (This is a bug in this plugin) // A fix would be to use FM_Touch or Ham_Touch or register_touch instead of FM_EmitSound if (equal(sample[8], "grenade_hit", 11)) { #endif #endif
new sModel[32]; pev(ent, pev_model, sModel, charsmax(sModel));
// Depending on where the Molotov lands, the EmitSound forward may get called 50+ times. // After the first hit, the model is changed to w_broke_molotov, so this code is skipped on successive calls if (equal(sModel[15], "w_molotov.mdl")) { #if defined TFC set_pev(ent, pev_nextthink, 99999.0); // For TFC, this is about the only way I can stop the explosion. #endif // The glass breaking sound has a low range (ATTN_STATIC) so as not to be overpowering emit_sound(ent, CHAN_AUTO, "debris/glass2.wav", VOL_NORM, ATTN_STATIC, 0, PITCH_LOW);
new Float:fFriction, Float:fVelocity[3]; pev(ent, pev_friction, fFriction); fFriction *= 1.15; // Increase friction to make it look more realistic set_pev(ent, pev_friction, fFriction);
pev(ent, pev_velocity, fVelocity); fVelocity[0] *= 0.3; // Decrease velocity because friction doesn't do it all fVelocity[1] *= 0.3; fVelocity[2] *= 0.3; set_pev(ent, pev_velocity, fVelocity);
molotov_explode(ent); // Replacement for normal grenade explosion
return FMRES_SUPERCEDE; } else if (equal(sModel[15], "w_broke_molotov.")) { // "mdl" is truncated because of the array size, which is OK return FMRES_SUPERCEDE; // Don't play any sounds for bounces. } } return FMRES_IGNORED; }
// Since TFC handles grenades differently, this is roughly equivalant to event_curweapon() used by cstrike and dod. #if defined TFC public fw_setmodel_post(ent, const model[]) { if (!pev_valid(ent)) { // Check if it's a valid entity to prevent errors return FMRES_IGNORED; }
new sClassname[32]; pev(ent, pev_classname, sClassname, charsmax(sClassname));
if (!get_pcvar_num(pEnabled) || !equal(sClassname, "normalgrenade")) { return FMRES_IGNORED; }
new iOwner = pev(ent, pev_owner); if (!g_NumMolotov[ID_TO_INDEX(iOwner)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return return FMRES_IGNORED; } if (g_NumMolotov[ID_TO_INDEX(iOwner)] > 0) { // Prevent negative values g_NumMolotov[ID_TO_INDEX(iOwner)]--; }
set_pev(ent, pev_team, get_user_team(iOwner)); custom_weapon_shot(g_wpnMolotov, iOwner);
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
return FMRES_HANDLED; } #endif
// When the player changes weapons to the Molotov, update the model #if defined CSTRIKE || defined DOD public event_curweapon(id) {
if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) { return PLUGIN_CONTINUE; }
if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return return PLUGIN_CONTINUE; }
new iWeaponID = get_user_weapon(id, _, _); #if defined CSTRIKE if (iWeaponID != CSW_SMOKEGRENADE) { #else // elseif *should* work, but there is a bug in the compiler #if defined DOD // current weapon is never set to DODW_MILLS_BOMB in this event; only DODW_HANDGRENADE/DODW_STICKGRENADE if ((iWeaponID != DODW_HANDGRENADE) && (iWeaponID != DODW_STICKGRENADE)) { #endif #endif return PLUGIN_CONTINUE; }
set_pev(id, pev_viewmodel2, "models/molotov/v_molotov.mdl"); // View model (First person) *model2 doesn't require allocating the string set_pev(id, pev_weaponmodel2, "models/molotov/p_molotov.mdl"); // Player model (Third person)
#if defined DOD // I think 3 is correct, but it looks strange.. set_pev(id, pev_weaponanim, 3); // 0: "idle"; 1: "pullpin"; 2: "throw"; 3: "deploy" #endif
return PLUGIN_CONTINUE; } #endif
// Reset Molotovs on death public event_deathmsg() { #if defined MOLOTOV_DEBUG log_amx("[MC] ========== DeathMsg ========== K(%d) V(%d)", read_data(1), read_data(2)); #endif g_NumMolotov[ID_TO_INDEX(read_data(2))] = 0; }
// cstrike only #if defined CSTRIKE public event_gamerestart() { #if defined MOLOTOV_DEBUG log_amx("[MC] ========== Game Restart =========="); #endif g_bRestarted = true; } #endif
// cstrike, dod, and tfc will all call this once per player on round end public event_round_end() { #if defined MOLOTOV_DEBUG log_amx("[MC] ========== Round End =========="); #endif
if (g_bReset == false) { reset_tasks(); g_bReset = true; #if defined TFC set_task(2.0, "cancel_reset", MOLOTOV_TASKID_RESET); // TFC won't call event_new_round, so do that stuff here instead #endif } }
// cstrike and dod will call this once per round, but TFC won't. #if defined CSTRIKE || defined DOD public event_new_round(id) { #if defined MOLOTOV_DEBUG log_amx("[MC] ========== New Round =========="); #endif g_bReset = false; // Stop blocking
if (!get_pcvar_num(pEnabled)) { return PLUGIN_CONTINUE; }
reset_tasks(); // This probably isn't needed anymore, but it shouldn't hurt anything
#if defined CSTRIKE if (get_pcvar_num(pMolotovMenu)) { if (get_pcvar_num(pOverride)) { client_print(id, print_center, "Molotov cocktails will replace purchased HE grenades"); } else { show_molotov_menu(id); } }
// For cstrike only, make sure the player didn't quickly purchase a Molotov before the first actual round if (g_bRestarted) { arrayset(g_NumMolotov, 0, sizeof(g_NumMolotov)); // Reset everyone to zero Molotovs g_bRestarted = false; } #endif
if (get_pcvar_num(pOverride)) { set_molotovs(); } else { reset_molotovs(); }
return PLUGIN_CONTINUE; } #endif
// Enable/Disable/Get status of override public molotov_override(id, level, cid) {
if (!cmd_access(id, level, cid, 1)) { // First argument (passed to molotov_override) is optional return PLUGIN_HANDLED; }
if (!get_pcvar_num(pEnabled)) { return PLUGIN_HANDLED; }
if (read_argc() == 1) { // No arguments; Display status console_print(id, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled"); return PLUGIN_HANDLED; }
new sArg[2]; read_argv(1, sArg, charsmax(sArg));
new iArg = str_to_num(sArg);
if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) { // If less than 0 or greater than 1 or not a digit console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg); return PLUGIN_HANDLED; }
if (iArg == get_pcvar_num(pOverride)) { console_print(id, "Override is already %s.", iArg ? "enabled" : "disabled"); return PLUGIN_HANDLED; }
set_pcvar_num(pOverride, iArg); console_print(id, "Override was %s.", iArg ? "enabled" : "disabled");
#if defined CSTRIKE || defined DOD if (iArg) { // If plugin is enabled (checked above) and override is enabled, set models to Molotov set_molotovs(); } else { reset_molotovs(); } #endif
return PLUGIN_HANDLED; }
// Enable/Disable/Get status of plugin public molotov_cocktail(id, level, cid) { if (!cmd_access(id, level, cid, 1)) { // First argument (passed to molotov_cocktail) is optional return PLUGIN_HANDLED; }
if (read_argc() == 1) { // No arguments; Display status console_print(id, "Plugin is currently %s. (Override:%d; MFF:%d)", get_pcvar_num(pEnabled) ? "enabled" : "disabled", get_pcvar_num(pOverride), get_pcvar_num(pMFF)); return PLUGIN_HANDLED; }
new sArg[2]; read_argv(1, sArg, charsmax(sArg));
new iArg = str_to_num(sArg);
if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) { // If less than 0 or greater than 1 or not a digit console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg); return PLUGIN_HANDLED; }
if (iArg == get_pcvar_num(pEnabled)) { console_print(id, "Plugin is already %s.", iArg ? "enabled" : "disabled"); return PLUGIN_HANDLED; }
set_pcvar_num(pEnabled, iArg); console_print(id, "Plugin was %s.", iArg ? "enabled" : "disabled");
#if defined CSTRIKE || defined DOD if (iArg && get_pcvar_num(pOverride)) { // If the plugin was enabled and override is enabled, set models to Molotov set_molotovs(); } else { reset_molotovs(); } #endif
return PLUGIN_HANDLED; }
// Handle molotov_give console command public molotov_give(id, level, cid) { if (!cmd_access(id, level, cid, 2)) { return PLUGIN_HANDLED; }
new sArg1[16], iTarget; read_argv(1, sArg1, charsmax(sArg1)); #if defined MOLOTOV_DEBUG log_amx("[MC] molotov_give sArg1[0](%s)", sArg1[0]); #endif new sAdmin[32]; get_user_name(id, sAdmin, charsmax(sAdmin)); new iGiveAmount = (get_pcvar_num(pMaxMolotovs) < MOLOTOV_HARD_LIMIT ? get_pcvar_num(pMaxMolotovs) : MOLOTOV_HARD_LIMIT);
if (sArg1[0] == '@') {
new iTargetTeam, sTeamName[32]; new Players[MAX_PLAYERS], iNum;
if (equali(sArg1[1], "all")) { iTargetTeam = 0; } else if (equali(sArg1[1], "t") || equali(sArg1[1], "al") || equali(sArg1[1], "br") || equali(sArg1[1], "b")) { // CS_TEAM_T or ALLIES/British or Blue iTargetTeam = TEAM_ONE; } else if (equali(sArg1[1], "ct") || equali(sArg1[1], "ax") || equali(sArg1[1], "r")) { // CS_TEAM_CT or AXIS or Red iTargetTeam = TEAM_TWO; #if defined TFC } else if (equali(sArg1[1], "y")) { // Yellow iTargetTeam = TEAM_THREE; } else if (equali(sArg1[1], "g")) { // Green iTargetTeam = TEAM_FOUR; #endif }
get_players(Players, iNum, "ach"); // alive, no bots, no HLTV
for (new i = 0; i < iNum; ++i) { iTarget = Players[i];
if ((iTargetTeam == 0) || (get_user_team(iTarget) == iTargetTeam)) { g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
#if defined CSTRIKE fm_give_item(iTarget, WEAPON_NAME); cs_set_user_bpammo(iTarget, CSW_SMOKEGRENADE, iGiveAmount); #endif #if defined DOD // TODO - This sets the count, but it is not immediately updated on the HUD switch(get_user_team(iTarget)) { case ALLIES: { // (or British) fm_give_item(iTarget, WEAPON_HANDGRENADE); dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount); } case AXIS: { fm_give_item(iTarget, WEAPON_STICKGRENADE); dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount); } } #endif #if defined TFC new iClass = pev(iTarget, pev_playerclass); if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) { // No unselected/spectator, scout, or civilian tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount); // Requires 1.8.3-dev-hg185 or newer } #endif #if defined CSTRIKE emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); #endif #if defined DOD emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); // "items/weaponpickup.wav" could work too, I suppose #endif #if defined TFC // Shotgun pumping sound for picking up grenades... That's how TFC does it! emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); #endif } }
switch(iTargetTeam) { case 0: { sTeamName = "everyone"; } case TEAM_ONE: { #if defined CSTRIKE sTeamName = "all terrorists"; #endif #if defined DOD sTeamName = "all allies"; // TODO - Allies or British #endif #if defined TFC sTeamName = "all blue"; // I *could* pull the team1_name value from the info_tfdetect entity (but only for some maps?) #endif } case TEAM_TWO: { #if defined CSTRIKE sTeamName = "all ct's"; #endif #if defined DOD sTeamName = "all axis"; #endif #if defined TFC sTeamName = "all red"; } case TEAM_THREE: { sTeamName = "all yellow"; } case TEAM_FOUR: { sTeamName = "all green"; #endif } } client_print(0, print_chat, "ADMIN %s has given %s %d Molotov cocktails!", sAdmin, sTeamName, iGiveAmount);
} else {
iTarget = cmd_target(id, sArg1, 6);
if (!is_user_connected(iTarget) || !is_user_alive(iTarget)) { return PLUGIN_HANDLED; }
g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
#if defined CSTRIKE fm_give_item(iTarget, WEAPON_NAME); cs_set_user_bpammo(iTarget, CSW_SMOKEGRENADE, iGiveAmount); #endif #if defined DOD switch(get_user_team(iTarget)) { case ALLIES: { // (or British) fm_give_item(iTarget, WEAPON_HANDGRENADE); dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount); } case AXIS: { fm_give_item(iTarget, WEAPON_STICKGRENADE); dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount); } } #endif #if defined TFC new iClass = pev(iTarget, pev_playerclass); if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) { // No unselected/spectator, scout, or civilian tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount); // Requires 1.8.3-dev-hg185 or newer } #endif #if defined CSTRIKE emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); #endif #if defined DOD emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); // "items/weaponpickup.wav" could work too, I suppose #endif #if defined TFC // Shotgun pumping sound for picking up grenades... That's how TFC does it! emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); #endif
client_print(iTarget, print_chat, "ADMIN %s has given you %d Molotov cocktails!", sAdmin, iGiveAmount);
} return PLUGIN_HANDLED; }
// Handle the /molotov command and molotov menu #if defined CSTRIKE public buy_molotov(id) {
if (!get_pcvar_num(pEnabled)) { return PLUGIN_HANDLED; }
//if (get_pcvar_num(pOverride)) { // client_print(id, print_center, "Just buy a HE grenade and get Molotov automatically!"); // return PLUGIN_HANDLED; //}
if (!is_user_alive(id)) { client_print(id, print_center, "You can't buy Molotov cocktails because you are dead."); return PLUGIN_HANDLED; }
if (!cs_get_user_buyzone(id) && get_pcvar_num(pBuyZone)) { client_print(id, print_center, "You are not in a buyzone."); return PLUGIN_HANDLED; }
new iMoney = cs_get_user_money(id);
if (iMoney < get_pcvar_num(pPrice)) { client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail."); return PLUGIN_HANDLED; }
if (!g_NumMolotov[ID_TO_INDEX(id)] && user_has_weapon(id, CSW_SMOKEGRENADE)) { if (get_pcvar_num(pOverride)) { g_NumMolotov[ID_TO_INDEX(id)] = cs_get_user_bpammo(id, CSW_SMOKEGRENADE); // If the user buys one from the VGUI menu with the override enabled, this updates g_NumMolotov } else { client_print(id, print_center, "You already have an HE Grenade."); return PLUGIN_HANDLED; } }
if (g_NumMolotov[ID_TO_INDEX(id)] == get_pcvar_num(pMaxMolotovs)) { if (g_NumMolotov[ID_TO_INDEX(id)] == 1) { client_print(id, print_center, "You already have a Molotov cocktail."); } else { client_print(id, print_center, "You already have %d Molotov cocktails.", g_NumMolotov[ID_TO_INDEX(id)]); } return PLUGIN_HANDLED; }
cs_set_user_money(id, iMoney - get_pcvar_num(pPrice)); fm_give_item(id, WEAPON_NAME); cs_set_user_bpammo(id, CSW_SMOKEGRENADE, ++g_NumMolotov[ID_TO_INDEX(id)]);
client_print(id, print_chat, "You got a Molotov cocktail!");
return PLUGIN_HANDLED; } #endif
// Just before the grenade is thrown, change the model #if defined CSTRIKE || defined DOD public grenade_throw(id, ent, wid) { #if defined CSTRIKE if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_SMOKEGRENADE) { #else #if defined DOD // current weapon can be DODW_MILLS_BOMB in this forward, but not in CurWeapon if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || ((wid != DODW_HANDGRENADE) && (wid != DODW_STICKGRENADE) && (wid != DODW_MILLS_BOMB))) { #endif #endif return PLUGIN_CONTINUE; }
if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return return PLUGIN_CONTINUE; }
if (g_NumMolotov[ID_TO_INDEX(id)] > 0) { // Prevent negative values g_NumMolotov[ID_TO_INDEX(id)]--; }
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl"); set_pev(ent, pev_nextthink, 99999.0);
custom_weapon_shot(g_wpnMolotov, id);
#if defined CSTRIKE // dod sets the team, cstrike doesn't, TFC sets this in fw_setmodel_post() set_pev(ent, pev_team, get_user_team(id)); #endif
#if defined DOD //set_pev(id, pev_weaponanim, 0); // 0:"idle"; 1:"pullpin"; 2:"throw"; 3:"deploy" #endif
return PLUGIN_HANDLED; } #endif
// Set up the explosion, sound, damage, etc. molotov_explode(ent) {
new param[7], iOrigin[3]; new Float:fOrigin[3]; new iOwner = pev(ent, pev_owner); // The broken bottle may continue to travel, but the fire will be centered around the explosion site, marked by this temporary info_target entity. new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));
pev(ent, pev_origin, fOrigin);
#if defined MOLOTOV_DEBUG log_amx("[MC] molotov_explode ent(%d) owner(%d) ent2(%d) -----", ent, iOwner, ent2); #endif
param[0] = ent; param[1] = ent2; param[2] = iOwner; param[3] = pev(ent, pev_team); param[4] = iOrigin[0] = floatround(fOrigin[0]); param[5] = iOrigin[1] = floatround(fOrigin[1]); param[6] = iOrigin[2] = floatround(fOrigin[2]);
engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");
random_fire(iOrigin, ent2); radius_damage2(iOwner, param[3], fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST, true);
// If the round ends because of damage inflicted by the initial blast (in the previous line of code), skip any further Molotov effects. // g_bReset may already be set, so it is safe to check it at this point. if (g_bReset == true) { set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_KILLME); // Remove the Molotov and later cancel the explosion return PLUGIN_HANDLED; }
new Float:FireTime = get_pcvar_float(pFireTime);
if (++g_iMolotovOffset[ID_TO_INDEX(iOwner)] == MOLOTOV_HARD_LIMIT) { g_iMolotovOffset[ID_TO_INDEX(iOwner)] = 0; }
set_task(0.2, "fire_damage", MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime / 0.2, floatround_floor)); set_task(1.0, "fire_sound", MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime) - 1); // This task removes the broken Molotov and "info_target" entity once molotov_firetime has expired set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7);
return PLUGIN_CONTINUE; }
// Since there isn't a reliable new round trigger in TFC, a task is created at round end that calls this function after a delay #if defined TFC public cancel_reset() { g_bReset = false; } #endif
// Make fire sounds public fire_sound(param[]) { emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); }
// Remove Molotov entities public fire_stop(param[]) { if (pev_valid(param[0])) { set_pev(param[0], pev_flags, pev(param[0], pev_flags) | FL_KILLME); } // Molotov entity if (pev_valid(param[1])) { set_pev(param[1], pev_flags, pev(param[1], pev_flags) | FL_KILLME); } // info_target entity }
// Call visual effect and damage functions public fire_damage(param[]) {
new iOrigin[3], Float:fOrigin[3]; iOrigin[0] = param[4]; iOrigin[1] = param[5]; iOrigin[2] = param[6];
random_fire(iOrigin, param[1]); // Visual effect
IVecFVec(iOrigin, fOrigin); radius_damage2(param[2], param[3], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, false); // Actual damage }
// There is a radius_damage() in engine, so this was renamed. stock radius_damage2(iAttacker, iAttackerTeam, Float:fOrigin[3], Float:fDamage, Float:fRange, iDmgType, bool:bCalc = true) {
new Float:pOrigin[3], Float:fDist, Float:fTmpDmg; new i, iFF = get_pcvar_num(pMFF);
if (iFF == -1) { // Obey mp_friendlyfire iFF = get_pcvar_num(pFriendlyFire); #if defined DOD || defined TFC } else if (iFF == -2) { // Obey mp_teamplay (bit 5) new iTeamPlay = get_pcvar_num(pTeamPlay); if (iTeamPlay & (1 << 4)) { // bit 5 (16 = teammates take no damage from explosive weaponfire) iFF = 0; } #endif } // else, leave it at 0 or 1
while (i++ < g_MaxPlayers) { if (!is_user_alive(i)) { continue; }
#if defined TFC if ((iFF == 0) && ((iAttackerTeam == get_user_team(i)) || (tfc_is_team_ally(iAttackerTeam, get_user_team(i))))) { // TODO: tfc_is_team_ally is broken in AMX Mod X #else if ((iFF == 0) && (iAttackerTeam == get_user_team(i))) { #endif continue; }
pev(i, pev_origin, pOrigin); fDist = get_distance_f(fOrigin, pOrigin);
if (fDist > fRange) { continue; }
if (bCalc) { fTmpDmg = fDamage - (fDamage / fRange) * fDist; } else { fTmpDmg = fDamage; }
if (floatround(fTmpDmg) > 0) { // This eliminated the "[CSX] Invalid damage 0" error custom_weapon_dmg(g_wpnMolotov, iAttacker, i, floatround(fTmpDmg), 0); }
if (pev(i, pev_health) <= fTmpDmg) { kill(iAttacker, i, iAttackerTeam); } else { fm_fakedamage(i, "molotov", fTmpDmg, iDmgType); } } // At this point, i is one higher than the highest possible player ID, so this loop only affects non-player entities while ((i = engfunc(EngFunc_FindEntityInSphere, i, fOrigin, fRange))) { // Extra parentheses fix warning 211: possibly unintended assignment if (pev(i, pev_takedamage)) { if (bCalc) { pev(i, pev_origin, pOrigin); fTmpDmg = fDamage - (fDamage / fRange) * get_distance_f(fOrigin, pOrigin); } else { fTmpDmg = fDamage; } fm_fakedamage(i, "molotov", fTmpDmg, iDmgType); } } }
// This stock only creates the visual effect. It does not handle any damage. // I tried using TE_FIREFIELD, but I can't make it look good in dod. stock random_fire(Origin[3], ent) {
static iRange, iOrigin[3], g_g, i;
iRange = get_pcvar_num(pMlRadius);
for (i = 1; i <= 5; i++) {
g_g = 1;
iOrigin[0] = Origin[0] + random_num(-iRange, iRange); iOrigin[1] = Origin[1] + random_num(-iRange, iRange); iOrigin[2] = Origin[2]; iOrigin[2] = ground_z(iOrigin, ent);
while (get_distance(iOrigin, Origin) > iRange) { // If iOrigin is too far away, recalculate its position
iOrigin[0] = Origin[0] + random_num(-iRange, iRange); iOrigin[1] = Origin[1] + random_num(-iRange, iRange); iOrigin[2] = Origin[2];
if (++g_g >= ANTI_LAGG) { iOrigin[2] = ground_z(iOrigin, ent, 1); } else { iOrigin[2] = ground_z(iOrigin, ent); } }
new rand = random_num(5, 15);
message_begin(MSG_BROADCAST, SVC_TEMPENTITY); write_byte(TE_SPRITE); write_coord(iOrigin[0]); // Position write_coord(iOrigin[1]); write_coord(iOrigin[2] + rand * 5); write_short(g_iFireSprite); // Sprite index write_byte(rand); // Scale write_byte(100); // Brightness message_end(); }
// One smoke puff for each call to random_fire, regardless of number of flames message_begin(MSG_BROADCAST, SVC_TEMPENTITY); write_byte(TE_SMOKE); write_coord(iOrigin[0]); // Position write_coord(iOrigin[1]); write_coord(iOrigin[2] + 120); write_short(g_iSmokeSprite[random_num(0, 1)]); // Sprite index write_byte(random_num(10, 30)); // Scale write_byte(random_num(10, 20)); // Framerate message_end();
}
// Stop all visual effect/physical damage tasks stock reset_tasks() { #if defined MOLOTOV_DEBUG new tmpdbgid; #endif for (new i; i < g_MaxPlayers; i++) { // for 0..31 for (new o; o < MOLOTOV_TASKID_OFFSET; o++) { if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) { remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o); #if defined MOLOTOV_DEBUG tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o; log_amx("[MC] %d exists. ----------==========----------", tmpdbgid); #endif }
if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) { remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o); #if defined MOLOTOV_DEBUG tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o; log_amx("[MC] %d exists. ----------==========----------", tmpdbgid); #endif } // The third task for each Molotov is not stopped so it can remove the Molotov/info_target entities. } } }
// This function handles the killing and scoring. // iKillerTeam is stored because the killer can disconnect before the Molotov kills someone, leading to inaccurate scoring stock kill(iKiller, iVictim, iKillerTeam) {
//TFC: DeathMsg, ScoreInfo, ScoreInfo // One ScoreInfo for killer and one for victim (order varies) //DOD: DeathMsg, ScoreShort, Frags // ScoreShort=victim, Frags=killer // CS: DeathMsg, Money, ScoreInfo, ScoreInfo
// Scoreboard // CSTRIKE: Score Deaths // DMC: Frags Deaths // DOD: Score Kills Deaths // HL: Score Deaths // HLOF: Kills Deaths // TFC: Score Deaths // Ricochet: Points /* ----- Attacker ----- ------ Victim ------ Score Deaths Kills Score Death Kills CS kill +1 - N/A - +1 N/A CS team kill -1 - N/A - +1 N/A CS suicide -1 +1 N/A --------------------- CS detonate/defuse +3 - N/A --------------------- DOD kill - - +1 - +1 - DOD team kill - - - - +1 - (mp_tkpenalty handles punishment) DOD suicide - +1 - --------------------- DOD cap +1 - - --------------------- TFC kill +1 - N/A - +1 N/A TFC team kill -1 - N/A - +1 N/A TFC suicide -1 +1 N/A --------------------- TFC cap/control varies - N/A --------------------- */
// ------------------------------------------------------------------------------------------------- DeathMsg (CS, DOD, TFC) // DeathMsg - Triggers HUD message and player console message // DOD and CSTRIKE have different formats for DeathMsg; most other mods should be default message_begin(MSG_ALL, g_msgDeathMsg, {0,0,0}, 0); write_byte(iKiller); // Killer ID write_byte(iVictim); // Victim ID #if defined CSTRIKE write_byte(0); // Is Headshot? #endif #if defined DOD write_byte(0); // Weapon ID - These don't match the DODW_* constants, and a custom weapon ID does not work, so we use "world" #else write_string("molotov"); // Truncated Weapon Name #endif message_end(); // ------------------------------------------------------------------------------------------------- /DeathMsg (CS, DOD, TFC)
// This block of code actually kills the user (silently - DeathMsg was already created) new iVictimTeam = get_user_team(iVictim); new iMsgBlock = get_msg_block(g_msgDeathMsg); // Store original block value set_msg_block(g_msgDeathMsg, BLOCK_ONCE); // Start blocking DeathMsg
#if defined CSTRIKE
new iKillerFrags = get_user_frags(iKiller); new iVictimFrags = get_user_frags(iVictim);
// TFC and CS scoring are mostly the same. See TFC comment. (I did most of my testing with TFC first) if (iKiller != iVictim) { fm_set_user_frags(iVictim, iVictimFrags + 1); // Add frag that user_kill() will remove }
if (iKillerTeam != iVictimTeam) { iKillerFrags++; // Killer's Score = Score + 1 } else { iKillerFrags--; // Killer's Score = Score - 1 } fm_set_user_frags(iKiller, iKillerFrags);
// CSTRIKE Results -------------------------------------------------------------------------------------------------- // DeathMsg ScoreInfoMsg KScore KDeath VScore VDeath Internally //user_kill(iVictim, 0); // Yes Yes (Victim) 0 0 -1 +1 Everything matches //user_kill(iVictim, 1); // Yes Yes (Victim) 0 0 -1 +1 VScore is different internally (unchanged) //user_silentkill(iVictim); // No Yes (Victim) 0 0 -1 +1 VScore is different internally (unchanged)
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes (Victim) 0 0 -1 +1 Everything matches //fm_user_kill(iVictim, 0); // Yes Yes (Victim) 0 0 -1 +1 Everything matches //fm_user_kill(iVictim, 1); // Yes Yes (Victim) 0 0 0 +1 Everything matches (fm_user_kill adds 1 to VScore)
// user_silentkill() blocks the DeathMsg, but it doesn't update the scoreboard properly // fm_user_kill() updates the scoreboard properly, but generates a DeathMsg with Victim=Killer, so we have to block that. #endif
#if defined TFC new iVictimFrags = tfc_get_user_frags(iVictim);
// TFC treats every type of kill in code as a suicide and takes away 1 frag from the victim. // After *extensive* testing, I found that the easiest solution is to increment the victim frags // (stored in pdata) beforehand and let the game decrement it and send out ScoreInfo messages. // This applies to *victims* of team kills as well as normal kills. Suicides still lose a frag. if (iKiller != iVictim) { tfc_set_user_frags(iVictim, iVictimFrags + 1); }
if ((iKillerTeam == iVictimTeam) || tfc_is_team_ally(iKillerTeam, iVictimTeam)) { // TODO: tfc_is_team_ally() is broken in AMX Mod X tfc_set_user_frags(iKiller, get_user_frags(iKiller) - 1); // Killer's Score = Score - 1 } else { tfc_set_user_frags(iKiller, get_user_frags(iKiller) + 1); // Killer's Score = Score + 1 }
// TFC Results -------------------------------------------------------------------------- // DeathMsg ScoreInfoMsg VFrag Internally //user_kill(iVictim, 0); // Yes Yes -1 Everything matches //user_kill(iVictim, 1); // Yes Yes 0 VFrags doesn't match //user_silentkill(iVictim); // No Yes 0 VFrags doesn't match
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes -1 Everything matches //fm_user_kill(iVictim, 0); // Yes Yes -1 Everything matches //fm_user_kill(iVictim, 1); // Yes Yes -1 Everything matches
#endif
// DOD just works.. // DOD Results -------------------------------------------------------------------------------------------------- // DeathMsg ScoreShortMsg FragMsg KFrag VDeath VFrag Internally //user_kill(iVictim, 0); // Yes Yes N 0 +1 0 Everything matches //user_kill(iVictim, 1); // Yes Yes N 0 +1 0 Everything matches //user_silentkill(iVictim); // No Yes N 0 +1 0 Everything matches
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes N 0 +1 0 Everything matches //fm_user_kill(iVictim, 0); // Yes Yes N 0 +1 0 Everything matches //fm_user_kill(iVictim, 1); // Yes Yes N 0 +1 +1 OK, but VFrags shouldn't be changed
user_kill(iVictim, 0); set_msg_block(g_msgDeathMsg, iMsgBlock); // Stop blocking DeathMsg
//CSTRIKE client console messages: //Kill "Player1 killed [P0D]M0rbid Desire (2) with molotov" //TK "Player1 killed [POD]Kate_Winslet (2) with molotov" //Self "Player1 killed self with molotov"
//DOD client console messages: (DOD uses indexes instead of strings and a custom weapon name can't be sent to the player console) //Kill "Player1 killed Sgt.Moving_Target with world" //TK "Player1 killed his teammate Sgt.dontSHOOTiJUSTwannaTALK with world" //Self "Player1 killed self"
//TFC client console messages: //Kill "Player1 killed [FoX]JesseJames with molotov" //TK "Player1 killed [FoX]Barry with molotov" //Self "Player1 killed self with molotov"
// I'm not really sure if this does anything, but it seems to match the Valve wiki: https://developer.valvesoftware.com/wiki/HL_Log_Standard new sVictim[32], sVictimAuth[35], sVictimTeam[32]; get_user_name(iVictim, sVictim, charsmax(sVictim)); get_user_authid(iVictim, sVictimAuth, charsmax(sVictimAuth)); get_user_team(iVictim, sVictimTeam, charsmax(sVictimTeam)); // TERRORIST, CT, Allies, Axis, #Dustbowl_team1 (Attackers/Blue), #Dustbowl_team2 (Defenders/Red) if (iKiller == iVictim) { log_message("^"%s<%d><%s><%s>^" committed suicide with ^"molotov^"", sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam); } else if (is_user_connected(iKiller)) { new sKiller[32], sKillerAuth[35], sKillerTeam[32]; get_user_name(iKiller, sKiller, charsmax(sKiller)); get_user_authid(iKiller, sKillerAuth, charsmax(sKillerAuth)); get_user_team(iKiller, sKillerTeam, charsmax(sKillerTeam)); log_message("^"%s<%d><%s><%s>^" killed ^"%s<%d><%s><%s>^" with ^"molotov^"", sKiller, get_user_userid(iKiller), sKillerAuth, sKillerTeam, sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam); } // TODO: There currently isn't a log message for a kill by a disconnected player. The wiki doesn't show the expected format.
// ------------------------------------------------------------------------------------------------- Money (CS) #if defined CSTRIKE new iMoney; if (iKillerTeam == iVictimTeam) { iMoney = cs_get_user_money(iKiller) - 3300; // TODO - $1500 hostage kill penalty cs_set_user_money(iKiller, iMoney < 0 ? 0 : iMoney); } else { iMoney = cs_get_user_money(iKiller) + 300; cs_set_user_money(iKiller, iMoney > 16000 ? 16000 : iMoney); } #endif // ------------------------------------------------------------------------------------------------- /Money (CS) // ------------------------------------------------------------------------------------------------- ScoreInfo (CS and TFC) // ScoreInfo - Updates scoreboard on clients (actual values are changed elsewhere) // TFC - ScoreInfo messages are sent automatically after killing a player. // CS - ScoreInfo is sent automatically for the victim, but not killer. #if defined CSTRIKE message_begin(MSG_ALL, g_msgScoreInfo); // Killer ScoreInfo write_byte(iKiller); write_short(iKillerFrags); write_short(get_user_deaths(iKiller)); write_short(0); write_short(iKillerTeam); message_end(); #endif // ------------------------------------------------------------------------------------------------- /ScoreInfo (CS and TFC)
#if defined DOD // ------------------------------------------------------------------------------------------------- ScoreShort (DOD) // ScoreShort is sent by user_kill() // ------------------------------------------------------------------------------------------------- /ScoreShort (DOD) // ------------------------------------------------------------------------------------------------- Frags (DOD) if (iKillerTeam != iVictimTeam) { // Only give a frag if the player killed was an enemy (not suicide or TK) dod_set_user_kills(iKiller, dod_get_user_kills(iKiller) + 1, 1); // These natives seem to work properly. } // ------------------------------------------------------------------------------------------------- /Frags (DOD) #endif }
// Attempt to drop the passed coordinates to ground level stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {
iOrigin[2] += random_num(5, 80);
if (!pev_valid(ent)) { return iOrigin[2]; }
new Float:fOrigin[3]; IVecFVec(iOrigin, fOrigin); set_pev(ent, pev_origin, fOrigin); engfunc(EngFunc_DropToFloor, ent);
if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) { if (iRecursion >= ANTI_LAGG) { skip = 1; }
return ground_z(iOrigin, ent, skip, ++iRecursion); }
pev(ent, pev_origin, fOrigin);
return floatround(fOrigin[2]); }
// If plugin or override is disabled, reset Molotov models to original models #if defined CSTRIKE || defined DOD stock reset_molotovs() { new ent = g_MaxPlayers; #if defined CSTRIKE // TODO - My limited testing showed this code is pointless. // It has no negative effect, so I'm leaving it for the 3.30 release. // (I don't think "model" is a valid parameter.) new iOwner; while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) { iOwner = pev(ent, pev_owner); #if defined MOLOTOV_DEBUG client_print(0, print_chat, "reset_molotovs - found one Molotov! Owner(%d)", iOwner); #endif // If plugin is disabled or player owns no molotovs, reset their model if (!get_pcvar_num(pEnabled) || !g_NumMolotov[ID_TO_INDEX(iOwner)]) { engfunc(EngFunc_SetModel, ent, "models/w_smokegrenade.mdl"); } } #endif #if defined DOD new iOwner; while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) { #if defined MOLOTOV_DEBUG client_print(0, print_chat, "reset_molotovs - found one Molotov!"); #endif
iOwner = pev(ent, pev_owner); if (iOwner) { switch(get_user_team(iOwner)) { case ALLIES: { // (or British) engfunc(EngFunc_SetModel, ent, "models/w_grenade.mdl"); // Mills is the same model, so this is probably fine } case AXIS: { engfunc(EngFunc_SetModel, ent, "models/w_stick.mdl"); } } } } #endif
} #endif
// Mods that show the model before it is thrown need the model set (I think) #if defined CSTRIKE || defined DOD stock set_molotovs() { new ent = g_MaxPlayers; #if defined CSTRIKE while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_smokegrenade.mdl"))) { #if defined MOLOTOV_DEBUG client_print(0, print_chat, "set_molotovs - found one smokegrenade!"); #endif engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl"); } #endif #if defined DOD while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_grenade.mdl"))) { #if defined MOLOTOV_DEBUG client_print(0, print_chat, "set_molotovs - found one grenade!"); #endif engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl"); } ent = g_MaxPlayers; while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_stick.mdl"))) { #if defined MOLOTOV_DEBUG client_print(0, print_chat, "set_molotovs - found one stick!"); #endif engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl"); } ent = g_MaxPlayers; while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_mills.mdl"))) { #if defined MOLOTOV_DEBUG client_print(0, print_chat, "set_molotovs - found one Mills!"); #endif engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl"); } #endif
} #endif
// Show optional buy menu #if defined CSTRIKE public show_molotov_menu(id) { new menu[128]; formatex(menu, charsmax(menu), "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));
// This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well // when using 0.5 as mp_buytime. If you want to, just change the time below. show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);
return PLUGIN_HANDLED; } #endif
// Our menu function will get the player id and the key they pressed #if defined CSTRIKE public giveMolotov(id, key) {
//key will start at zero switch(key) { case 0: buy_molotov(id); //I don't think these messages are necessary. //case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail"); //default: client_print(id, print_center, "You have chosen to exit the Molotov menu"); } } #endif
// Set user frags (score) in TFC #if defined TFC stock tfc_set_user_frags(iIndex, iNewFrags) { if (is_linux_server()) { set_pdata_int(iIndex, 76, iNewFrags); // real_frags = 76 (on Linux) Required! } else { set_pdata_int(iIndex, 77, iNewFrags); // real_frags = 77 (on Windows) Required? } // Is there a mac version?
// As far as I can tell, real_frags is what should be set, and something copies it to m_iClientFrags. //set_pdata_int(iIndex, 643, iNewFrags); // m_iClientFrags = 643 (on Linux/Windows)
// Sometimes this is required, sometimes it isn't. I think what is happening is that in // the cases it doesn't seem to be required, pev_frags is getting updated internally. set_pev(iIndex, pev_frags, float(iNewFrags));
#if defined MOLOTOV_DEBUG mydump(iIndex, 65, 85); mydump(iIndex, 635, 655); #endif } #endif
// Return a user's frags, and verify that the TFC offsets haven't changed #if defined TFC stock tfc_get_user_frags(iIndex) { new iOffset = (is_linux_server() ? 76 : 77); // real_frags = 76 (Linux), 77 (Windows) new iFrags = get_user_frags(iIndex);
// This code is the easiest way to detect a change in the offsets and help prevent annoying troubleshooting. // get_user_frags seems to always return the correct value. if (iFrags != get_pdata_int(iIndex, iOffset)) { client_print(0, print_chat, "OFFSET CHANGED! get_user_frags(%d):%d; get_pdata_int(%d, %d):%d; Contact plugin author!", iIndex, iFrags, iIndex, iOffset, get_pdata_int(iIndex, iOffset)); console_print(0, "WARNING! get_user_frags != real_frags! Contact plugin author!!!!!!!!!!!!!!!!!!!!"); }
return get_pdata_int(iIndex, iOffset); } #endif
// This won't be compiled unless MOLOTOV_DEBUG is set (and only for TFC) #if defined TFC stock mydump(iIndex, iStart, iEnd) {
new sLine[512]; new FILE[] = "addons/amxmodx/logs/pdatadump.log";
new sClassname[64]; entity_get_string(iIndex, EV_SZ_classname, sClassname, charsmax(sClassname)); format(sLine, charsmax(sLine), "Starting dump of entity %s %d", sClassname, iIndex); console_print(1, "%s to file %s", sLine, FILE); if (!write_file(FILE, sLine)) { console_print(1, "Error dumping to %s!", FILE); return PLUGIN_HANDLED; }
for (new i = iStart; i <= iEnd; i++) { format(sLine, charsmax(sLine), "%s %d: Offset %d:^t%d^t%f", sClassname, iIndex, i, get_pdata_int(iIndex, i), get_pdata_float(iIndex, i)); if (!write_file(FILE, sLine)) { console_print(1, "Error dumping to %s!", FILE); return PLUGIN_HANDLED; } }
console_print(1, "Dump done. Check %s!", FILE);
return 1; } #endif
|
this is exactly what i need, but do you know how can i made the smoke bounce off the wall when i throw, because its exploding everywhere i throw, example in the video below:
https://drive.google.com/file/d/1jrO...qH5QmBdcM/view
i want it to explode just in the floor, do you know how can i do it?!
__________________
⠀⠀⠀⠀⠀⠀
⠀⠀kabest
Last edited by joaokb; 04-23-2021 at 08:26.
|
|