/[svn]/hlds/cstrike/addons/amxmodx/scripting/molotov_cocktail.sma
ViewVC logotype

Diff of /hlds/cstrike/addons/amxmodx/scripting/molotov_cocktail.sma

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 26 by cstrike, Sun Nov 23 19:48:46 2008 UTC revision 61 by cstrike, Sun Apr 13 20:37:54 2014 UTC
# Line 26  Line 26 
26    
27    
28                          Molotov Cocktail                          Molotov Cocktail
29                            Version 3.20                            Version 3.30
30                  Maintained by: DynamicBits (Andy)                  Maintained by: DynamicBits (Andy)
31    
32  * Commands:  * Commands:
33  - say molotov - Buy a molotov  - say molotov - Buy a Molotov
34  - say /molotov - Buy a molotov  - say /molotov - Buy a Molotov
35  - molotov_give <player|@all|@t|@ct|@al|@ax> - Gives molotovs to a player, a team, or everyone  - molotov_give <player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give Molotov(s) to a player, a team, or everyone
36  - molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status)  - molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status)
37  - molotov_override [0|1] - Enable/disable the HE grenade override (If no arguments, show the status)  - molotov_override [0|1] - Enable/disable the standard grenade override (If no arguments, show the status)
38    
39  * Cvars  * Cvars
40  - molotov_enabled - Enable/disable the plugin [1 = enabled; 0 = disabled] [default = 1]  - molotov_enabled <0|1> - (Default: 1) Enable(1)/disable(0) the plugin
41  - molotov_price - Set the molotov price [default = 1200]  - molotov_price <N> - (Default: 300) Set the Molotov price (Counter-Strike only)
42  - molotov_damage - Set the damage done by initial molotov explosion [default = 50.0]  - molotov_damage <N> - (Default: 50.0) Set the damage done by initial Molotov explosion
43  - molotov_radius - Set the radius of molotov damage [default = 150.0]  - molotov_radius <N> - (Default: 150.0) Set the radius of Molotov damage
44  - molotov_firetime - Duration (in seconds) of fire effects, sounds, etc. [default = 6]  - molotov_firetime <N> - (Default: 6) Duration (in seconds) of fire effects, sounds, etc.
45  - molotov_firedamage - Amount of damage done by fire effects (every 0.2 secs). [default = 3]  - molotov_firedamage <N> - (Default: 3) Amount of damage done by fire effects (every 0.2 secs)
46  - molotov_ff - Disable(0)/enable(1) the ability to damage/kill someone on your team with molotov. [default = 1] (Was molotov_tk)  - molotov_ff <0|1|-1|-2> - (Default: 1) Set Molotov friendly fire status (Was molotov_tk)
47  - molotov_override_he - Override the original hegrenade automatically with molotov. [default = 0] (Was molotov_tempoverride)    *  0 - Disable friendly fire for Molotovs (regardless of mp_friendlyfire)
48  - molotov_max - Max num of molotovs able to carry. [default = 1] (Does not work with override)    *  1 - Enable friendly fire for Molotovs (regardless of mp_friendlyfire)
49  - molotov_inmenu - Puts molotov in the end of the equipment buymenu (old menu, not VGUI). [default = 0]    * -1 - Use mp_friendlyfire value
50                     (If the override cvar is enabled the hegrenade will be replaced instead.)    * -2 - Check bit 5 (decimal: 16) of mp_teamplay (DOD and TFC only)
51  - molotov_buyzone - Do you have to be in buyzone? [default = 1] (If molotov_inmenu=1, this is ignored)  - molotov_override_he <0|1> - (Default: 0) Override the mod's standard grenade automatically with Molotov (Was molotov_tempoverride)
52  - molotov_menu - Enables menu at beginning of each round [default = 0] (Was amx_molotovmenu)  - molotov_max <N> - (Default: 1) Limit carried Molotovs to this amount
53      * (Recommended: CSTRIKE: ≤ 10; DOD: ≤ 9; TFC: ≤ 4;)
54    - molotov_buyzone <0|1> - (Default: 1) Limit Molotov buying to buyzone (Counter-Strike only)
55    - molotov_menu <0|1> - (Default: 0) Enable menu at beginning of each round (Was amx_molotovmenu)
56    
57  * Required Modules:  * Required Modules:
58  - Fakemeta  - Fakemeta
59  - Fakemeta Utilities  - Cstrike (Counter-Strike only)
60  - Cstrike  - Csx (Counter-Strike only)
61    - Dodfun (Day of Defeat only)
62    - Dodx (Day of Defeat only)
63    - Tfcx (Team Fortress Classic only)
64    - Engine (TFC with debugging only)
65    
66  * Changelog/Credit:  * Changelog/Credit:
67  - DynamicBits  - DynamicBits
68    * (SVN)    * Version 3.30 (2014-04-13)
69      - Removed unnecessary csx include      - (Beta) Day of Defeat support was added
70        - (Beta) Team Fortress Classic support was added
71        - (Untested) Stats logging support was added
72        - New values for molotov_ff were added
73        - Friction/velocity after explosion was adjusted for realism
74        - Bottle breaking sound was added
75        - Molotov suicides no longer reward extra score points
76        - Frag count calculations were fixed
77        - Money calculations were fixed
78        - Override no longer sets a negative number of Molotovs
79        - Default price was changed to match standard grenades
80        - Console text now goes to correct console
81        - Text (non-VGUI) buy menu support was removed
82      - Converted fun functions to fakemeta_util functions (removed fun include)      - Converted fun functions to fakemeta_util functions (removed fun include)
83      - Minor optimizations      - Various optimizations
84      - Fixed a few typos      - A few typos were fixed
85    * Version 3.20 (2008-11-20)    * Version 3.20 (2008-11-20)
86      - My first public release      - My first public release
87      - Finally tracked down and fixed the intermittent crashing problem (I hope!)      - Finally tracked down and fixed the intermittent crashing problem (I hope!)
88      - Modified default damage values      - Modified default damage values
89      - molotov_cocktail/molotov_override commands now change settings *or* display status      - molotov_cocktail/molotov_override commands now change settings *or* display status
90      - Broken molotov model stays closer to the explosion (looks more realistic)      - Broken Molotov model stays closer to the explosion (looks more realistic)
91      - Task IDs are now guaranteed to be unique      - Task IDs are now guaranteed to be unique
92      - Modified anti-lag calculations to be more accurate (less likely to lag)      - Modified anti-lag calculations to be more accurate (less likely to lag)
93      - Changed amx_molotovmenu CVAR to molotov_menu      - Changed amx_molotovmenu CVAR to molotov_menu
# Line 82  Line 101 
101  - Raffe (CantShoot)  - Raffe (CantShoot)
102    * (Unversioned release)    * (Unversioned release)
103      - Originally fixed plugin to run on Linux servers      - Originally fixed plugin to run on Linux servers
104      - Added optional menu to purchase molotov cocktails each round      - Added optional menu to purchase Molotov cocktails each round
105      - Moved models and sounds into proper molotov/ subdirectories      - Moved models and sounds into proper molotov/ subdirectories
106      - Fixed molotovs not being reset upon player disconnect      - Fixed Molotovs not being reset upon player disconnect
107      - (Almost) fixed molotovs not being removed for new round      - (Almost) fixed Molotovs not being removed for new round
108      - Added @all/@ct/@t arguments to cmd_MolotovGive      - Added @all/@ct/@t arguments to molotov_give command
109      - Changed some models/sound      - Changed some models/sound
110  - [ --<-@ ] Black Rose  - [ --<-@ ] Black Rose
111    * Version 3.0-3.1c ?    * Version 3.0-3.1c ?
# Line 98  Line 117 
117  */  */
118    
119  #pragma semicolon 1  #pragma semicolon 1
120  #include <amxmodx>  
121  #include <amxmisc>  // Uncomment only the define that applies to your mod
122  #include <fakemeta>  #define CSTRIKE
123  #include <fakemeta_util>  //#define DOD
124  #include <cstrike>  //#define TFC
125    
126  // Uncomment the following line to enable debug logging.  // Uncomment the following line to enable debug logging.
127  //#define MOLOTOV_DEBUG  //#define MOLOTOV_DEBUG
128    
 #define AUTHORS "DynamicBits"  
129    
130    #include <amxmodx>
131    #include <amxmisc>
132    //#include <fakemeta>           // (runtime only)
133    #include <fakemeta_util>
134    #if defined CSTRIKE
135            #include <cstrike>
136            #include <csx>          // Used only for custom_weapon_* functions
137    #endif
138    #if defined DOD
139            #include <dodfun>
140            #include <dodx>         // Used only for custom_weapon_* functions
141    #endif
142    #if defined TFC
143            #include <tfcx>
144            #include <engine>       // Used only with debugging enabled
145    #endif
146    
147    // If you really want to same some memory and know you won't have 32 players, you can change this.
148    #define MAX_PLAYERS             32
149  #define ADMIN_ACCESS ADMIN_KICK  #define ADMIN_ACCESS ADMIN_KICK
150    
151  #define ANTI_LAGG 7.0   // Defines max calculations before a flame is spawned without check if onground  #define ANTI_LAGG               7       // Defines max calculations before a flame is spawned without check if on ground
152  // This is to prevent lagg at really narrow ents where you could end up with 400 calculations per flame  // This is to prevent lag at really narrow ents where you could end up with 400 calculations per flame. Suggested: <= 10
 // Suggested: <= 10  
153    
154  #define MOLOTOV_HARD_LIMIT 10   // Maximum molotov cocktails this code is capable of handling without bugs  #define MOLOTOV_HARD_LIMIT      10      // Maximum Molotov cocktails this code is capable of handling without bugs (per player)
155                                  // Also how many cocktails people get with molotov_give command  
156    #define ID_TO_INDEX(%0)         %0 - 1  // Use this macro rather than dim the arrays with 33
157    
158  #define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2      // Choices to look for with optional menu  #define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2      // Choices to look for with optional menu
159    
160  #define MOLOTOV_TASKID_BASE 1000                                // What is the lowest task ID?  // Task IDs
161    #define MOLOTOV_TASKID_RESET    1000                                            // Set g_bReset to false after a short delay (task is TFC only)
162  #define MOLOTOV_TASKID_OFFSET MOLOTOV_HARD_LIMIT  #define MOLOTOV_TASKID_OFFSET MOLOTOV_HARD_LIMIT
163  #define MOLOTOV_TASKID_BASE1 MOLOTOV_TASKID_BASE                                 // By default, with 32 players, task ids  // These task IDs are dynamically set per-Molotov
164  #define MOLOTOV_TASKID_BASE2 MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * 32) // from 1000 to 1959 can  #define MOLOTOV_TASKID_BASE1    2000                                            // By default, with 32 players, task ids
165  #define MOLOTOV_TASKID_BASE3 MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * 32) // potentially be used used.  #define MOLOTOV_TASKID_BASE2    MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // from 2000 to 2959 can
166    #define MOLOTOV_TASKID_BASE3    MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // potentially be used used.
167    
168  #define TEAM_UNASSIGNED 0  #define TEAM_UNASSIGNED 0
169  #define TEAM_ONE        1  #define TEAM_ONE        1
170  #define TEAM_TWO        2  #define TEAM_TWO        2
171  #define TEAM_SPECTATOR  3  #define TEAM_THREE              3
172    #define TEAM_FOUR               4
173  new pEnabled, pPrice, pMlDamage, pMlRadius, pFireTime, pOverride;  #define MC_TFC_PC_CIVILIAN      11      // Temporary workaround for AMXX bug 6042
174  new pFriendlyFire, pFireDmg, pMaxMolotovs, pBuyMenu, pBuyZone, pMolotovMenu;  
175    
176  new gmsgScoreInfo, gmsgDeathMsg;  new const g_PLUGIN[]  = "Molotov Cocktail";
177    new const g_AUTHORS[] = "DynamicBits";
178  new g_pAllocModel, g_vAllocModel;  new const g_VERSION[] = "3.30";
179    
180    new pEnabled;                           // Pointer to molotov_enabled
181    new pMlDamage;                          // Pointer to molotov_damage
182    new pMlRadius;                          // Pointer to molotov_radius
183    new pFireTime;                          // Pointer to molotov_firetime
184    new pOverride;                          // Pointer to molotov_override_he
185    new pMFF;                               // Pointer to molotov_ff
186    new pFriendlyFire;                      // Pointer to mp_friendlyfire
187    new pFireDmg;                           // Pointer to molotov_firedamage
188    new pMaxMolotovs;                       // Pointer to molotov_max
189    #if defined DOD || defined TFC
190    new pTeamPlay;                          // Pointer to mp_teamplay
191    #endif
192    #if defined CSTRIKE
193    new pBuyZone;                           // Pointer to molotov_buyzone
194    new pMolotovMenu;                       // Pointer to molotov_menu
195    new pPrice;                             // Pointer to molotov_price
196    
197  new g_frags[33];  new g_msgScoreInfo;                     // ScoreInfo message ID
198  new g_hasMolotov[33];  #endif
199  new g_restarted;  new g_msgDeathMsg;                      // DeathMsg message ID
 new g_MaxPlayers;  
 new g_bomb_map;  
