tsuza / TF2CA-weaponmodel_override

[TF2] Sourcemod plugin that lets you change your viewmodel ( weapon model, arms model, arms animations )
11 stars 6 forks source link

Getting a weapon which uses this system removed while it is equipped breaks it. #13

Open SupremeSpookmaster opened 1 year ago

SupremeSpookmaster commented 1 year ago

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.

tsuza commented 1 year ago

Thank you for the report. Could you provide a small script to reproduce it?

SupremeSpookmaster commented 1 year ago

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;
}