Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
/**
* Sourcemod 1.7 Plugin Template
*/
#pragma semicolon 1
#include <sourcemod>
#include <tf2_stocks>
#pragma newdecls required
#define PLUGIN_VERSION "0.0.0"
public Plugin myinfo = {
name = "[TF2] Jungle Inferno Equipment",
author = "nosoop",
description = "Allows players to equip new weapons for Pyro and Heavy. "
... "Because screw the contract system.",
version = PLUGIN_VERSION,
url = "https://csrd.science/"
}
enum UpdateWeapon {
Weapon_DragonsFury,
Weapon_ThermalThruster,
Weapon_GasPasser,
Weapon_HotHand,
Weapon_SecondBanana
};
char INFERNO_WEAPON_LOADOUT_NAMES[UpdateWeapon][] = {
"The Dragon's Fury (Pyro Primary)",
"The Thermal Thruster (Pyro Secondary)",
"The Gas Passer (Pyro Secondary)",
"The Hot Hand (Pyro Melee)",
"The Second Banana (Heavy Secondary)"
};
int g_EquippedWeapons[MAXPLAYERS + 1];
bool g_bUpdateWeaponNotify[MAXPLAYERS + 1];
public void OnPluginStart() {
HookEvent("post_inventory_application", OnInventoryApplied);
HookEvent("player_disconnect", OnPlayerDisconnect);
RegConsoleCmd("sm_loadout", OpenLoadoutOverrideMenu);
}
public void OnPlayerDisconnect(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
g_EquippedWeapons[client] = 0;
g_bUpdateWeaponNotify[client] = false;
}
public Action OpenLoadoutOverrideMenu(int client, int argc) {
Menu loadoutMenu = new Menu(LoadoutUpdateCallback, MENU_ACTIONS_ALL);
loadoutMenu.SetTitle("Select an item to toggle. Items will persist until disconnect.");
for (int i = 0; i < view_as<int>(UpdateWeapon); i++) {
char menuInfo[8];
IntToString(i, menuInfo, sizeof(menuInfo));
// display will be overridden in the menu callback
loadoutMenu.AddItem(menuInfo, INFERNO_WEAPON_LOADOUT_NAMES[i]);
}
loadoutMenu.Display(client, 10);
return Plugin_Handled;
}
public int LoadoutUpdateCallback(Menu menu, MenuAction action, int param1, int param2) {
switch (action) {
case MenuAction_Select: {
int client = param1, selection = param2;
char menuInfo[8];
menu.GetItem(selection, menuInfo, sizeof(menuInfo));
UpdateWeapon menuItem = view_as<UpdateWeapon>(StringToInt(menuInfo));
ToggleUpdateWeaponOverride(client, menuItem);
menu.Display(client, 10);
}
case MenuAction_DisplayItem: {
int client = param1, item = param2;
char menuInfo[8];
menu.GetItem(item, menuInfo, sizeof(menuInfo));
UpdateWeapon menuItem = view_as<UpdateWeapon>(StringToInt(menuInfo));
char menuDisplay[128];
Format(menuDisplay, sizeof(menuDisplay), "[%s] %s",
IsUpdateWeaponOverride(client, menuItem) ? "x" : " ",
INFERNO_WEAPON_LOADOUT_NAMES[menuItem]);
return RedrawMenuItem(menuDisplay);
}
case MenuAction_End: {
int endReason = param1;
if (endReason == MenuEnd_Cancelled || endReason == MenuEnd_Exit) {
delete menu;
}
}
}
return 0;
}
public void OnInventoryApplied(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
if (!g_bUpdateWeaponNotify[client]) {
PrintToChat(client, "This server is allowing players to equip the new Jungle Inferno "
... "update weapons. Type /loadout to toggle them.");
g_bUpdateWeaponNotify[client] = true;
}
int userid = event.GetInt("userid");
RequestFrame(DeferedLoadoutOverride, userid);
}
public void DeferedLoadoutOverride(int userid) {
int client = GetClientOfUserId(userid);
if (!client) {
return;
}
// TODO replace with the correct defindices and weapon classes
switch(TF2_GetPlayerClass(client)) {
case TFClass_Pyro: {
if (IsUpdateWeaponOverride(client, Weapon_DragonsFury)) {
TF2_RemoveWeaponSlot(client, 0);
TF2_CreateAndEquipWeapon(client, 1178, "tf_weapon_rocketlauncher_fireball");
}
if (IsUpdateWeaponOverride(client, Weapon_ThermalThruster)) {
// ...
TF2_RemoveWeaponSlot(client, 1);
TF2_CreateAndEquipWeapon(client, 1179, "tf_weapon_rocketpack");
}
if (IsUpdateWeaponOverride(client, Weapon_GasPasser)) {
TF2_RemoveWeaponSlot(client, 1);
TF2_CreateAndEquipWeapon(client, 1180, "tf_weapon_jar_gas");
}
if (IsUpdateWeaponOverride(client, Weapon_HotHand)) {
TF2_RemoveWeaponSlot(client, 2);
TF2_CreateAndEquipWeapon(client, 1181, "tf_weapon_slap");
}
}
case TFClass_Heavy: {
if (IsUpdateWeaponOverride(client, Weapon_SecondBanana)) {
TF2_RemoveWeaponSlot(client, 1);
TF2_CreateAndEquipWeapon(client, 1190, "tf_weapon_lunchbox");
}
}
}
}
bool IsUpdateWeaponOverride(int client, UpdateWeapon weapon) {
return !!((g_EquippedWeapons[client] >> view_as<int>(weapon)) & 1);
}
bool ToggleUpdateWeaponOverride(int client, UpdateWeapon weapon) {
// disable the other secondary weapon
if (weapon == Weapon_ThermalThruster) {
g_EquippedWeapons[client] &= ~(1 << view_as<int>(Weapon_GasPasser));
} else if (weapon == Weapon_GasPasser) {
g_EquippedWeapons[client] &= ~(1 << view_as<int>(Weapon_ThermalThruster));
}
g_EquippedWeapons[client] ^= (1 << view_as<int>(weapon));
}
void TF2_CreateAndEquipWeapon(int client, int defindex, const char[] weaponClass) {
int weapon = TF2_CreateWeapon(defindex, weaponClass);
TF2_EquipPlayerWeapon(client, weapon);
// correctly set ammo
if (defindex == 1178) {
TF2_SetWeaponAmmo(weapon, 40);
}
}
/**
* Creates a weapon for the specified player.
*/
stock int TF2_CreateWeapon(int defindex, const char[] weaponClass) {
int weapon = CreateEntityByName(weaponClass);
if (IsValidEntity(weapon)) {
SetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex", defindex);
SetEntProp(weapon, Prop_Send, "m_bInitialized", 1);
// Allow quality / level override by updating through the offset.
char netClass[64];
GetEntityNetClass(weapon, netClass, sizeof(netClass));
SetEntData(weapon, FindSendPropInfo(netClass, "m_iEntityQuality"), 0);
SetEntData(weapon, FindSendPropInfo(netClass, "m_iEntityLevel"), 0);
DispatchSpawn(weapon);
}
return weapon;
}
stock void TF2_EquipPlayerWeapon(int client, int weapon) {
char weaponClass[64];
GetEntityClassname(weapon, weaponClass, sizeof(weaponClass));
if (StrContains(weaponClass, "tf_wearable", false) == 0) {
// CTFPlayer_EquipWearable(client, weapon);
} else {
EquipPlayerWeapon(client, weapon);
/**
* Ensure that weapon spawns with the correct amount of ammo.
*/
TF2_GiveWeaponAmmo(weapon, 500);
}
}
/**
* Gives a weapon ammo. Rather, it gives ammo to the player holding the weapon.
* This stock uses GivePlayerAmmo, so it obeys the maximum ammo count that the player can carry.
*
* @return Amount of ammo actually given.
*/
stock int TF2_GiveWeaponAmmo(int weapon, int amount, bool supressSound = true) {
int ammoType = GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType");
int client = GetEntPropEnt(weapon, Prop_Send, "m_hOwner");
if (client > 0 && client <= MaxClients) {
return GivePlayerAmmo(client, amount, ammoType, supressSound);
}
return 0;
}
stock int TF2_SetWeaponAmmo(int weapon, int amount, bool suppressSound = true) {
int ammoType = GetEntProp(weapon, Prop_Send, "m_iPrimaryAmmoType");
int client = GetEntPropEnt(weapon, Prop_Send, "m_hOwner");
SetEntProp(client, Prop_Send, "m_iAmmo", amount, 4, ammoType);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment