Open SupremeSpookmaster opened 1 year ago
Thank you for the report. Could you provide a small script to reproduce it?
Sure thing, here's a brief example:
public void SpawnExampleWeapon(int client) //Spawn a rocket launcher as an example, we'll assume this rocket launcher uses this plugin's attributes.
{
int weapon = Basic_SpawnWeapon(client, "tf_weapon_rocketlauncher", 18, 77, 7, 0, 9999, 9999, "2 ; 2.0");
if (IsValidEntity(weapon))
{
DataPack pack = new DataPack();
CreateTimer(5.0, RemoveAndSwitch, pack, TIMER_FLAG_NO_MAPCHANGE);
WritePackCell(pack, GetClientUserId(client));
WritePackCell(pack, EntIndexToEntRef(weapon));
}
}
public Action RemoveAndSwitch(Handle swap, DataPack pack)
{
ResetPack(pack);
int client = GetClientOfUserId(ReadPackCell(pack));
int weapon = EntRefToEntIndex(ReadPackCell(pack));
if (client < 1 || client > MaxClients || !IsPlayerInGame(client) || !IsValidEntity(weapon))
return Plugin_Continue;
//We'll just assume the weapon will always be in slot 0 for this example, since it will be because we're spawning a rocket launcher.
TF2_RemoveWeaponSlot(client, 0); //Remove the timed weapon.
SetEntPropEnt(client, Prop_Send, "m_hActiveWeapon", GetFirstValidWeapon(client)); //Automatically switch the client to the first valid weapon they're holding. For the sake of this example, we can assume this weapon uses this plugin's attributes.
}
int GetFirstValidWeapon(int client)
{
int ReturnValue = -1;
for (int i = 0; i < 5; i++)
{
ReturnValue = GetPlayerWeaponSlot(client, i);
if (ReturnValue != -1)
break;
}
return ReturnValue;
}
//I completely ran out of time so I had to copy/paste this directly from the plugin I'm working on. It likely won't work with this script, sorry, I would complete it if I had time but I have to go.
int Basic_SpawnWeapon(int client, char classname[255], int index, int level, int quality, int slot, int reserve, int clip, char atts[255])
{
if(StrEqual(name, "saxxy", false)) // if "saxxy" is specified as the name, replace with appropiate name
{
switch(TF2_GetPlayerClass(client))
{
case TFClass_Scout: ReplaceString(name, 64, "saxxy", "tf_weapon_bat", false);
case TFClass_Pyro: ReplaceString(name, 64, "saxxy", "tf_weapon_fireaxe", false);
case TFClass_DemoMan: ReplaceString(name, 64, "saxxy", "tf_weapon_bottle", false);
case TFClass_Heavy: ReplaceString(name, 64, "saxxy", "tf_weapon_fists", false);
case TFClass_Engineer: ReplaceString(name, 64, "saxxy", "tf_weapon_wrench", false);
case TFClass_Medic: ReplaceString(name, 64, "saxxy", "tf_weapon_bonesaw", false);
case TFClass_Sniper: ReplaceString(name, 64, "saxxy", "tf_weapon_club", false);
case TFClass_Spy: ReplaceString(name, 64, "saxxy", "tf_weapon_knife", false);
default: ReplaceString(name, 64, "saxxy", "tf_weapon_shovel", false);
}
}
else if(StrEqual(name, "tf_weapon_shotgun", false)) // If using tf_weapon_shotgun for Soldier/Pyro/Heavy/Engineer
{
switch(TF2_GetPlayerClass(client))
{
case TFClass_Pyro: ReplaceString(name, 64, "tf_weapon_shotgun", "tf_weapon_shotgun_pyro", false);
case TFClass_Heavy: ReplaceString(name, 64, "tf_weapon_shotgun", "tf_weapon_shotgun_hwg", false);
case TFClass_Engineer: ReplaceString(name, 64, "tf_weapon_shotgun", "tf_weapon_shotgun_primary", false);
default: ReplaceString(name, 64, "tf_weapon_shotgun", "tf_weapon_shotgun_soldier", false);
}
}
Handle hWeapon = TF2Items_CreateItem(OVERRIDE_ALL|FORCE_GENERATION);
if(hWeapon == INVALID_HANDLE)
return -1;
if (unequip)
{
TF2_RemoveWeaponSlot(client, slot);
}
TF2Items_SetClassname(hWeapon, name);
TF2Items_SetItemIndex(hWeapon, index);
TF2Items_SetLevel(hWeapon, level);
TF2Items_SetQuality(hWeapon, qual);
char atts[32][32];
int count = ExplodeString(att, ";", atts, 32, 32);
if(count % 2)
--count;
TF2Items_SetNumAttributes(hWeapon, count);
for(int i; i < count; i += 2)
{
int attrib = StringToInt(atts[i]);
if(attrib)
{
TF2Items_SetAttribute(hWeapon, i == 0 ? 0 : i / 2, attrib, StringToFloat(atts[i + 1]));
}
}
int entity = TF2Items_GiveNamedItem(client, hWeapon);
delete hWeapon;
if(entity == -1)
return -1;
EquipPlayerWeapon(client, entity);
if(visible)
{
SetEntProp(entity, Prop_Send, "m_bValidatedAttachedEntity", 1);
}
else
{
SetEntProp(entity, Prop_Send, "m_iWorldModelIndex", -1);
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", 0.001);
}
if (slot < 2)
{
#if defined TESTING
SetAmmo(client, entity, reserve);
SetClip(client, entity, clip);
#else
//The following functionality completely obliterates bots and makes them constantly swarm the resupply locker.
//This causes them to constantly have their character recreated. For just one client? This is no issue.
//20+ bots all doing it at the same time? Say goodbye to your server performance.
//I don't intend to make CF work with bots, so I won't bother fixing this myself, but it DOES make testing very difficult, hence the #if defined TESTING.
if (!spawn)
{
SetAmmo(client, entity, reserve);
SetClip(client, entity, clip);
}
else
{
DataPack pack = new DataPack();
RequestFrame(CFW_GiveAmmoOnDelay, pack);
WritePackCell(pack, GetClientUserId(client));
WritePackCell(pack, EntIndexToEntRef(entity));
WritePackCell(pack, reserve);
WritePackCell(pack, clip);
}
#endif
}
strcopy(s_WeaponFireAbility[entity], 255, fireAbility);
strcopy(s_WeaponFirePlugin[entity], 255, firePlugin);
strcopy(s_WeaponFireSound[entity], 255, fireSound);
b_WeaponIsVisible[entity] = visible;
return entity;
}
To be more precise, if the weapon you are holding uses these attributes and is removed via plugin, and then the plugin auto-switches you to another weapon which also uses these attributes, the old weapon's model doesn't go away. For example: I give myself a rocket launcher using these attributes, I take the rocket launcher away after 5s and auto-swap to an SMG which also uses these attributes, the SMG now uses the rocket launcher as a model.
This issue fixes itself if you switch your weapon manually, but it still looks pretty bad, hence why I'd like it fixed. I made a fork and tried to fix it myself, but none of my changes seemed to do anything, so I'm at a loss.