Raised This Month: $12 Target: $400
 3% 

Solved How to return multiple values and apply conditionals for all possible combinations?


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
PatriotGames
AlliedModders Donor
Join Date: Feb 2012
Location: root@irs:/# rm -rf /
Old 02-12-2019 , 16:56   How to return multiple values and apply conditionals for all possible combinations?
Reply With Quote #1

My plugin needs to check a survivor's primary weapon, if they have one, for any upgrades (laser, incendiary ammo or explosive ammo) and then decide if an upgrade should be given or not.
Peace-Maker posted some code that I modified to find the current upgrades, if any, and return a value that corresponds to the appropriate cvar values for the same upgrades.

There are two challenges at this point:

1. The GetUpgradeType function successfully determines the client's primary weapon upgrades, but it needs to return multiple values and I'm not sure how best to handle it.
2. Because there are five unique non-repeating combinations of possible upgrades (incendiary and explosive ammo are mutually exclusive) the logic to check them for regular and vip players gets a little complicated.

The code below works for one upgrade, but not more. I understand why but am not sure where to go from here. Seems like an array is needed but my array experience is limited:

Note: Survivors are handled individually based on player_spawn event, so this codes runs for one client at a time.

PHP Code:
#define L4D2_WEPUPGFLAG_NONE            (0 << 0)
#define L4D2_WEPUPGFLAG_INCENDIARY      (1 << 0)
#define L4D2_WEPUPGFLAG_EXPLOSIVE       (1 << 1)
#define L4D2_WEPUPGFLAG_LASER           (1 << 2)

g_cvUpgradeChoice CreateConVar("l4d2_sis_upgrade_choice""0""Give an Upgrade to the survivors? 0=disable, 1=laser, 2=incendiary ammo, 3=explosive ammo"FCVAR_NOTIFY);
g_cvRandomUpgrade CreateConVar("l4d2_sis_random_upgrade""0""Give a random Upgrade item to the survivors? Overrides previous upgrade settings. 0=disable, 1=enable."FCVAR_NOTIFY);
g_cvVipUpgradeChoice CreateConVar("l4d2_sis_vip_upgrade_choice""0""Give an Upgrade to the VIPs? Overrides ALL other upgrade settings. 0=disable, 1=laser, 2=incendiary ammo, 3=explosive ammo"FCVAR_NOTIFY);