200    
201  new firespr, smokespr[2];  new g_NumMolotov[MAX_PLAYERS];          // How many Molotovs each player has
202    #if defined CSTRIKE
203    new bool:g_bRestarted;                  // Reset Molotovs after first round restart
204    #endif
205    new g_MaxPlayers;                       // Max players (calculated at runtime to make loops more efficient)
206    new g_wpnMolotov;                       // Custom weapon ID
207    new bool:g_bReset;                      // Reset and stop explosions after round ends; Stop reset_tasks() from getting called once per player
208    
209    new g_iFireSprite, g_iSmokeSprite[2];   // Handles to the precached sprites
210    new g_iMolotovOffset[MAX_PLAYERS];      // Offset used for a player's task ID
211    
212    // The Pawn compiler does not optimize the DATA section. Any string that appears multiple times should be optimized with a global constant.
213    //   *Unused* constants do not affect the compiled size, however they create compiler warnings. (#pragma unused is a quick fix for the warnings.)
214    new const EVENT_ROUND_END[] = "event_round_end";
215    #if defined CSTRIKE
216    new const BUY_MOLOTOV[] = "buy_molotov";
217    new const WEAPON_HEGRENADE[] = "weapon_hegrenade";
218    #endif
219    #if defined DOD
220    new const WEAPON_HANDGRENADE[] = "weapon_handgrenade";
221    new const WEAPON_STICKGRENADE[] = "weapon_stickgrenade";
222    #endif
223    #if defined DOD || defined TFC
224    new const HUDTEXT[] = "HudText";
225    #endif
226    #if defined CSTRIKE || defined TFC
227    new const TEXTMSG[] = "TextMsg";
228    #endif
229    
230  new last_Molotov_ent;  // Check for outdated tfcconst.inc file (and likely outdated AMX Mod X core/modules).
231  new g_Molotov_offset[33];  //   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.
232    //   In the meantime, I created the MC_TFC_PC_CIVILIAN define.
233    //#if defined TFC && TFC_PC_CIVILIAN != 11      // TFC_PC_CIVILIAN was (incorrectly) 10 in older versions
234    //      #error TFC_PC_CIVILIAN != 11. Update your tfcconst.inc include file. Get the latest AMX Mod X snapshots at www.amxmodx.org/snapshots.php
235    //#endif
236    
 new g_g;  
237    
238    // Initialize the plugin
239  public plugin_init() {  public plugin_init() {
240    
241          register_plugin("Molotov Cocktail", "3.20SVN", AUTHORS);          register_plugin(g_PLUGIN, g_VERSION, g_AUTHORS);
242            server_print("[MC] ---- Molotov Cocktail %s loaded ----", g_VERSION);
243            register_cvar("MolotovCocktail", g_VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
244    
245    #if defined CSTRIKE
246          register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");          register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");
247    
248  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
249          register_clcmd("molotov_menutest", "show_molotov_menu");          register_clcmd("molotov_menutest", "show_molotov_menu");
250  #endif  #endif
251    
252          register_clcmd("say /molotov", "buy_molotov");          register_clcmd("say /molotov", BUY_MOLOTOV);
253          register_clcmd("say molotov", "buy_molotov");          register_clcmd("say molotov", BUY_MOLOTOV);
254    #endif
255          register_concmd("molotov_give", "cmd_MolotovGive", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax> - Gives free molotov cocktails (default=10)");          register_concmd("molotov_give", "molotov_give", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give free Molotov cocktails");
256          register_concmd("molotov_override", "cmd_Override", ADMIN_ACCESS, "[0|1] - Enable/disable the HE grenade override (If no arguments, show the status)");          register_concmd("molotov_override", "molotov_override", ADMIN_ACCESS, "[0|1] - Enable/disable the standard grenade override (If no arguments, show the status)");
257          register_concmd("molotov_cocktail", "cmd_PluginStatus", ADMIN_ACCESS, "[0|1] - Enable/disable the plugin (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)");
258    
259          pEnabled = register_cvar("molotov_enabled", "1", FCVAR_SPONLY);          pEnabled = register_cvar("molotov_enabled", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
260          pOverride = register_cvar("molotov_override_he", "0", FCVAR_SPONLY);          pOverride = register_cvar("molotov_override_he", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
261          pPrice = register_cvar("molotov_price", "1200", FCVAR_SPONLY);          pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
262          pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_SPONLY);          pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
263          pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_SPONLY);          pFireTime = register_cvar("molotov_firetime", "6", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
264          pFireTime = register_cvar("molotov_firetime", "6", FCVAR_SPONLY);          pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
265          pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_SPONLY);          pMFF = register_cvar("molotov_ff", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
266          pFriendlyFire = register_cvar("molotov_ff", "1", FCVAR_SPONLY);          pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
267          pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_SPONLY);          pFriendlyFire = register_cvar("mp_friendlyfire", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
268          pBuyMenu = register_cvar("molotov_inmenu", "0", FCVAR_SPONLY);  #if defined CSTRIKE
269          pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_SPONLY);          pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
270          pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_SPONLY);          pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
271            pPrice = register_cvar("molotov_price", "300", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
272          register_event("CurWeapon", "Event_CurWeapon", "be", "1=1");  #endif
273          register_event("HLTV", "event_new_round", "a", "1=0", "2=0");  #if defined DOD
274          register_event("TextMsg", "Event_GameRestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in");          pTeamPlay = register_cvar("mp_teamplay", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
275          register_event("DeathMsg", "event_DeathMsg", "a");  #endif
276    #if defined TFC
277          register_event("ShowMenu", "event_BuyMenuT", "b", "4=#T_BuyItem", "1=575");          pTeamPlay = register_cvar("mp_teamplay", "21", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
278          register_event("ShowMenu", "event_BuyMenuCT", "b", "4=#CT_BuyItem", "1=703");  #endif
         register_event("ShowMenu", "event_BuyMenuT", "b", "4=#DT_BuyItem", "1=575");  
         register_event("ShowMenu", "event_BuyMenuCT", "b", "4=#DCT_BuyItem", "1=767");  
279    
280          register_logevent("logevent_Round_End", 2, "1=Round_End");          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).
281    #if defined CSTRIKE || defined DOD
282            register_event("CurWeapon", "event_curweapon", "be", "1=1");
283            register_event("HLTV", "event_new_round", "a", "1=0", "2=0");   // cstrike/dod new round; So far, I haven't found a TFC equivalent
284    #endif
285    #if defined CSTRIKE
286            register_event(TEXTMSG, "event_gamerestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in");
287    #endif
288    #if defined DOD
289            register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&VICTORY");     // Sent once per player on round end
290    #endif
291    #if defined TFC
292            // Since TFC doesn't have any generic end of round event/message, specific messages need to be caught for certain maps.
293            //   Maps that don't have traditional rounds (2fort, badlands, casbah, crossover2, cz2, ravelin, skate2, well, etc.) don't apply here.
294            //   All of the default maps are accounted for. If there is demand for specific custom maps, I will add the appropriate message(s).
295            new sCurrentMap[32];
296            get_mapname(sCurrentMap, charsmax(sCurrentMap));
297            if (!strcmp(sCurrentMap, "avanti")) {
298                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#italy_endround_win");         // Was Avanti originally called Italy?
299            } else if ((!strcmp(sCurrentMap, "dustbowl")) || (!strcmp(sCurrentMap, "castleargh")) || (!strcmp(sCurrentMap, "castleargh2"))) {
300                    register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_one");  // Technically these are "stages," not "rounds"
301                    register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_two");
302                    //register_event(TEXTMSG, "event_round_end", "b", "2=#dustbowl_blue_caps");     // The map ends after this cap
303            } else if (!strcmp(sCurrentMap, "epicenter")) {
304                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#dblmint_you_capped_flag");    // dblmint?!
305            } else if (!strcmp(sCurrentMap, "flagrun")) {
306                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&you won this round!");
307            } else if (!strcmp(sCurrentMap, "hunted")) {
308                    register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#hunted_target_killed");
309            } else if (!strcmp(sCurrentMap, "push")) {
310                    register_event(TEXTMSG, EVENT_ROUND_END, "b", "2&_netname_scores");
311            } else if (!strcmp(sCurrentMap, "rock2")) {
312                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=1 . . .^n");
313            } else if (!strcmp(sCurrentMap, "warpath")) {
314                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_red_wins");
315                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_blue_wins");
316            // ---------- Custom Maps ----------
317            } else if (!strcmp(sCurrentMap, "castleargh3")) {
318                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&You have secured");            // This works for all four stages
319            } else if (!strcmp(sCurrentMap, "hwguyz2")) {
320                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#2fort_you_capped_flag");
321                    register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&Time Ran Out");
322            }
323    
324            //#if defined MOLOTOV_DEBUG
325            //register_event(TEXTMSG, "event_textmsg_a", "a");
326            //register_event(TEXTMSG, "event_textmsg_b", "b");
327            //register_event(HUDTEXT, "event_hudtext_a", "a");
328            //register_event(HUDTEXT, "event_hudtext_b", "b");
329            //#endif
330    #endif
331    
332          register_menucmd(register_menuid("#CT_BuyItem"), 1023, "handle_BuyMenuCT");  #if defined CSTRIKE
333          register_menucmd(register_menuid("#T_BuyItem"), 1023, "handle_BuyMenuT");          register_logevent(EVENT_ROUND_END, 2, "1=Round_End");
334    #endif
335    
336          register_forward(FM_EmitSound, "fw_EmitSound");          register_forward(FM_EmitSound, "fw_emitsound");
337    #if defined TFC
338            register_forward(FM_SetModel, "fw_setmodel_post", 1);
339    #endif
340    
341          g_MaxPlayers = get_maxplayers();          g_MaxPlayers = get_maxplayers();
342    
343          gmsgScoreInfo = get_user_msgid("ScoreInfo");  #if defined CSTRIKE
344          gmsgDeathMsg = get_user_msgid("DeathMsg");          g_msgScoreInfo = get_user_msgid("ScoreInfo");
345    #endif
346            g_msgDeathMsg = get_user_msgid("DeathMsg");
347    
348            g_wpnMolotov = custom_weapon_add("molotov", 0, "molotov");      // I can hardly find any documentation or sample code for this. I have no
349                                                                            //   idea if I'm using it correctly or not. I'm not even sure what it affects.
350    }
351    
352    // These are primarily for catching messages in TFC to add custom round end triggers.
353    /*
354    #if defined MOLOTOV_DEBUG
355    public event_textmsg_a() {
356            new sArg2[64];
357            read_data(2, sArg2, charsmax(sArg2));
358    
359            client_print(0, print_chat, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
360            console_print(0, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
361    }
362    
363          g_pAllocModel = engfunc(EngFunc_AllocString, "models/molotov/p_molotov.mdl");  public event_textmsg_b() {
364          g_vAllocModel = engfunc(EngFunc_AllocString, "models/molotov/v_molotov.mdl");          new sArg2[64];
365            read_data(2, sArg2, charsmax(sArg2));
366    
367          g_bomb_map = engfunc(EngFunc_FindEntityByString, g_MaxPlayers, "classname", "info_bomb_target") ? 1 : 0;          client_print(0, print_chat, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
368            console_print(0, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
369  }  }
370    
371  public plugin_precache() {  public event_hudtext_a() {
372            new sArg1[64];
373            read_data(1, sArg1, charsmax(sArg1));
374    
375          firespr = precache_model("sprites/flame.spr");          client_print(0, print_chat, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
376            console_print(0, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
377    }
378    
379          smokespr[0] = precache_model("sprites/black_smoke3.spr");  public event_hudtext_b() {
380          smokespr[1] = precache_model("sprites/steam1.spr");          new sArg1[64];
381            read_data(1, sArg1, charsmax(sArg1));
382    
383          precache_sound("molotov/molotov_fire.wav");          client_print(0, print_chat, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
384            console_print(0, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
385    }
386    #endif
387    */
388    
389    // Precache models and sound(s)
390    public plugin_precache() {
391    
392            g_iFireSprite = precache_model("sprites/flame.spr");
393    
394            g_iSmokeSprite[0] = precache_model("sprites/black_smoke3.spr");
395    #if defined DOD
396            g_iSmokeSprite[1] = g_iSmokeSprite[0];                  // steam1.spr shows a black background in dod
397    #else
398            g_iSmokeSprite[1] = precache_model("sprites/steam1.spr");
399    #endif
400    
401    #if defined CSTRIKE || defined DOD
402          precache_model("models/molotov/p_molotov.mdl");          precache_model("models/molotov/p_molotov.mdl");
403          precache_model("models/molotov/v_molotov.mdl");          precache_model("models/molotov/v_molotov.mdl");
404    #endif
405          precache_model("models/molotov/w_molotov.mdl");          precache_model("models/molotov/w_molotov.mdl");
406          precache_model("models/molotov/w_broke_molotov.mdl");          precache_model("models/molotov/w_broke_molotov.mdl");
407    
408            precache_sound("molotov/molotov_fire.wav");
409    
410  }  }
411    
412    // Reset Molotovs so that a new player doesn't have any
413  public client_disconnect(id) {  public client_disconnect(id) {
414          g_hasMolotov[id] = 0;          g_NumMolotov[ID_TO_INDEX(id)] = 0;
415  }  }
416    
417  public fw_EmitSound(ent, channel, sample[]) {  // Catch the first impact of the Molotov and start the sound/explosion
418    //   A Molotov cocktail should "explode" on impact, not after a set time.
419    public fw_emitsound(ent, channel, sample[]) {
420    #if defined CSTRIKE
421          if (equal(sample[8], "he_bounce", 9)) {          if (equal(sample[8], "he_bounce", 9)) {
422    #else
423            #if defined DOD || defined TFC
424            // 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)
425            //   A fix would be to use FM_Touch or Ham_Touch or register_touch instead of FM_EmitSound
426            if (equal(sample[8], "grenade_hit", 11)) {
427            #endif
428    #endif
429    
430                  new model[32];                  new sModel[32];
431                  pev(ent, pev_model, model, 31);                  pev(ent, pev_model, sModel, charsmax(sModel));
432    
433                  if (equal(model[9], "lotov/w_molotov.mdl")) {                  // Depending on where the Molotov lands, the EmitSound forward may get called 50+ times.
434                          if (last_Molotov_ent != ent) {                  // After the first hit, the model is changed to w_broke_molotov, so this code is skipped on successive calls
435                                  new Float:fFriction, Float:fVelocity[3];                  if (equal(sModel[15], "w_molotov.mdl")) {
436    #if defined TFC
437                            set_pev(ent, pev_nextthink, 99999.0);           // For TFC, this is about the only way I can stop the explosion.
438    #endif
439                            // The glass breaking sound has a low range (ATTN_STATIC) so as not to be overpowering
440                            emit_sound(ent, CHAN_AUTO, "debris/glass2.wav", VOL_NORM, ATTN_STATIC, 0, PITCH_LOW);
441    
442                            new Float:fFriction, Float:fVelocity[3];
443                                  pev(ent, pev_friction, fFriction);                                  pev(ent, pev_friction, fFriction);
444                            fFriction *= 1.15;                              // Increase friction to make it look more realistic
445                            set_pev(ent, pev_friction, fFriction);
446    
447                                  pev(ent, pev_velocity, fVelocity);                                  pev(ent, pev_velocity, fVelocity);
448                            fVelocity[0] *= 0.3;                            // Decrease velocity because friction doesn't do it all
449                            fVelocity[1] *= 0.3;
450                            fVelocity[2] *= 0.3;
451                            set_pev(ent, pev_velocity, fVelocity);
452    
453                                  fFriction *= 1.3;       // Increase friction to make it look more realistic                          molotov_explode(ent);                           // Replacement for normal grenade explosion
                                 set_pev(ent, pev_friction, fFriction);  
 #if defined MOLOTOV_DEBUG  
                                 log_amx("[MC] ent:%d Friction:%f Velocity:%f/%f/%f ====================", ent, fFriction, fVelocity[0], fVelocity[1], fVelocity[2]);  
 #endif  
                                 last_Molotov_ent = ent;  
                                 grenade_explode(ent);  
454    
455                                  return FMRES_SUPERCEDE;                                  return FMRES_SUPERCEDE;
456  #if defined MOLOTOV_DEBUG                  } else if (equal(sModel[15], "w_broke_molotov.")) {     // "mdl" is truncated because of the array size, which is OK
457                          } else {                          return FMRES_SUPERCEDE;                 // Don't play any sounds for bounces.
                                 log_amx("[MC] last_Molotov_ent(%d) ent(%d)", last_Molotov_ent, ent);  
 #endif  
458                          }                          }
459                  }                  }
460    
461            return FMRES_IGNORED;
462          }          }
463    
464    // Since TFC handles grenades differently, this is roughly equivalant to event_curweapon() used by cstrike and dod.
465    #if defined TFC
466    public fw_setmodel_post(ent, const model[]) {
467            if (!pev_valid(ent)) {                  // Check if it's a valid entity to prevent errors
468          return FMRES_IGNORED;          return FMRES_IGNORED;
469  }  }
470    
471  public Event_CurWeapon(id) {          new sClassname[32];
472            pev(ent, pev_classname, sClassname, charsmax(sClassname));
473    
474          if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) {          if (!get_pcvar_num(pEnabled) || !equal(sClassname, "normalgrenade")) {
475                  return PLUGIN_CONTINUE;                  return FMRES_IGNORED;
476          }          }
477    
478          if (!g_hasMolotov[id] && !get_pcvar_num(pOverride)) {          new iOwner = pev(ent, pev_owner);
479                  return PLUGIN_CONTINUE;          if (!g_NumMolotov[ID_TO_INDEX(iOwner)] && !get_pcvar_num(pOverride)) {  // If no Molotovs and override is disabled, return
480                    return FMRES_IGNORED;
481          }          }
482    
483          new WeaponID = get_user_weapon(id, WeaponID, WeaponID);          if (g_NumMolotov[ID_TO_INDEX(iOwner)] > 0) {    // Prevent negative values
484                    g_NumMolotov[ID_TO_INDEX(iOwner)]--;
485            }
486    
487          if (WeaponID != CSW_HEGRENADE) {          set_pev(ent, pev_team, get_user_team(iOwner));
488                  return PLUGIN_CONTINUE;          custom_weapon_shot(g_wpnMolotov, iOwner);
489    
490            engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
491    
492            return FMRES_HANDLED;
493          }          }
494    #endif
495    
496          set_pev(id, pev_viewmodel, g_vAllocModel);  // When the player changes weapons to the Molotov, update the model
497          set_pev(id, pev_weaponmodel, g_pAllocModel);  #if defined CSTRIKE || defined DOD
498          set_pev(id, pev_weaponanim, 9);  public event_curweapon(id) {
499    
500            if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) {
501          return PLUGIN_CONTINUE;          return PLUGIN_CONTINUE;
502  }  }
503    
504  public Event_GameRestart() {          if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {      // If no Molotovs and override is disabled, return
505          g_restarted = 1;                  return PLUGIN_CONTINUE;
506  }  }
507    
508  public event_DeathMsg() {          new iWeaponID = get_user_weapon(id, _, _);
509          g_hasMolotov[read_data(2)] = 0;  #if defined CSTRIKE
510            if (iWeaponID != CSW_HEGRENADE) {
511    #else                                                   // elseif *should* work, but there is a bug in the compiler
512            #if defined DOD
513            // current weapon is never set to DODW_MILLS_BOMB in this event; only DODW_HANDGRENADE/DODW_STICKGRENADE
514            if ((iWeaponID != DODW_HANDGRENADE) && (iWeaponID != DODW_STICKGRENADE)) {
515            #endif
516    #endif
517                    return PLUGIN_CONTINUE;
518  }  }
519    
520  public logevent_Round_End() {          set_pev(id, pev_viewmodel2, "models/molotov/v_molotov.mdl");    // View model (First person)    *model2 doesn't require allocating the string
521  #if defined MOLOTOV_DEBUG          set_pev(id, pev_weaponmodel2, "models/molotov/p_molotov.mdl");  // Player model (Third person)
522          log_amx("[MC] ========== Round_End ==========");  
523    #if defined DOD
524            // I think 3 is correct, but it looks strange..
525            set_pev(id, pev_weaponanim, 3); // 0: "idle"; 1: "pullpin"; 2: "throw"; 3: "deploy"
526  #endif  #endif
         reset_tasks();  
 }  
527    
528  stock reset_tasks() {          return PLUGIN_CONTINUE;
529  #if defined MOLOTOV_DEBUG  }
         new tmpdbgid;  
530  #endif  #endif
531          for (new i; i < g_MaxPlayers; i++) {  
532                  for (new o; o < MOLOTOV_TASKID_OFFSET; o++) {   //TODO DEBUG: Verify this fixes the tasks not reset at new round issue  // Reset Molotovs on death
533                          if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) {  public event_deathmsg() {
                                 remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o);  
534  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
535                                  tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o;          log_amx("[MC] ========== DeathMsg ========== K(%d) V(%d)", read_data(1), read_data(2));
                                 log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);  
536  #endif  #endif
537            g_NumMolotov[ID_TO_INDEX(read_data(2))] = 0;
538                          }                          }
539    
540                          if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) {  // cstrike only
541                                  remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o);  #if defined CSTRIKE
542    public event_gamerestart() {
543  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
544                                  tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o;          log_amx("[MC] ========== Game Restart ==========");
                                 log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);  
545  #endif  #endif
546            g_bRestarted = true;
547                          }                          }
548    #endif
549    
550                          if (task_exists(MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * i) + o)) {  // cstrike, dod, and tfc will all call this once per player on round end
551                                  remove_task(MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * i) + o);  public event_round_end() {
552  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
553                                  tmpdbgid = MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * i) + o;          log_amx("[MC] ========== Round End ==========");
554                                  log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);  #endif
555    
556            if (g_bReset == false) {
557                    reset_tasks();
558                    g_bReset = true;
559    #if defined TFC
560                    set_task(2.0, "cancel_reset", MOLOTOV_TASKID_RESET);    // TFC won't call event_new_round, so do that stuff here instead
561  #endif  #endif
                         }  
                 }  
562          }          }
563  }  }
564    
565    // cstrike and dod will call this once per round, but TFC won't.
566    #if defined CSTRIKE || defined DOD
567  public event_new_round(id) {  public event_new_round(id) {
568  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
569          log_amx("[MC] ========== event_new_round ==========");          log_amx("[MC] ========== New Round ==========");
570  #endif  #endif
571            g_bReset = false;       // Stop blocking
572    
573          if (!get_pcvar_num(pEnabled)) {          if (!get_pcvar_num(pEnabled)) {
574                  return PLUGIN_CONTINUE;                  return PLUGIN_CONTINUE;
575          }          }
576    
577          reset_tasks();  // DEBUG          reset_tasks();                                          // This probably isn't needed anymore, but it shouldn't hurt anything
578    
579    #if defined CSTRIKE
580          if (get_pcvar_num(pMolotovMenu)) {          if (get_pcvar_num(pMolotovMenu)) {
581                    if (get_pcvar_num(pOverride)) {
582                            client_print(id, print_center, "Molotov cocktails will replace purchased HE grenades");
583                    } else {
584                  show_molotov_menu(id);                  show_molotov_menu(id);
585          }          }
   
         for (new i; i < g_MaxPlayers; i++) {  
                 if (g_frags[i] && is_user_connected(i)) {  
                         fm_set_user_frags(i, get_user_frags(i) + g_frags[i]);  
                 }  
                 g_frags[i] = 0;  
586          }          }
587    
588          if (g_restarted) {          // For cstrike only, make sure the player didn't quickly purchase a Molotov before the first actual round
589                  for (new i; i < g_MaxPlayers; i++) {          if (g_bRestarted) {
590                          g_hasMolotov[i] = 0;                  arrayset(g_NumMolotov, 0, sizeof(g_NumMolotov));        // Reset everyone to zero Molotovs
591                  }                  g_bRestarted = false;
                 g_restarted = 0;  
592          }          }
593    #endif
594    
595          if (get_pcvar_num(pOverride)) {          if (get_pcvar_num(pOverride)) {
596                  set_molotovs();                  set_molotovs();
# Line 368  Line 600 
600    
601          return PLUGIN_CONTINUE;          return PLUGIN_CONTINUE;
602  }  }
603    #endif
604    
605  public cmd_Override(id, level, cid) {  // Enable/Disable/Get status of override
606    public molotov_override(id, level, cid) {
607    
608          if (!cmd_access(id, level, cid, 1)) {           // First argument (passed to molotov_override) is optional          if (!cmd_access(id, level, cid, 1)) {           // First argument (passed to molotov_override) is optional
609                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
# Line 380  Line 614 
614          }          }
615    
616          if (read_argc() == 1) {                         // No arguments; Display status          if (read_argc() == 1) {                         // No arguments; Display status
617                  client_print(id, print_console, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled");                  console_print(id, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled");
618                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
619          }          }
620    
621          new arg[2];          new sArg[2];
622          read_argv(1, arg, 1);          read_argv(1, sArg, charsmax(sArg));
623    
624          new num = str_to_num(arg);          new iArg = str_to_num(sArg);
625    
626          if ((num < 0) || (num > 1) || (!isdigit(arg[0]))) {     // If less than 0 or greater than 1 or not a digit          if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {  // If less than 0 or greater than 1 or not a digit
627                  if (id) {                  console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
                         client_print(id, print_console, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", arg);  
                 } else {  
                         server_print("Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", arg);  
                 }  
628                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
629          }          }
630    
631          if (num == get_pcvar_num(pOverride)) {          if (iArg == get_pcvar_num(pOverride)) {
632                  if (id) {                  console_print(id, "Override is already %s.", iArg ? "enabled" : "disabled");
                         client_print(id, print_console, "Override is already %s.", num ? "enabled" : "disabled");  
                 } else {  
                         server_print("Override is already %s.", num ? "enabled" : "disabled");  
                 }  
633                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
634          }          }
635    
636          set_pcvar_num(pOverride, num);          set_pcvar_num(pOverride, iArg);
637            console_print(id, "Override was %s.", iArg ? "enabled" : "disabled");
638    
639          if (id) {  #if defined CSTRIKE || defined DOD
640                  client_print(id, print_console, "Override was %s.", num ? "enabled" : "disabled");          if (iArg) {                                     // If plugin is enabled (checked above) and override is enabled, set models to Molotov
         } else {  
                 server_print("Override was %s.", num ? "enabled" : "disabled");  
         }  
   
         if (num) {  
641                  set_molotovs();                  set_molotovs();
642          } else {          } else {
643                  reset_molotovs();                  reset_molotovs();
644          }          }
645    #endif
646    
647          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
648  }  }
649    
650  public cmd_PluginStatus(id, level, cid) {  // Enable/Disable/Get status of plugin
651    public molotov_cocktail(id, level, cid) {
652          if (!cmd_access(id, level, cid, 1)) {           // First argument (passed to molotov_cocktail) is optional          if (!cmd_access(id, level, cid, 1)) {           // First argument (passed to molotov_cocktail) is optional
653                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
654          }          }
655    
656          if (read_argc() == 1) {                         // No arguments; Display status          if (read_argc() == 1) {                         // No arguments; Display status
657                  client_print(id, print_console, "Plugin is currently %s.", get_pcvar_num(pEnabled) ? "enabled" : "disabled");                  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));
658                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
659          }          }
660    
661          new arg[2];          new sArg[2];
662          read_argv(1, arg, 1);          read_argv(1, sArg, charsmax(sArg));
663    
664          new num = str_to_num(arg);          new iArg = str_to_num(sArg);
665    
666          if ((num < 0) || (num > 1) || (!isdigit(arg[0]))) {     // If less than 0 or greater than 1 or not a digit          if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {  // If less than 0 or greater than 1 or not a digit
667                  if (id) {                  console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
                         client_print(id, print_console, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", arg);  
                 } else {  
                         server_print("Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", arg);  
                 }  
668                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
669          }          }
670    
671          if (num == get_pcvar_num(pEnabled)) {          if (iArg == get_pcvar_num(pEnabled)) {
672                  if (id) {                  console_print(id, "Plugin is already %s.", iArg ? "enabled" : "disabled");
                         client_print(id, print_console, "Plugin is already %s.", num ? "enabled" : "disabled");  
                 } else {  
                         server_print("Plugin is already %s.", num ? "enabled" : "disabled");  
                 }  
673                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
674          }          }
675    
676          set_pcvar_num(pEnabled, num);          set_pcvar_num(pEnabled, iArg);
677            console_print(id, "Plugin was %s.", iArg ? "enabled" : "disabled");
         if (id) {  
                 client_print(id, print_console, "Plugin was %s.", num ? "enabled" : "disabled");  
         } else {  
                 server_print("Plugin was %s.", num ? "enabled" : "disabled");  
         }  
678    
679          if (num && get_pcvar_num(pOverride)) {  #if defined CSTRIKE || defined DOD
680            if (iArg && get_pcvar_num(pOverride)) {         // If the plugin was enabled and override is enabled, set models to Molotov
681                  set_molotovs();                  set_molotovs();
682          } else {          } else {
683                  reset_molotovs();                  reset_molotovs();
684          }          }
685    #endif
686    
687          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
688  }  }
689    
690  public cmd_MolotovGive(id, level, cid) {  // Handle molotov_give console command
691    public molotov_give(id, level, cid) {
 #if defined MOLOTOV_DEBUG  
         log_amx("[MC] cmd_MolotovGive");  
 #endif  
692          if (!cmd_access(id, level, cid, 2)) {          if (!cmd_access(id, level, cid, 2)) {
693                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
694          }          }
695    
696          new Arg1[64], target;          new sArg1[16], iTarget;
697          read_argv(1, Arg1, 63);          read_argv(1, sArg1, charsmax(sArg1));
   
         new adminName[32];  
         get_user_name(id, adminName, 31);  
   
         new targetTeam, targetTeamName[32];  
         new Players[32], iNum;  
698  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
699          log_amx("[MC] cmd_MolotovGive Arg1[0](%s) Arg1[1](%s)", Arg1[0], Arg1[1]);          log_amx("[MC] molotov_give sArg1[0](%s)", sArg1[0]);
700    #endif
701            new sAdmin[32];
702            get_user_name(id, sAdmin, charsmax(sAdmin));
703            new iGiveAmount = (get_pcvar_num(pMaxMolotovs) < MOLOTOV_HARD_LIMIT ? get_pcvar_num(pMaxMolotovs) : MOLOTOV_HARD_LIMIT);
704    
705            if (sArg1[0] == '@') {
706    
707                    new iTargetTeam, sTeamName[32];
708                    new Players[MAX_PLAYERS], iNum;
709    
710                    if (equali(sArg1[1], "all")) {
711                            iTargetTeam = 0;
712                    } 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
713                            iTargetTeam = TEAM_ONE;
714                    } else if (equali(sArg1[1], "ct") || equali(sArg1[1], "ax") || equali(sArg1[1], "r")) { // CS_TEAM_CT or AXIS or Red
715                            iTargetTeam = TEAM_TWO;
716    #if defined TFC
717                    } else if (equali(sArg1[1], "y")) {     // Yellow
718                            iTargetTeam = TEAM_THREE;
719                    } else if (equali(sArg1[1], "g")) {     // Green
720                            iTargetTeam = TEAM_FOUR;
721  #endif  #endif
         if (Arg1[0] == '@') {  
   
                 if (equali(Arg1[1], "all")) {  
                         targetTeam = 0;  
                 } else if (equali(Arg1[1], "t") || equali(Arg1[1], "al")) {     // CS_TEAM_T or ALLIES  
                         targetTeam = TEAM_ONE;  
                 } else if (equali(Arg1[1], "ct") || equali(Arg1[1], "ax")) {    // CS_TEAM_CT or AXIS  
                         targetTeam = TEAM_TWO;  
722                  }                  }
723    
724                  get_players(Players, iNum, "ac");       // Bots don't understand Molotov cocktails                  get_players(Players, iNum, "ach");      // alive, no bots, no HLTV
725    
726                  for (new i = 0; i < iNum; ++i) {                  for (new i = 0; i < iNum; ++i) {
727                          target = Players[i];                          iTarget = Players[i];
728    
729                          if ((targetTeam == 0) || (get_user_team(target) == targetTeam)) {                          if ((iTargetTeam == 0) || (get_user_team(iTarget) == iTargetTeam)) {
730                                  g_hasMolotov[target] = MOLOTOV_HARD_LIMIT;                                  g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
731    
732                                  fm_give_item(target, "weapon_hegrenade");  #if defined CSTRIKE
733                                  cs_set_user_bpammo(target, CSW_HEGRENADE, MOLOTOV_HARD_LIMIT);                                  fm_give_item(iTarget, WEAPON_HEGRENADE);
734                                  emit_sound(target, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);                                  cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
735    #endif
736    #if defined DOD
737                                    // TODO - This sets the count, but it is not immediately updated on the HUD
738                                    switch(get_user_team(iTarget)) {
739                                            case ALLIES: {  // (or British)
740                                                    fm_give_item(iTarget, WEAPON_HANDGRENADE);
741                                                    dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
742                                            }
743                                            case AXIS: {
744                                                    fm_give_item(iTarget, WEAPON_STICKGRENADE);
745                                                    dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
746                                            }
747                                    }
748    #endif
749    #if defined TFC
750                                    new iClass = pev(iTarget, pev_playerclass);
751                                    if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {       // No unselected/spectator, scout, or civilian
752                                            tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);     // Requires 1.8.3-dev-hg185 or newer
753                                    }
754    #endif
755    #if defined CSTRIKE
756                                    emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
757    #endif
758    #if defined DOD
759                                    emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);   // "items/weaponpickup.wav" could work too, I suppose
760    #endif
761    #if defined TFC
762                                    // Shotgun pumping sound for picking up grenades... That's how TFC does it!
763                                    emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
764    #endif
765                          }                          }
766                  }                  }
767    
768                  switch(targetTeam) {                  switch(iTargetTeam) {
769                          case 0: {                          case 0: {
770                                  targetTeamName = "everyone";                                  sTeamName = "everyone";
771                          }                          }
772                          case 1: {                          case TEAM_ONE: {
773                                  if (cstrike_running()) {  #if defined CSTRIKE
774                                          targetTeamName = "all terrorists";                                  sTeamName = "all terrorists";
775                                  } else if (is_running("dod")) {  #endif
776                                          targetTeamName = "all allies";  #if defined DOD
777                                  } else {                                  sTeamName = "all allies";       // TODO - Allies or British
778                                          targetTeamName = "team 1";  #endif
779    #if defined TFC
780                                    sTeamName = "all blue";         // I *could* pull the team1_name value from the info_tfdetect entity (but only for some maps?)
781    #endif
782                                  }                                  }
783                            case TEAM_TWO: {
784    #if defined CSTRIKE
785                                    sTeamName = "all ct's";
786    #endif
787    #if defined DOD
788                                    sTeamName = "all axis";
789    #endif
790    #if defined TFC
791                                    sTeamName = "all red";
792                          }                          }
793                          case 2: {                          case TEAM_THREE: {
794                                  if (cstrike_running()) {                                  sTeamName = "all yellow";
                                         targetTeamName = "all ct's";  
                                 } else if (is_running("dod")) {  
                                         targetTeamName = "all axis";  
                                 } else {  
                                         targetTeamName = "team 2";  
795                                  }                                  }
796                            case TEAM_FOUR: {
797                                    sTeamName = "all green";
798    #endif
799                          }                          }
800                  }                  }
801                  client_print(0, print_chat, "ADMIN %s: has given %s %d Molotov cocktails!", adminName, targetTeamName, MOLOTOV_HARD_LIMIT);                  client_print(0, print_chat, "ADMIN %s has given %s %d Molotov cocktails!", sAdmin, sTeamName, iGiveAmount);
802    
803          } else {          } else {
804    
805                  target = cmd_target(id, Arg1, 0);                  iTarget = cmd_target(id, sArg1, 6);
806    
807                  if (!is_user_connected(target) || !is_user_alive(target)) {                  if (!is_user_connected(iTarget) || !is_user_alive(iTarget)) {
808                          return PLUGIN_HANDLED;                          return PLUGIN_HANDLED;
809                  }                  }
810    
811                  new targetName[32];                  g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
                 get_user_name(target, targetName, 31);  
   
                 g_hasMolotov[target] = MOLOTOV_HARD_LIMIT;  
812    
813                  fm_give_item(target, "weapon_hegrenade");  #if defined CSTRIKE
814                  cs_set_user_bpammo(target, CSW_HEGRENADE, MOLOTOV_HARD_LIMIT);                  fm_give_item(iTarget, WEAPON_HEGRENADE);
815                    cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
816                  emit_sound(target, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);  #endif
817    #if defined DOD
818                    switch(get_user_team(iTarget)) {
819                            case ALLIES: {  // (or British)
820                                    fm_give_item(iTarget, WEAPON_HANDGRENADE);
821                                    dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
822                            }
823                            case AXIS: {
824                                    fm_give_item(iTarget, WEAPON_STICKGRENADE);
825                                    dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
826                            }
827                    }
828    #endif
829    #if defined TFC
830                    new iClass = pev(iTarget, pev_playerclass);
831                    if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {       // No unselected/spectator, scout, or civilian
832                            tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);                             // Requires 1.8.3-dev-hg185 or newer
833                    }
834    #endif
835    #if defined CSTRIKE
836                                    emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
837    #endif
838    #if defined DOD
839                                    emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);   // "items/weaponpickup.wav" could work too, I suppose
840    #endif
841    #if defined TFC
842                                    // Shotgun pumping sound for picking up grenades... That's how TFC does it!
843                                    emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
844    #endif
845    
846                  client_print(target, print_chat, "ADMIN %s: has given you %d Molotov cocktails!", adminName, MOLOTOV_HARD_LIMIT);                  client_print(iTarget, print_chat, "ADMIN %s has given you %d Molotov cocktails!", sAdmin, iGiveAmount);
847    
848          }          }
849          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
850  }  }
851    
852    // Handle the /molotov command and molotov menu
853    #if defined CSTRIKE
854  public buy_molotov(id) {  public buy_molotov(id) {
855    
856          if (!get_pcvar_num(pEnabled)) {          if (!get_pcvar_num(pEnabled)) {
857                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
858          }          }
859    
860          if (get_pcvar_num(pOverride)) {          //if (get_pcvar_num(pOverride)) {
861                  if (get_pcvar_num(pBuyMenu)) {          //      client_print(id, print_center, "Just buy a HE grenade and get Molotov automatically!");
862                          client_print(id, print_center, "Buy them in the buy equipment menu.");          //      return PLUGIN_HANDLED;
863                  } else {          //}
                         client_print(id, print_center, "Just buy a HE grenade and get molotov automatically!");  
                 }  
                 return PLUGIN_HANDLED;  
         }  
864    
865          if (!is_user_alive(id)) {          if (!is_user_alive(id)) {
866                  client_print(id, print_center, "You can't buy Molotov cocktails because you are dead.");                  client_print(id, print_center, "You can't buy Molotov cocktails because you are dead.");
# Line 591  Line 872 
872                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
873          }          }
874    
875          new money = cs_get_user_money(id);          new iMoney = cs_get_user_money(id);
876    
877          if (money < get_pcvar_num(pPrice)) {          if (iMoney < get_pcvar_num(pPrice)) {
878                  client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");                  client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");
879                  return PLUGIN_HANDLED;                  return PLUGIN_HANDLED;
880          }          }
881    
882          if (g_hasMolotov[id] == get_pcvar_num(pMaxMolotovs)) {          if (!g_NumMolotov[ID_TO_INDEX(id)] && user_has_weapon(id, CSW_HEGRENADE)) {
883                  if (g_hasMolotov[id] == 1) {                  if (get_pcvar_num(pOverride)) {
884                          client_print(id, print_center, "You already have a Molotov cocktail.");                          g_NumMolotov[ID_TO_INDEX(id)] = cs_get_user_bpammo(id, CSW_HEGRENADE);  // If the user buys one from the VGUI menu with the override enabled, this updates g_NumMolotov
885                  } else {                  } else {
886                          client_print(id, print_center, "You already have %d Molotov cocktails.", g_hasMolotov[id]);                          client_print(id, print_center, "You already have an HE Grenade.");
                 }  
                 return PLUGIN_HANDLED;  
         }  
   
         if (!g_hasMolotov[id] && user_has_weapon(id, CSW_HEGRENADE)) {  
                 client_print(id, print_center, "You already have a HE Grenade.");  
                 return PLUGIN_HANDLED;  
         }  
   
         cs_set_user_money(id, money - get_pcvar_num(pPrice));  
         fm_give_item(id, "weapon_hegrenade");  
         cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_hasMolotov[id]);  
         client_print(id, print_chat, "You got a Molotov cocktail!");  
   
887          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
888  }  }
   
 public event_BuyMenuCT(id) {  
   
         if (!get_pcvar_num(pEnabled) || !get_pcvar_num(pBuyMenu)) {  
                 return PLUGIN_CONTINUE;  
         }  
   
         new Override = get_pcvar_num(pOverride);  
   
         new menu[1024];  
         new len = formatex(menu, 1023, "\yBuy Equipment\R$ Cost");  
   
         len += formatex(menu[len], 1023-len, "^n^n\w1. Kevlar Vest\R\y650");  
         len += formatex(menu[len], 1023-len, "^n\w2. Kevlar Vest & Helmet\R\y1000");  
         len += formatex(menu[len], 1023-len, "^n\w3. Flashbang\R\y200");  
   
         if (Override) {  
                 len += formatex(menu[len], 1023-len, "^n\w4. Molotov Cocktail\R\y%d", get_pcvar_num(pPrice));  
         } else {  
                 len += formatex(menu[len], 1023-len, "^n\w4. HE Grenade\R\y300");  
         }  
   
         len += formatex(menu[len], 1023-len, "^n\w5. Smoke Grenade\R\y300");  
         len += formatex(menu[len], 1023-len, "^n\w6. NightVision Goggles\R\y1250");  
         len += formatex(menu[len], 1023-len, "^n\%c7. Defuse Kit\R\y200 ", g_bomb_map ? 'w' : 'd');  
         len += formatex(menu[len], 1023-len, "^n\w8. Tactical Shield\R\y2200");  
   
         if (!Override) {  
                 len += formatex(menu[len], 1023-len, "^n\w9. Molotov Cocktail\R\y%d", get_pcvar_num(pPrice));  
889          }          }
890    
891          len += formatex(menu[len], 1023-len, "^n^n\w0. Exit");          if (g_NumMolotov[ID_TO_INDEX(id)] == get_pcvar_num(pMaxMolotovs)) {
892                    if (g_NumMolotov[ID_TO_INDEX(id)] == 1) {
893          show_menu(id, read_data(1)|MENU_KEY_9, menu, -1, "#CT_BuyItem");                          client_print(id, print_center, "You already have a Molotov cocktail.");
   
         return PLUGIN_HANDLED;  
 }  
   
 public event_BuyMenuT(id) {  
   
         if (!get_pcvar_num(pEnabled) || !get_pcvar_num(pBuyMenu)) {  
                 return PLUGIN_CONTINUE;  
         }  
   
         new Override = get_pcvar_num(pOverride);  
   
         new menu[1024];  
         new len = formatex(menu, 1023, "\yBuy Equipment\R$ Cost");  
         len += formatex(menu[len], 1023-len, "^n^n\w1. Kevlar Vest\R\y650");  
         len += formatex(menu[len], 1023-len, "^n\w2. Kevlar Vest & Helmet\R\y1000");  
         len += formatex(menu[len], 1023-len, "^n\w3. Flashbang\R\y200");  
   
         if (Override) {  
                 len += formatex(menu[len], 1023-len, "^n\w4. Molotov Cocktail\R\y%d", get_pcvar_num(pPrice));  
894          } else {          } else {
895                  len += formatex(menu[len], 1023-len, "^n\w4. HE Grenade\R\y300");                          client_print(id, print_center, "You already have %d Molotov cocktails.", g_NumMolotov[ID_TO_INDEX(id)]);
         }  
   
         len += formatex(menu[len], 1023-len, "^n\w5. Smoke Grenade\R\y300");  
         len += formatex(menu[len], 1023-len, "^n\w6. NightVision Goggles\R\y1250");  
   
         if (!Override) {  
                 len += formatex(menu[len], 1023-len, "^n\w7. Molotov Cocktail\R\y%d", get_pcvar_num(pPrice));  
896          }          }
   
         len += formatex(menu[len], 1023-len, "^n^n\w0. Exit");  
   
         show_menu(id, read_data(1)|MENU_KEY_7, menu, -1, "#T_BuyItem");  
   
897          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
898  }  }
899    
900  public handle_BuyMenuCT(id, key) {          cs_set_user_money(id, iMoney - get_pcvar_num(pPrice));
901            fm_give_item(id, WEAPON_HEGRENADE);
902            cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_NumMolotov[ID_TO_INDEX(id)]);
903    
         if (key == (get_pcvar_num(pOverride) ? 3 : 8)) {  
                 handle_BuyMenu(id);  
                 return PLUGIN_HANDLED;  
         }  
904    
         return PLUGIN_CONTINUE;  
 }  
   
 public handle_BuyMenuT(id, key) {  
   
         if (key == (get_pcvar_num(pOverride) ? 3 : 6)) {  
                 handle_BuyMenu(id);  
                 return PLUGIN_HANDLED;  
         }  
   
         return PLUGIN_CONTINUE;  
 }  
   
 stock handle_BuyMenu(id) {  
   
         new money = cs_get_user_money(id);  
   
         if (money < get_pcvar_num(pPrice)) {  
                 client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");  
                 return PLUGIN_HANDLED;  
         }  
   
         if (g_hasMolotov[id] == get_pcvar_num(pMaxMolotovs)) {  
                 if (g_hasMolotov[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_hasMolotov[id]);  
                 }  
                 return PLUGIN_HANDLED;  
         } else if (!g_hasMolotov[id] && user_has_weapon(id, CSW_HEGRENADE)) {  
                 client_print(id, print_center, "You already have a HE Grenade.");  
                 return PLUGIN_HANDLED;  
         }  
   
         cs_set_user_money(id, money - get_pcvar_num(pPrice));  
         fm_give_item(id, "weapon_hegrenade");  
         cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_hasMolotov[id]);  
905          client_print(id, print_chat, "You got a Molotov cocktail!");          client_print(id, print_chat, "You got a Molotov cocktail!");
906    
907          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
908  }  }
909    #endif
910    
911    // Just before the grenade is thrown, change the model
912    #if defined CSTRIKE || defined DOD
913  public grenade_throw(id, ent, wid) {  public grenade_throw(id, ent, wid) {
914    #if defined CSTRIKE
915          if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_HEGRENADE) {          if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_HEGRENADE) {
916    #else
917            #if defined DOD
918            // current weapon can be DODW_MILLS_BOMB in this forward, but not in CurWeapon
919            if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || ((wid != DODW_HANDGRENADE) && (wid != DODW_STICKGRENADE) && (wid != DODW_MILLS_BOMB))) {
920            #endif
921    #endif
922                  return PLUGIN_CONTINUE;                  return PLUGIN_CONTINUE;
923          }          }
924    
925          if (!g_hasMolotov[id] && !get_pcvar_num(pOverride)) {          if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {      // If no Molotovs and override is disabled, return
926                  return PLUGIN_CONTINUE;                  return PLUGIN_CONTINUE;
927          }          }
928    
929          g_hasMolotov[id]--;          if (g_NumMolotov[ID_TO_INDEX(id)] > 0) {        // Prevent negative values
930                    g_NumMolotov[ID_TO_INDEX(id)]--;
931            }
932    
933          engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");          engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
934          set_pev(ent, pev_nextthink, 99999.0);          set_pev(ent, pev_nextthink, 99999.0);
935    
936          return PLUGIN_CONTINUE;          custom_weapon_shot(g_wpnMolotov, id);
937    
938    #if defined CSTRIKE     // dod sets the team, cstrike doesn't, TFC sets this in fw_setmodel_post()
939            set_pev(ent, pev_team, get_user_team(id));
940    #endif
941    
942    #if defined DOD
943            //set_pev(id, pev_weaponanim, 0);               // 0:"idle"; 1:"pullpin"; 2:"throw"; 3:"deploy"
944    #endif
945    
946            return PLUGIN_HANDLED;
947  }  }
948    #endif
949    
950  public grenade_explode(ent) {  // Set up the explosion, sound, damage, etc.
951    molotov_explode(ent) {
952    
953          new param[6], iOrigin[3];          new param[7], iOrigin[3];
954          new Float:fOrigin[3];          new Float:fOrigin[3];
955          new owner = pev(ent, pev_owner);          new iOwner = pev(ent, pev_owner);
956            // The broken bottle may continue to travel, but the fire will be centered around the explosion site, marked by this temporary info_target entity.
957          new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));          new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));
958    
959          pev(ent, pev_origin, fOrigin);          pev(ent, pev_origin, fOrigin);
960    
961  #if defined MOLOTOV_DEBUG  #if defined MOLOTOV_DEBUG
962          new iOwnerAlive = is_user_alive(owner);          log_amx("[MC] molotov_explode ent(%d) owner(%d) ent2(%d) -----", ent, iOwner, ent2);
         new iOwnerConnected = is_user_connected(owner);  
         new iEnt2Valid = pev_valid(ent2);  
         log_amx("[MC] grenade_explode ent(%d) owner(%d) ent2(%d) iOwnerAlive(%d) iOwnerConnected(%d) iEnt2Valid(%d)", ent, owner, ent2, iOwnerAlive, iOwnerConnected, iEnt2Valid);  
963  #endif  #endif
964    
965          param[0] = ent;          param[0] = ent;
966          param[1] = ent2;          param[1] = ent2;
967          param[2] = owner;          param[2] = iOwner;
968          param[3] = iOrigin[0] = floatround(fOrigin[0]);          param[3] = pev(ent, pev_team);
969          param[4] = iOrigin[1] = floatround(fOrigin[1]);          param[4] = iOrigin[0] = floatround(fOrigin[0]);
970          param[5] = iOrigin[2] = floatround(fOrigin[2]);          param[5] = iOrigin[1] = floatround(fOrigin[1]);
971            param[6] = iOrigin[2] = floatround(fOrigin[2]);
         emit_sound(ent, CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);  
972    
973          engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");          engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");
974    
975          random_fire(iOrigin, ent2);          random_fire(iOrigin, ent2);
976          radius_damage(owner, fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST);          radius_damage2(iOwner, param[3], fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST, true);
977    
978            // If the round ends because of damage inflicted by the initial blast (in the previous line of code), skip any further Molotov effects.
979            // g_bReset may already be set, so it is safe to check it at this point.
980            if (g_bReset == true) {
981                    set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_KILLME);       // Remove the Molotov and later cancel the explosion
982                    return PLUGIN_HANDLED;
983            }
984    
985          new Float:FireTime = get_pcvar_float(pFireTime);          new Float:FireTime = get_pcvar_float(pFireTime);
986    
987          if (++g_Molotov_offset[owner] == MOLOTOV_HARD_LIMIT) {          if (++g_iMolotovOffset[ID_TO_INDEX(iOwner)] == MOLOTOV_HARD_LIMIT) {
988                  g_Molotov_offset[owner] = 0;                  g_iMolotovOffset[ID_TO_INDEX(iOwner)] = 0;
989          }          }
990    
991          set_task(0.2, "fire_damage", MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * (owner - 1)) + g_Molotov_offset[owner], param, 6, "a", floatround(FireTime / 0.2, floatround_floor));          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));
992          set_task(1.0, "fire_sound", MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * (owner - 1)) + g_Molotov_offset[owner], param, 6, "a", floatround(FireTime) - 1);          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);
993          set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (owner - 1)) + g_Molotov_offset[owner], param, 6);          // This task removes the broken Molotov and "info_target" entity once molotov_firetime has expired
994            set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7);
995    
996          return PLUGIN_CONTINUE;          return PLUGIN_CONTINUE;
997  }  }
998    
999    // 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
1000    #if defined TFC
1001    public cancel_reset() {
1002            g_bReset = false;
1003    }
1004    #endif
1005    
1006    // Make fire sounds
1007  public fire_sound(param[]) {  public fire_sound(param[]) {
1008          emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);          emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
1009  }  }
1010    
1011    // Remove Molotov entities
1012  public fire_stop(param[]) {  public fire_stop(param[]) {
1013          g_g = 0;          if (pev_valid(param[0])) { set_pev(param[0], pev_flags, pev(param[0], pev_flags) | FL_KILLME); }        // Molotov entity
1014            if (pev_valid(param[1])) { set_pev(param[1], pev_flags, pev(param[1], pev_flags) | FL_KILLME); }        // info_target entity
         if (pev_valid(param[0])) { engfunc(EngFunc_RemoveEntity, param[0]); }  
         if (pev_valid(param[1])) { engfunc(EngFunc_RemoveEntity, param[1]); }  
   
         if ((last_Molotov_ent = (param[0]))) {  
                 last_Molotov_ent = 0;  
         }  
1015  }  }
1016    
1017    // Call visual effect and damage functions
1018  public fire_damage(param[]) {  public fire_damage(param[]) {
1019    
1020          new iOrigin[3], Float:fOrigin[3];          new iOrigin[3], Float:fOrigin[3];
1021          iOrigin[0] = param[3];          iOrigin[0] = param[4];
1022          iOrigin[1] = param[4];          iOrigin[1] = param[5];
1023          iOrigin[2] = param[5];          iOrigin[2] = param[6];
1024    
1025          random_fire(iOrigin, param[1]);          random_fire(iOrigin, param[1]); // Visual effect
1026    
1027          IVecFVec(iOrigin, fOrigin);          IVecFVec(iOrigin, fOrigin);
1028          radius_damage(param[2], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, 0);          radius_damage2(param[2], param[3], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, false);    // Actual damage
1029  }  }
1030    
1031  stock radius_damage(attacker, Float:origin[3], Float:damage, Float:range, dmgtype, calc = 1) {  // There is a radius_damage() in engine, so this was renamed.
1032    stock radius_damage2(iAttacker, iAttackerTeam, Float:fOrigin[3], Float:fDamage, Float:fRange, iDmgType, bool:bCalc = true) {
1033    
1034          new Float:pOrigin[3], Float:dist, Float:tmpdmg;          new Float:pOrigin[3], Float:fDist, Float:fTmpDmg;
1035          new i, ateam = get_user_team(attacker), iFF = get_pcvar_num(pFriendlyFire);          new i, iFF = get_pcvar_num(pMFF);
1036    
1037            if (iFF == -1) {                        // Obey mp_friendlyfire
1038                    iFF = get_pcvar_num(pFriendlyFire);
1039    #if defined DOD || defined TFC
1040            } else if (iFF == -2) {         // Obey mp_teamplay (bit 5)
1041                    new iTeamPlay = get_pcvar_num(pTeamPlay);
1042                    if (iTeamPlay & (1 << 4)) {     // bit 5 (16 = teammates take no damage from explosive weaponfire)
1043                            iFF = 0;
1044                    }
1045    #endif
1046            }       // else, leave it at 0 or 1
1047    
1048          while (i++ < g_MaxPlayers) {          while (i++ < g_MaxPlayers) {
1049                  if (!is_user_alive(i)) {                  if (!is_user_alive(i)) {
1050                          continue;                          continue;
1051                  }                  }
1052    
1053                  if (!iFF && ateam == get_user_team(i)) {  #if defined TFC
1054                    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
1055    #else
1056                    if ((iFF == 0) && (iAttackerTeam == get_user_team(i))) {
1057    #endif
1058                          continue;                          continue;
1059                  }                  }
1060    
1061                  pev(i, pev_origin, pOrigin);                  pev(i, pev_origin, pOrigin);
1062                  dist = get_distance_f(origin, pOrigin);                  fDist = get_distance_f(fOrigin, pOrigin);
1063    
1064                  if (dist > range) {                  if (fDist > fRange) {
1065                          continue;                          continue;
1066                  }                  }
1067    
1068                  if (calc) {                  if (bCalc) {
1069                          tmpdmg = damage - (damage / range) * dist;                          fTmpDmg = fDamage - (fDamage / fRange) * fDist;
1070                  } else {                  } else {
1071                          tmpdmg = damage;                          fTmpDmg = fDamage;
1072                    }
1073    
1074                    if (floatround(fTmpDmg) > 0) {  // This eliminated the "[CSX] Invalid damage 0" error
1075                            custom_weapon_dmg(g_wpnMolotov, iAttacker, i, floatround(fTmpDmg), 0);
1076                  }                  }
1077    
1078                  if (pev(i, pev_health) < tmpdmg) {                  if (pev(i, pev_health) <= fTmpDmg) {
1079                          kill(attacker, i);                          kill(iAttacker, i, iAttackerTeam);
1080                  } else {                  } else {
1081                          fm_fakedamage(i, "molotov", tmpdmg, dmgtype);                          fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
1082                  }                  }
1083          }          }
1084    
1085          while ((i = engfunc(EngFunc_FindEntityInSphere, i, origin, range))) {   // warning 211: possibly unintended assignment          // At this point, i is one higher than the highest possible player ID, so this loop only affects non-player entities
1086            while ((i = engfunc(EngFunc_FindEntityInSphere, i, fOrigin, fRange))) { // Extra parentheses fix warning 211: possibly unintended assignment
1087                  if (pev(i, pev_takedamage)) {                  if (pev(i, pev_takedamage)) {
1088                          if (calc) {                          if (bCalc) {
1089                                  pev(i, pev_origin, pOrigin);                                  pev(i, pev_origin, pOrigin);
1090                                  tmpdmg = damage - (damage / range) * get_distance_f(origin, pOrigin);                                  fTmpDmg = fDamage - (fDamage / fRange) * get_distance_f(fOrigin, pOrigin);
1091                          } else {                          } else {
1092                                  tmpdmg = damage;                                  fTmpDmg = fDamage;
1093                          }                          }
1094                          fm_fakedamage(i, "molotov", tmpdmg, dmgtype);                          fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
1095                  }                  }
1096          }          }
1097  }  }
1098    
1099    // This stock only creates the visual effect. It does not handle any damage.
1100    // I tried using TE_FIREFIELD, but I can't make it look good in dod.
1101  stock random_fire(Origin[3], ent) {  stock random_fire(Origin[3], ent) {
1102    
1103          new range = get_pcvar_num(pMlRadius);          static iRange, iOrigin[3], g_g, i;
1104          new iOrigin[3];  
1105            iRange = get_pcvar_num(pMlRadius);
1106    
1107          for (new i = 1; i <= 5; i++) {          for (i = 1; i <= 5; i++) {
1108    
1109                  g_g = 1;                  g_g = 1;
1110    
1111                  iOrigin[0] = Origin[0] + random_num(-range, range);                  iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
1112                  iOrigin[1] = Origin[1] + random_num(-range, range);                  iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
1113                  iOrigin[2] = Origin[2];                  iOrigin[2] = Origin[2];
1114                  iOrigin[2] = ground_z(iOrigin, ent);                  iOrigin[2] = ground_z(iOrigin, ent);
1115    
1116                  while (get_distance(iOrigin, Origin) > range) {                  while (get_distance(iOrigin, Origin) > iRange) {                // If iOrigin is too far away, recalculate its position
1117                          g_g++;  
1118  #if defined MOLOTOV_DEBUG                          iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
1119                          //log_amx("[MC] random_fire ent(%d) i(%d) g_g(%d)", ent, i, g_g);                          iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
 #endif  
                         iOrigin[0] = Origin[0] + random_num(-range, range);  
                         iOrigin[1] = Origin[1] + random_num(-range, range);  
1120                          iOrigin[2] = Origin[2];                          iOrigin[2] = Origin[2];
1121    
1122                          if (g_g >= ANTI_LAGG) {                          if (++g_g >= ANTI_LAGG) {
1123                                  iOrigin[2] = ground_z(iOrigin, ent, 1);                                  iOrigin[2] = ground_z(iOrigin, ent, 1);
1124                          } else {                          } else {
1125                                  iOrigin[2] = ground_z(iOrigin, ent);                                  iOrigin[2] = ground_z(iOrigin, ent);
# Line 909  Line 1130 
1130    
1131                  message_begin(MSG_BROADCAST, SVC_TEMPENTITY);                  message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
1132                  write_byte(TE_SPRITE);                  write_byte(TE_SPRITE);
1133                  write_coord(iOrigin[0]);                  write_coord(iOrigin[0]);        // Position
1134                  write_coord(iOrigin[1]);                  write_coord(iOrigin[1]);
1135                  write_coord(iOrigin[2] + rand * 5);                  write_coord(iOrigin[2] + rand * 5);
1136                  write_short(firespr);                  write_short(g_iFireSprite);     // Sprite index
1137                  write_byte(rand);                  write_byte(rand);               // Scale
1138                  write_byte(100);                  write_byte(100);                // Brightness
1139                  message_end();                  message_end();
1140            }
1141    
1142                  if (!(i % 4)) { // Smoke every 4th flame          // One smoke puff for each call to random_fire, regardless of number of flames
1143                          message_begin(MSG_BROADCAST, SVC_TEMPENTITY);                          message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
1144                          write_byte(TE_SMOKE);                          write_byte(TE_SMOKE);
1145                          write_coord(iOrigin[0]);          write_coord(iOrigin[0]);                        // Position
1146                          write_coord(iOrigin[1]);                          write_coord(iOrigin[1]);
1147                          write_coord(iOrigin[2] + 120);                          write_coord(iOrigin[2] + 120);
1148                          write_short(smokespr[random_num(0, 1)]);          write_short(g_iSmokeSprite[random_num(0, 1)]);  // Sprite index
1149                          write_byte(random_num(10, 30));          write_byte(random_num(10, 30));                 // Scale
1150                          write_byte(random_num(10, 20));          write_byte(random_num(10, 20));                 // Framerate
1151                          message_end();                          message_end();
1152    
1153    }
1154    
1155    // Stop all visual effect/physical damage tasks
1156    stock reset_tasks() {
1157    #if defined MOLOTOV_DEBUG
1158            new tmpdbgid;
1159    #endif
1160            for (new i; i < g_MaxPlayers; i++) {    // for 0..31
1161                    for (new o; o < MOLOTOV_TASKID_OFFSET; o++) {
1162                            if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
1163                                    remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o);
1164    #if defined MOLOTOV_DEBUG
1165                                    tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o;
1166                                    log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
1167    #endif
1168                            }
1169    
1170                            if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
1171                                    remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o);
1172    #if defined MOLOTOV_DEBUG
1173                                    tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o;
1174                                    log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
1175    #endif
1176                            }
1177                            // The third task for each Molotov is not stopped so it can remove the Molotov/info_target entities.
1178                  }                  }
1179          }          }
1180  }  }
1181    
1182  stock kill(k, v) {  // This function handles the killing and scoring.
1183    //   iKillerTeam is stored because the killer can disconnect before the Molotov kills someone, leading to inaccurate scoring
1184    stock kill(iKiller, iVictim, iKillerTeam) {
1185    
1186    //TFC:  DeathMsg, ScoreInfo, ScoreInfo          // One ScoreInfo for killer and one for victim (order varies)
1187    //DOD:  DeathMsg, ScoreShort, Frags             // ScoreShort=victim, Frags=killer
1188    // CS:  DeathMsg, Money, ScoreInfo, ScoreInfo
1189    
1190    //      Scoreboard
1191    // CSTRIKE:     Score Deaths
1192    // DMC:         Frags Deaths
1193    // DOD:         Score Kills Deaths
1194    // HL:          Score Deaths
1195    // HLOF:        Kills Deaths
1196    // TFC:         Score Deaths
1197    // Ricochet:    Points
1198    /*                              ----- Attacker -----            ------ Victim ------
1199                                    Score   Deaths  Kills           Score   Death   Kills
1200            CS  kill                +1      -       N/A             -       +1      N/A
1201            CS  team kill           -1      -       N/A             -       +1      N/A
1202            CS  suicide             -1      +1      N/A             ---------------------
1203            CS  detonate/defuse     +3      -       N/A             ---------------------
1204            DOD kill                -       -       +1              -       +1      -
1205            DOD team kill           -       -       -               -       +1      -       (mp_tkpenalty handles punishment)
1206            DOD suicide             -       +1      -               ---------------------
1207            DOD cap                 +1      -       -               ---------------------
1208            TFC kill                +1      -       N/A             -       +1      N/A
1209            TFC team kill           -1      -       N/A             -       +1      N/A
1210            TFC suicide             -1      +1      N/A             ---------------------
1211            TFC cap/control         varies  -       N/A             ---------------------
1212    */
1213    
1214    
1215    // ------------------------------------------------------------------------------------------------- DeathMsg (CS, DOD, TFC)
1216            // DeathMsg - Triggers HUD message and player console message
1217            // DOD and CSTRIKE have different formats for DeathMsg; most other mods should be default
1218            message_begin(MSG_ALL, g_msgDeathMsg, {0,0,0}, 0);
1219            write_byte(iKiller);            // Killer ID
1220            write_byte(iVictim);            // Victim ID
1221    #if defined CSTRIKE
1222            write_byte(0);                  // Is Headshot?
1223    #endif
1224    #if defined DOD
1225            write_byte(0);                  // Weapon ID - These don't match the DODW_* constants, and a custom weapon ID does not work, so we use "world"
1226    #else
1227            write_string("molotov");        // Truncated Weapon Name
1228    #endif
1229            message_end();
1230    // ------------------------------------------------------------------------------------------------- /DeathMsg (CS, DOD, TFC)
1231    
1232          user_silentkill(v);          // This block of code actually kills the user (silently - DeathMsg was already created)
1233            new iVictimTeam = get_user_team(iVictim);
1234            new iMsgBlock = get_msg_block(g_msgDeathMsg);   // Store original block value
1235            set_msg_block(g_msgDeathMsg, BLOCK_ONCE);       // Start blocking DeathMsg
1236    
1237          new kteam = get_user_team(k);  #if defined CSTRIKE
         new vteam = get_user_team(v);  
1238    
1239          new kfrags = get_user_frags(k) + 1;          new iKillerFrags = get_user_frags(iKiller);
1240          new kdeaths = get_user_deaths(k);          new iVictimFrags = get_user_frags(iVictim);
1241          if (kteam == vteam) {  
1242                  kfrags = get_user_frags(k) - 2;          // TFC and CS scoring are mostly the same. See TFC comment. (I did most of my testing with TFC first)
1243            if (iKiller != iVictim) {
1244                    fm_set_user_frags(iVictim, iVictimFrags + 1);                   // Add frag that user_kill() will remove
1245          }          }
1246    
1247          new vfrags = get_user_frags(v);          if (iKillerTeam != iVictimTeam) {
1248          new vdeaths = get_user_deaths(v);                  iKillerFrags++;                                                 // Killer's Score = Score + 1
1249            } else {
1250                    iKillerFrags--;                                                 // Killer's Score = Score - 1
1251            }
1252            fm_set_user_frags(iKiller, iKillerFrags);
1253    
1254          message_begin(MSG_ALL, gmsgScoreInfo);          //      CSTRIKE Results --------------------------------------------------------------------------------------------------
1255          write_byte(k);                                                  //      DeathMsg        ScoreInfoMsg    KScore  KDeath  VScore  VDeath  Internally
1256          write_short(kfrags);          //user_kill(iVictim, 0);                //      Yes             Yes (Victim)    0       0       -1      +1      Everything matches
1257          write_short(kdeaths);          //user_kill(iVictim, 1);                //      Yes             Yes (Victim)    0       0       -1      +1      VScore is different internally (unchanged)
1258          write_short(0);          //user_silentkill(iVictim);             //      No              Yes (Victim)    0       0       -1      +1      VScore is different internally (unchanged)
         write_short(kteam);  
         message_end();  
1259    
1260          message_begin(MSG_ALL, gmsgScoreInfo);          //dllfunc(DLLFunc_ClientKill, iVictim); //      Yes             Yes (Victim)    0       0       -1      +1      Everything matches
1261          write_byte(v);          //fm_user_kill(iVictim, 0);             //      Yes             Yes (Victim)    0       0       -1      +1      Everything matches
1262          write_short(vfrags + 1);          //fm_user_kill(iVictim, 1);             //      Yes             Yes (Victim)    0       0        0      +1      Everything matches (fm_user_kill adds 1 to VScore)
         write_short(vdeaths);  
         write_short(0);  
         write_short(vteam);  
         message_end();  
1263    
1264          message_begin(MSG_ALL, gmsgDeathMsg, {0,0,0}, 0);          // user_silentkill() blocks the DeathMsg, but it doesn't update the scoreboard properly
1265          write_byte(k);          // fm_user_kill() updates the scoreboard properly, but generates a DeathMsg with Victim=Killer, so we have to block that.
1266          write_byte(v);  #endif
         write_byte(0);  
         write_string("molotov");  
         message_end();  
1267    
1268          g_frags[k]++;  #if defined TFC
1269            new iVictimFrags = tfc_get_user_frags(iVictim);
1270    
1271          if (kteam != vteam) {          // TFC treats every type of kill in code as a suicide and takes away 1 frag from the victim.
1272                  cs_set_user_money(k, cs_get_user_money(k) + 300);          //   After *extensive* testing, I found that the easiest solution is to increment the victim frags
1273          } else {          //   (stored in pdata) beforehand and let the game decrement it and send out ScoreInfo messages.
1274                  cs_set_user_money(k, cs_get_user_money(k) - 300);          //   This applies to *victims* of team kills as well as normal kills. Suicides still lose a frag.
1275            if (iKiller != iVictim) {
1276                    tfc_set_user_frags(iVictim, iVictimFrags + 1);
1277          }          }
1278    
1279            if ((iKillerTeam == iVictimTeam) || tfc_is_team_ally(iKillerTeam, iVictimTeam)) {       // TODO: tfc_is_team_ally() is broken in AMX Mod X
1280                    tfc_set_user_frags(iKiller, get_user_frags(iKiller) - 1);       // Killer's Score = Score - 1
1281            } else {
1282                    tfc_set_user_frags(iKiller, get_user_frags(iKiller) + 1);       // Killer's Score = Score + 1
1283  }  }
1284    
1285  stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {          //      TFC Results     --------------------------------------------------------------------------
1286                                                    //      DeathMsg        ScoreInfoMsg    VFrag   Internally
1287            //user_kill(iVictim, 0);                //      Yes             Yes             -1      Everything matches
1288            //user_kill(iVictim, 1);                //      Yes             Yes              0      VFrags doesn't match
1289            //user_silentkill(iVictim);             //      No              Yes              0      VFrags doesn't match
1290    
1291  #if defined MOLOTOV_DEBUG          //dllfunc(DLLFunc_ClientKill, iVictim); //      Yes             Yes             -1      Everything matches
1292          new iEntValid = pev_valid(ent);          //fm_user_kill(iVictim, 0);             //      Yes             Yes             -1      Everything matches
1293          if (iRecursion > 0) {          //fm_user_kill(iVictim, 1);             //      Yes             Yes             -1      Everything matches
1294                  log_amx("[MC] ground_z ent(%d) iEntValid(%d) skip(%d) iRecursion(%d)", ent, iEntValid, skip, iRecursion);  
1295    #endif
1296    
1297            // DOD just works..
1298            //      DOD Results     --------------------------------------------------------------------------------------------------
1299                                                    //      DeathMsg        ScoreShortMsg   FragMsg KFrag   VDeath  VFrag   Internally
1300            //user_kill(iVictim, 0);                //      Yes             Yes             N       0       +1       0      Everything matches
1301            //user_kill(iVictim, 1);                //      Yes             Yes             N       0       +1       0      Everything matches
1302            //user_silentkill(iVictim);             //      No              Yes             N       0       +1       0      Everything matches
1303    
1304            //dllfunc(DLLFunc_ClientKill, iVictim); //      Yes             Yes             N       0       +1       0      Everything matches
1305            //fm_user_kill(iVictim, 0);             //      Yes             Yes             N       0       +1       0      Everything matches
1306            //fm_user_kill(iVictim, 1);             //      Yes             Yes             N       0       +1      +1      OK, but VFrags shouldn't be changed
1307    
1308            user_kill(iVictim, 0);
1309            set_msg_block(g_msgDeathMsg, iMsgBlock);                // Stop blocking DeathMsg
1310    
1311            //CSTRIKE client console messages:
1312            //Kill  "Player1 killed [P0D]M0rbid Desire (2) with molotov"
1313            //TK    "Player1 killed [POD]Kate_Winslet (2) with molotov"
1314            //Self  "Player1 killed self with molotov"
1315    
1316            //DOD client console messages: (DOD uses indexes instead of strings and a custom weapon name can't be sent to the player console)
1317            //Kill  "Player1 killed Sgt.Moving_Target with world"
1318            //TK    "Player1 killed his teammate Sgt.dontSHOOTiJUSTwannaTALK with world"
1319            //Self  "Player1 killed self"
1320    
1321            //TFC client console messages:
1322            //Kill  "Player1 killed [FoX]JesseJames with molotov"
1323            //TK    "Player1 killed [FoX]Barry with molotov"
1324            //Self  "Player1 killed self with molotov"
1325    
1326    
1327            // 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
1328            new sVictim[32], sVictimAuth[35], sVictimTeam[32];
1329            get_user_name(iVictim, sVictim, charsmax(sVictim));
1330            get_user_authid(iVictim, sVictimAuth, charsmax(sVictimAuth));
1331            get_user_team(iVictim, sVictimTeam, charsmax(sVictimTeam));     // TERRORIST, CT, Allies, Axis, #Dustbowl_team1 (Attackers/Blue), #Dustbowl_team2 (Defenders/Red)
1332            if (iKiller == iVictim) {
1333                    log_message("^"%s<%d><%s><%s>^" committed suicide with ^"molotov^"", sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
1334            } else if (is_user_connected(iKiller)) {
1335                    new sKiller[32], sKillerAuth[35], sKillerTeam[32];
1336                    get_user_name(iKiller, sKiller, charsmax(sKiller));
1337                    get_user_authid(iKiller, sKillerAuth, charsmax(sKillerAuth));
1338                    get_user_team(iKiller, sKillerTeam, charsmax(sKillerTeam));
1339                    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);
1340            }
1341            // TODO: There currently isn't a log message for a kill by a disconnected player. The wiki doesn't show the expected format.
1342    
1343    // ------------------------------------------------------------------------------------------------- Money (CS)
1344    #if defined CSTRIKE
1345            new iMoney;
1346            if (iKillerTeam == iVictimTeam) {
1347                    iMoney = cs_get_user_money(iKiller) - 3300;                     // TODO - $1500 hostage kill penalty
1348                    cs_set_user_money(iKiller, iMoney < 0 ? 0 : iMoney);
1349            } else {
1350                    iMoney = cs_get_user_money(iKiller) + 300;
1351                    cs_set_user_money(iKiller, iMoney > 16000 ? 16000 : iMoney);
1352          }          }
1353  #endif  #endif
1354    // ------------------------------------------------------------------------------------------------- /Money (CS)
1355    // ------------------------------------------------------------------------------------------------- ScoreInfo (CS and TFC)
1356            // ScoreInfo - Updates scoreboard on clients (actual values are changed elsewhere)
1357            // TFC - ScoreInfo messages are sent automatically after killing a player.
1358            // CS - ScoreInfo is sent automatically for the victim, but not killer.
1359    #if defined CSTRIKE
1360            message_begin(MSG_ALL, g_msgScoreInfo);         // Killer ScoreInfo
1361            write_byte(iKiller);
1362            write_short(iKillerFrags);
1363            write_short(get_user_deaths(iKiller));
1364            write_short(0);
1365            write_short(iKillerTeam);
1366            message_end();
1367    #endif
1368    // ------------------------------------------------------------------------------------------------- /ScoreInfo (CS and TFC)
1369    
1370    #if defined DOD
1371    // ------------------------------------------------------------------------------------------------- ScoreShort (DOD)
1372            // ScoreShort is sent by user_kill()
1373    // ------------------------------------------------------------------------------------------------- /ScoreShort (DOD)
1374    // ------------------------------------------------------------------------------------------------- Frags (DOD)
1375            if (iKillerTeam != iVictimTeam) {       // Only give a frag if the player killed was an enemy (not suicide or TK)
1376                    dod_set_user_kills(iKiller, dod_get_user_kills(iKiller) + 1, 1);        // These natives seem to work properly.
1377            }
1378    // ------------------------------------------------------------------------------------------------- /Frags (DOD)
1379    #endif
1380    }
1381    
1382    // Attempt to drop the passed coordinates to ground level
1383    stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {
1384    
1385          iOrigin[2] += random_num(5, 80);          iOrigin[2] += random_num(5, 80);
1386    
1387          if (!pev_valid(ent)) {  // Fix for: Run time error 10: native error (native "set_pev")          if (!pev_valid(ent)) {
1388                  return iOrigin[2];                  return iOrigin[2];
1389          }          }
1390    
1391          new Float:fOrigin[3];          new Float:fOrigin[3];
   
1392          IVecFVec(iOrigin, fOrigin);          IVecFVec(iOrigin, fOrigin);
   
1393          set_pev(ent, pev_origin, fOrigin);          set_pev(ent, pev_origin, fOrigin);
   
1394          engfunc(EngFunc_DropToFloor, ent);          engfunc(EngFunc_DropToFloor, ent);
1395    
1396          if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) {          if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) {
1397                  if (iRecursion >= ANTI_LAGG) {                  if (iRecursion >= ANTI_LAGG) {
1398                          skip = 1;                          skip = 1;
1399                  }                  }
1400  #if defined MOLOTOV_DEBUG  
                 log_amx("[MC] ground_z ++iRecursion(%d)", ++iRecursion);  
                 return ground_z(iOrigin, ent, skip, iRecursion);  
 #else  
1401                  return ground_z(iOrigin, ent, skip, ++iRecursion);                  return ground_z(iOrigin, ent, skip, ++iRecursion);
 #endif  
1402          }          }
1403    
1404          pev(ent, pev_origin, fOrigin);          pev(ent, pev_origin, fOrigin);
# Line 1018  Line 1406 
1406          return floatround(fOrigin[2]);          return floatround(fOrigin[2]);
1407  }  }
1408    
1409    // If plugin or override is disabled, reset Molotov models to original models
1410    #if defined CSTRIKE || defined DOD
1411  stock reset_molotovs() {  stock reset_molotovs() {
1412          new ent = g_MaxPlayers;          new ent = g_MaxPlayers;
1413    #if defined CSTRIKE
1414            // TODO - My limited testing showed this code is pointless.
1415            //   It has no negative effect, so I'm leaving it for the 3.30 release.
1416            //   (I don't think "model" is a valid parameter.)
1417            new iOwner;
1418          while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {          while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
1419                    iOwner = pev(ent, pev_owner);
1420    #if defined MOLOTOV_DEBUG
1421                    client_print(0, print_chat, "reset_molotovs - found one Molotov! Owner(%d)", iOwner);
1422    #endif
1423                    // If plugin is disabled or player owns no molotovs, reset their model
1424                    if (!get_pcvar_num(pEnabled) || !g_NumMolotov[ID_TO_INDEX(iOwner)]) {
1425                  engfunc(EngFunc_SetModel, ent, "models/w_hegrenade.mdl");                  engfunc(EngFunc_SetModel, ent, "models/w_hegrenade.mdl");
1426          }          }
1427  }  }
1428    #endif
1429    #if defined DOD
1430            new iOwner;
1431            while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
1432    #if defined MOLOTOV_DEBUG
1433                    client_print(0, print_chat, "reset_molotovs - found one Molotov!");
1434    #endif
1435    
1436                    iOwner = pev(ent, pev_owner);
1437                    if (iOwner) {
1438                            switch(get_user_team(iOwner)) {
1439                                    case ALLIES: {  // (or British)
1440                                            engfunc(EngFunc_SetModel, ent, "models/w_grenade.mdl");         // Mills is the same model, so this is probably fine
1441                                    }
1442                                    case AXIS: {
1443                                            engfunc(EngFunc_SetModel, ent, "models/w_stick.mdl");
1444                                    }
1445                            }
1446                    }
1447            }
1448    #endif
1449    
1450    }
1451    #endif
1452    
1453    // Mods that show the model before it is thrown need the model set (I think)
1454    #if defined CSTRIKE || defined DOD
1455  stock set_molotovs() {  stock set_molotovs() {
1456          new ent = g_MaxPlayers;          new ent = g_MaxPlayers;
1457    #if defined CSTRIKE
1458          while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_hegrenade.mdl"))) {          while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_hegrenade.mdl"))) {
1459    #if defined MOLOTOV_DEBUG
1460                    client_print(0, print_chat, "set_molotovs - found one hegrenade!");
1461    #endif
1462                    engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
1463            }
1464    #endif
1465    #if defined DOD
1466            while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_grenade.mdl"))) {
1467    #if defined MOLOTOV_DEBUG
1468                    client_print(0, print_chat, "set_molotovs - found one grenade!");
1469    #endif
1470                  engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");                  engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
1471          }          }
1472            ent = g_MaxPlayers;
1473            while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_stick.mdl"))) {
1474    #if defined MOLOTOV_DEBUG
1475                    client_print(0, print_chat, "set_molotovs - found one stick!");
1476    #endif
1477                    engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
1478  }  }
1479            ent = g_MaxPlayers;
1480            while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_mills.mdl"))) {
1481    #if defined MOLOTOV_DEBUG
1482                    client_print(0, print_chat, "set_molotovs - found one Mills!");
1483    #endif
1484                    engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
1485            }
1486    #endif
1487    
1488    }
1489    #endif
1490    
1491  // This will run at event New Round if enabled  // Show optional buy menu
1492    #if defined CSTRIKE
1493  public show_molotov_menu(id) {  public show_molotov_menu(id) {
1494          new menu[128];          new menu[128];
1495          formatex(menu, 127, "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));          formatex(menu, charsmax(menu), "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));
1496    
1497          // This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well          // This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well
1498          // when using 0.5 as mp_buytime. If you like, just change the time below          // when using 0.5 as mp_buytime. If you want to, just change the time below.
1499          show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);          show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);
1500    
1501          return PLUGIN_HANDLED;          return PLUGIN_HANDLED;
1502  }  }
1503    #endif
1504    
1505  //Our menu function will get the player id and the key they pressed  //Our menu function will get the player id and the key they pressed
1506    #if defined CSTRIKE
1507  public giveMolotov(id, key) {  public giveMolotov(id, key) {
1508    
1509          //key will start at zero          //key will start at zero
1510          switch(key) {          switch(key) {
1511                  case 0: buy_molotov(id);                  case 0: buy_molotov(id);
1512                  case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail");                  //I don't think these messages are necessary.
1513                  default: client_print(id, print_center, "You have chosen to exit the Molotov menu");                  //case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail");
1514                    //default: client_print(id, print_center, "You have chosen to exit the Molotov menu");
1515          }          }
1516  }  }
1517    #endif
1518    
1519    // Set user frags (score) in TFC
1520    #if defined TFC
1521    stock tfc_set_user_frags(iIndex, iNewFrags) {
1522            if (is_linux_server()) {
1523                    set_pdata_int(iIndex, 76, iNewFrags);           // real_frags = 76 (on Linux)           Required!
1524            } else {
1525                    set_pdata_int(iIndex, 77, iNewFrags);           // real_frags = 77 (on Windows)         Required?
1526            }       // Is there a mac version?
1527    
1528            // As far as I can tell, real_frags is what should be set, and something copies it to m_iClientFrags.
1529            //set_pdata_int(iIndex, 643, iNewFrags);                // m_iClientFrags = 643 (on Linux/Windows)
1530    
1531            // Sometimes this is required, sometimes it isn't. I think what is happening is that in
1532            //   the cases it doesn't seem to be required, pev_frags is getting updated internally.
1533            set_pev(iIndex, pev_frags, float(iNewFrags));
1534    
1535    #if defined MOLOTOV_DEBUG
1536            mydump(iIndex, 65, 85);
1537            mydump(iIndex, 635, 655);
1538    #endif
1539    }
1540    #endif
1541    
1542    // Return a user's frags, and verify that the TFC offsets haven't changed
1543    #if defined TFC
1544    stock tfc_get_user_frags(iIndex) {
1545            new iOffset = (is_linux_server() ? 76 : 77);    // real_frags = 76 (Linux), 77 (Windows)
1546            new iFrags = get_user_frags(iIndex);
1547    
1548            // This code is the easiest way to detect a change in the offsets and help prevent annoying troubleshooting.
1549            // get_user_frags seems to always return the correct value.
1550            if (iFrags != get_pdata_int(iIndex, iOffset)) {
1551                    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));
1552                    console_print(0, "WARNING! get_user_frags != real_frags! Contact plugin author!!!!!!!!!!!!!!!!!!!!");
1553            }
1554    
1555            return get_pdata_int(iIndex, iOffset);
1556    }
1557    #endif
1558    
1559    // This won't be compiled unless MOLOTOV_DEBUG is set (and only for TFC)
1560    #if defined TFC
1561    stock mydump(iIndex, iStart, iEnd) {
1562    
1563            new sLine[512];
1564            new FILE[] = "addons/amxmodx/logs/pdatadump.log";
1565    
1566            new sClassname[64];
1567            entity_get_string(iIndex, EV_SZ_classname, sClassname, charsmax(sClassname));
1568            format(sLine, charsmax(sLine), "Starting dump of entity %s %d", sClassname, iIndex);
1569            console_print(1, "%s to file %s", sLine, FILE);
1570            if (!write_file(FILE, sLine)) {
1571                    console_print(1, "Error dumping to %s!", FILE);
1572                    return PLUGIN_HANDLED;
1573            }
1574    
1575            for (new i = iStart; i <= iEnd; i++) {
1576                    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));
1577                    if (!write_file(FILE, sLine)) {
1578                            console_print(1, "Error dumping to %s!", FILE);
1579                            return PLUGIN_HANDLED;
1580                    }
1581            }
1582    
1583            console_print(1, "Dump done. Check %s!", FILE);
1584    
1585            return 1;
1586    }
1587    #endif

Legend:
Removed from v.26  
changed lines
  Added in v.61

Contact webmaster
ViewVC Help
Powered by ViewVC RSS 2.0 feed