/* =============  Upgrade Item  ============= */
        
        
if (g_cvUpgradeChoice.IntValue || g_cvRandomUpgrade.BoolValue || g_cvVipUpgradeChoice.IntValue 0// check that at least one upgrade cvar is > 0
        
{
            
int iEntPrimaryWeapon GetPlayerWeaponSlot(clientview_as<int>(L4D2WeaponSlot_Primary)); // check for a primary weapon
            
if (iEntPrimaryWeapon == -&& !IsValidEntity(iEntPrimaryWeapon))
            {
                return; 
// client doesn't have a primary weapon, so no upgrade is given.
            
}
            else 
// client has a primary weapon.
            
{
                
int iExistingUpgrade GetUpgradeType(client); // what is the existing upgrade, if any?  Need to return and manage multiple values here!
                
if (iExistingUpgrade != 0//client has an upgrade. Should they receive another?
                
{
                    if (
bIsVip && g_cvVipUpgradeChoice.IntValue && g_cvVipUpgradeChoice.IntValue != iExistingUpgrade //check for vip/admin status and the vip upgrade cvar
                    
{
                        
SelectUpgrade(clientbIsVip);
                    }
                    else if (
g_cvUpgradeChoice.IntValue && g_cvUpgradeChoice.IntValue != iExistingUpgrade// client is not vip, check for base upgrade cvar.
                    
{
                        
SelectUpgrade(clientbIsVip);
                    }
                }
                else 
// client does not have an existing upgrade. Should they receive one?
                
{
                    if (
bIsVip && g_cvVipUpgradeChoice.IntValue 0// check vip/admin status and vip upgrade cvar.
                    
{
                        
SelectUpgrade(clientbIsVip);
                    }
                    else if (
g_cvUpgradeChoice.IntValue 0// client is not vip, check base upgrade cvar.
                    
{
                        
SelectUpgrade(clientbIsVip);
                    }
                }
            }
        }
        
        
stock int GetUpgradeType(int client)
{
    
int iEntPrimaryWeapon GetPlayerWeaponSlot(clientview_as<int>(L4D2WeaponSlot_Primary));
    if (
iEntPrimaryWeapon != -&& IsValidEdict(iEntPrimaryWeapon) && HasEntProp(iEntPrimaryWeaponProp_Send"m_upgradeBitVec")) // check if client has a valid primary weapon with an upgrade(s)
    
{
        
int iUpgradeType GetEntProp(iEntPrimaryWeaponProp_Send"m_upgradeBitVec"); // get the upgrade type
        
if (iUpgradeType L4D2_WEPUPGFLAG_LASER)
        {
            return 
1// laser upgrade cvar value
        
}
        else if (
iUpgradeType L4D2_WEPUPGFLAG_INCENDIARY)
        {
            return 
2// indendiary upgrade cvar value 
        
}
        else if (
iUpgradeType L4D2_WEPUPGFLAG_EXPLOSIVE)
        {
            return 
3// explosive upgrade cvar value 
        
}
    }
    return 
0// no upgrades

Appreciate any help,
PG

Last edited by PatriotGames; 02-15-2019 at 17:08. Reason: Update status to solved.
PatriotGames is offline
Ilusion9
Veteran Member
Join Date: Jun 2018
Location: Romania
Old 02-12-2019 , 17:17   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #2

The only problem i could see:

PHP Code:
if (iEntPrimaryWeapon == -&& !IsValidEntity(iEntPrimaryWeapon))
{
      return;

It should be:
PHP Code:
if (iEntPrimaryWeapon == -|| !IsValidEntity(iEntPrimaryWeapon))
{
      return;

__________________

Last edited by Ilusion9; 02-12-2019 at 17:17.
Ilusion9 is offline
nosoop
Veteran Member
Join Date: Aug 2014
Old 02-12-2019 , 18:08   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #3

A bitvec / bitflag is basically an array of boolean values, so you might as well get the contents of m_upgradeBitVec and check your requirements against it; something like the following (untested):

Code:
// contains bitflag of upgrades (m_upgradeBitVec)
int upgradeBitVec;

// you'd want to translate the cvar to the corresponding bitvec somehow, your choice on implementation
// for the sake of example, I'll fill them in:
int vipUpgradeChoice = L4D2_WEPUPGFLAG_INCENDIARY;
int regUpgradeChoice = L4D2_WEPUPGFLAG_LASER;

// true if the player has either incendiary or explosive upgrades
// I'm not sure what your desired behavior is in that case so you'll have to work that out
bool hasMutuallyExclusiveUpgrade = upgradeBitVec & (L4D2_WEPUPGFLAG_INCENDIARY | L4D2_WEPUPGFLAG_EXPLOSIVE);

if (vipUpgradeChoice != L4D2_WEPUPGFLAG_NONE && bIsVip && !(upgradeBitVec & vipUpgradeChoice)) {
	// they are VIP and they don't have this upgrade, so apply it
	// TODO apply the VIP upgrade here
} else if (regUpgradeChoice != L4D2_WEPUPGFLAG_NONE && !(upgradeBitVec & regUpgradeChoice)) {
	// they aren't VIP or they already had the upgrade
	// TODO apply the regular upgrade here
}
__________________
I do TF2, TF2 servers, and TF2 plugins.
I don't do DMs over Discord -- PM me on the forums regarding inquiries.
AlliedModders Releases / Github / TF2 Server / Donate (BTC / BCH / coffee)

Last edited by nosoop; 02-12-2019 at 18:11. Reason: code oops
nosoop is offline
Spirit_12
Veteran Member
Join Date: Dec 2012
Location: Toronto, CA
Old 02-13-2019 , 01:23   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #4

I don’t have enough time on me these days to poke around, but you can hook these functions straight out of binary. Check out backpack plugin and the forwards I posted there.
__________________
Spirit_12 is offline
eyal282
Veteran Member
Join Date: Aug 2011
Old 02-13-2019 , 05:51   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #5

Return an array if you need to return multiple integers. Return a data pack handle if what you return is a combo of chars ( strings ) and other stuff like float or ints. Check how cloning handles works on Google.
__________________
I am available to make plugins for pay.

Discord: Eyal282#1334

Last edited by eyal282; 02-13-2019 at 05:51.
eyal282 is offline
PatriotGames
AlliedModders Donor
Join Date: Feb 2012
Location: root@irs:/# rm -rf /
Old 02-15-2019 , 17:07   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #6

Thanks so much for all of the help and code examples. Once I recognized it was unnecessary to return multiple values from the GetUpgrdType function, I adapted nosoop's example code. Here's the updated version:

PHP Code:
// This simple function translates the upgrade item cvar to the bitvec upgrade value.
stock int UpgdChoiceBitVec(int item)
{
    switch (
item)
    {
        case 
1: return L4D2_WEPUPGFLAG_LASER;
        case 
2: return L4D2_WEPUPGFLAG_INCENDIARY;
        case 
3: return L4D2_WEPUPGFLAG_EXPLOSIVE;
    }
    return 
0;

PHP Code:
// This function returns the existing upgrade bitvec from the client's primary weapon.
stock int GetUpgrdType(int client)
{
    
int iUpgrdType L4D2_WEPUPGFLAG_NONE;
    
int iEntPrimaryWeapon GetPlayerWeaponSlot(clientview_as<int>(L4D2WeaponSlot_Primary));
    if (
iEntPrimaryWeapon != -&& IsValidEdict(iEntPrimaryWeapon) && HasEntProp(iEntPrimaryWeaponProp_Send"m_upgradeBitVec"))
    {
        
iUpgrdType GetEntProp(iEntPrimaryWeaponProp_Send"m_upgradeBitVec");
    }
    return 
iUpgrdType;

PHP Code:
// This is the revised conditional logic for selecting the upgrade.
if (g_cvUpgradeChoice.IntValue || g_cvRandomUpgrade.BoolValue || g_cvVipUpgradeChoice.IntValue 0)
{
    
int iEntPrimaryWeapon GetPlayerWeaponSlot(clientview_as<int>(L4D2WeaponSlot_Primary));
    if (
iEntPrimaryWeapon == -|| !IsValidEntity(iEntPrimaryWeapon))
    {
        return;
    }
    else
    {
        
int iExistingUpgrd GetUpgrdType(client);
        if (
iExistingUpgrd == L4D2_WEPUPGFLAG_NONE)
        {
            
SelectUpgrd(clientbIsVip);
        }
        else
        {
            
bool bHasAmmoUpgrd view_as<bool>(iExistingUpgrd & (L4D2_WEPUPGFLAG_INCENDIARY L4D2_WEPUPGFLAG_EXPLOSIVE));
            if (
bIsVip && UpgdChoiceBitVec(g_cvVipUpgradeChoice.IntValue) > 0  && !(UpgdChoiceBitVec(g_cvVipUpgradeChoice.IntValue) & iExistingUpgrd))
            {
                if (
UpgdChoiceBitVec(g_cvVipUpgradeChoice.IntValue) == L4D2_WEPUPGFLAG_LASER)
                {
                    
SelectUpgrd(clientbIsVip);
                }
                else if (!
bHasAmmoUpgrd)
                {
                    
SelectUpgrd(clientbIsVip);
                }
            }    
            else if (
UpgdChoiceBitVec(g_cvUpgradeChoice.IntValue) > && !(UpgdChoiceBitVec(g_cvUpgradeChoice.IntValue) & iExistingUpgrd))
            {
                if (
UpgdChoiceBitVec(g_cvUpgradeChoice.IntValue) == L4D2_WEPUPGFLAG_LASER)
                {
                    
SelectUpgrd(clientbIsVip);
                }
                else if (!
bHasAmmoUpgrd)
                {
                    
SelectUpgrd(clientbIsVip);
                }
            }
        }
    }

Admittedly, there is some redundancy in this last bit of code, but at this point, I don't see a more efficient way to write it. Testing confirms the code works.

Please let me know if you see any mistakes or opportunities for improvement.

Thanks again for all of the help!
PG

Last edited by PatriotGames; 02-16-2019 at 14:35. Reason: bluf...
PatriotGames is offline
nosoop
Veteran Member
Join Date: Aug 2014
Old 02-16-2019 , 15:07   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #7

You can save an indentation level by stripping the surrounding else { /* ... */ } nesting since you're returning from the function early anyways.

There's also the possibility of or-ing the UpgdChoiceBitVec and !bHasAmmoUpgrd result together, and storing the result of UpgdChoiceBitVec may help with readability so it's less characters to read.

Since all the decision making just determines if SelectUpgrd(client, bIsVip); is called or not, you could reduce it down to a couple of boolean variables to check against (even down to a single one if you want to sacrifice readability).

I wouldn't worry about it too much as long as the code works; you'll pick up these things as you learn.
__________________
I do TF2, TF2 servers, and TF2 plugins.
I don't do DMs over Discord -- PM me on the forums regarding inquiries.
AlliedModders Releases / Github / TF2 Server / Donate (BTC / BCH / coffee)
nosoop is offline
PatriotGames
AlliedModders Donor
Join Date: Feb 2012
Location: root@irs:/# rm -rf /
Old 02-17-2019 , 23:23   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #8

Quote:
Originally Posted by nosoop View Post
You can save an indentation level by stripping the surrounding else { /* ... */ } nesting since you're returning from the function early anyways.

There's also the possibility of or-ing the UpgdChoiceBitVec and !bHasAmmoUpgrd result together, and storing the result of UpgdChoiceBitVec may help with readability so it's less characters to read.

Since all the decision making just determines if SelectUpgrd(client, bIsVip); is called or not, you could reduce it down to a couple of boolean variables to check against (even down to a single one if you want to sacrifice readability).

I wouldn't worry about it too much as long as the code works; you'll pick up these things as you learn.
nosoop,

Thank you for those suggestions. Now that you've pointed them out they seem obvious

After making those changes in this section, I went through the rest of the item selection code and made similar improvements in several other sections. It really simplified the nested conditionals and was a good exercise in hunting redundancy hunting (lol).

I think further consolidation could be done, but for the sake of readability and debugging, I'll stop here. Some of the if statements are getting rather long. Here's the updated code:

PHP Code:
if (g_cvUpgradeChoice.IntValue || g_cvRandomUpgrade.BoolValue || g_cvVipUpgradeChoice.IntValue 0)
{
    
int iEntPrimaryWeapon GetPlayerWeaponSlot(clientview_as<int>(L4D2WeaponSlot_Primary));
    if (
iEntPrimaryWeapon == -|| !IsValidEntity(iEntPrimaryWeapon))
    {
        return;
    }
    
int iExistingUpgrd GetUpgrdType(client);
    if (
iExistingUpgrd == L4D2_WEPUPGFLAG_NONE)
    {
        
SelectUpgrd(clientbIsVip);
    }
    else
    {
        
int iVipUpgrdChoiceBv UpgdChoiceBitVec(g_cvVipUpgradeChoice.IntValue);
        
int iUpgrdChoiceBv UpgdChoiceBitVec(g_cvUpgradeChoice.IntValue);
        
bool bHasAmmoUpgrd view_as<bool>(iExistingUpgrd & (L4D2_WEPUPGFLAG_INCENDIARY L4D2_WEPUPGFLAG_EXPLOSIVE));
        
        if (
bIsVip && iVipUpgrdChoiceBv 0  && !(iVipUpgrdChoiceBv iExistingUpgrd) && (iVipUpgrdChoiceBv == L4D2_WEPUPGFLAG_LASER || !bHasAmmoUpgrd))
        {
            
SelectUpgrd(clientbIsVip);
        }    
        else if (
iUpgrdChoiceBv && !(iUpgrdChoiceBv iExistingUpgrd) && (iUpgrdChoiceBv == L4D2_WEPUPGFLAG_LASER || !bHasAmmoUpgrd))
        {
            
SelectUpgrd(clientbIsVip);
        }
    }

Thanks again for the help.
PG
PatriotGames is offline
Skyy
AlliedModders Donor
Join Date: Jan 2010
Location: Toronto, Canada
Old 02-20-2019 , 00:27   Re: How to return multiple values and apply conditionals for all possible combination
Reply With Quote #9

Don't forget to remember the upgrade values for your weapons.
I saw a plugin that let you drop your weapon, but didn't remember the upgrades, and that's really frustrating when you're trying to drop it for a teammate.
Skyy is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 09:07.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode