Strange behavior with my implementation of player.set_active_weapon()

Please post any questions about developing your plugin here. Please use the search function before posting!
taifuhime
Junior Member
Posts: 3
Joined: Wed Nov 28, 2018 12:49 am

Strange behavior with my implementation of player.set_active_weapon()

Postby taifuhime » Wed Nov 28, 2018 10:30 pm

First off, I'm very new to this API and programming in general (like, only started programming a few days ago >w<), so sorry if I'm making any super basic mistakes. Please correct them.

I've created a plugin which is meant to give a player a weapon (stripping their current weapon in that slot, if applicable) and set the new weapon as their active weapon. It works, for the most part... However, in some cases the weapon model isn't changed until the player issues an action for that weapon, i.e reloading or firing or switching to another weapon and back. This is not what I want.

I have tried several ways of giving the weapon, including:

    Creating an entity with Entity.create('classname') followed by making a weapon entity from that entity's index, then setting the weapon's origin to the players origin, then spawning it, then setting it as the player's active weapon. Such as:

    Syntax: Select all

    @TypedSayCommand('!awp')
    @TypedClientCommand('sp_awp')
    def give_awp(command):
    player = Player(command.index)
    if player.primary:
    current_weapon = player.primary
    player.drop_weapon(current_weapon.pointer, NULL_VECTOR, NULL_VECTOR)
    current_weapon.remove()
    awp_base = Entity.create('weapon_awp')
    awp = Weapon(awp_base.index)
    awp.origin = player.origin
    awp.spawn()
    player.set_active_weapon(awp)
    return CommandReturn.BLOCK


    I feel this is probably a poor way of doing it. However, I have also tried creating the weapon via player.give_named_item('classname'). For example:

    Syntax: Select all

    @TypedSayCommand('!scout')
    @TypedClientCommand('sp_scout')
    def give_scout(command):
    player = Player(command.index)
    if player.primary:
    current_weapon = player.primary
    player.drop_weapon(current_weapon.pointer, NULL_VECTOR, NULL_VECTOR)
    current_weapon.remove()
    player.give_named_item('weapon_scout')
    new_weapon = player.primary
    player.set_active_weapon(new_weapon)
    return CommandReturn.BLOCK


    I feel this is probably a poor way of doing it as well, and it doesn't work well for my implementation because it relies on player.primary and player.secondary to build the weapon object, making it impossible (from my understanding) to use this system for knife (which i have made droppable) and grenades.

    Finally, I have tried making the weapon with make_object and player.give_named_item('classname'), a la:

    Syntax: Select all

    @TypedSayCommand('!ak47')
    @TypedClientCommand('sp_ak47')
    def give_ak47(command):
    player = Player(command.index)
    if player.primary:
    current_weapon = player.primary
    player.drop_weapon(current_weapon.pointer, NULL_VECTOR, NULL_VECTOR)
    current_weapon.remove()
    ak47 = make_object(Weapon, player.give_named_item('weapon_ak47'))
    player.set_active_weapon(ak47)
    return CommandReturn.BLOCK


    This seems like the best compromise. However, it still has the same issue as the other two methods. In all three methods, in some cases, particularly when spawning a secondary weapon (all of the examples i've given so far are primaries, but I have the correct checks in place for secondaries (i think), check the files below) the weapon model isn't updated until the player does an action with the new weapon.

Forgive me if the syntax on any of those examples is imperfect, I just retyped them from memory and I may have misremembered something. If you see something wrong, let me know, because I may have been doing it wrong in the first place :tongue:.

Here is a video of the issue since I am terrible at explaining things: https://streamable.com/29aoi

I'm sure it's a mistake I'm making, just can't figure out what. On a related note, I have an additional beginner question:

I wrote a script to write all of the commands for me (there are a lot) in a separate file. Here is an example: https://gist.github.com/taifuhime/478fb2f85e51624250830fceff6893d5

This script outputs a file full of commands which i then copy and paste into my plugin file: https://gist.github.com/taifuhime/90378eda3e904a428792ac774671d605

Is this a normal thing to do? Is there a more efficient way to write this plugin which would make this step unnecessary?

Last question for now: I mentioned earlier that I made knives droppable. There is a small issue with that as well. I'm using this code snippet which I believe I found somewhere on the forums. I may or may not have modified it, I cannot remember. Anyhow:

Syntax: Select all

@ClientCommand('drop')
def drop_knife(command, pindex):
player = Player(pindex)
weapon = player.get_active_weapon()
if weapon.weapon_name == "weapon_knife":
player.drop_weapon(weapon.pointer, player.view_vector, NULL_VECTOR)
weapon.remove()

The issue with this is that when the knife is dropped, it drops another weapon alongside it. So, when you drop the knife it will get dropped (and subsequently removed) and another weapon would get dropped on the floor. This isn't that big of a deal since most people who are dropping knives are dropping all of their weapons regardless, but I'm still interested in why this happens.

Thank you for taking the time to read if you have. I have so, so, so many more questions (as I said, I'm extremely new and definitely not good at reading documentation yet) but I'll leave it here for now.
Last edited by taifuhime on Thu Nov 29, 2018 12:13 am, edited 1 time in total.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby satoon101 » Wed Nov 28, 2018 10:39 pm

taifuhime wrote:Last question for now: I mentioned earlier that I made knives droppable. There is a small issue with that as well. I'm using this code snippet which I believe I found somewhere on the forums. I may or may not have modified it, I cannot remember. Anyhow:

Syntax: Select all

@ClientCommand('drop')
def drop_knife(command, pindex):
player = Player(pindex)
weapon = player.get_active_weapon()
if weapon.weapon_name == "weapon_knife":
player.drop_weapon(weapon.pointer, player.view_vector, NULL_VECTOR)
weapon.remove()

The issue with this is that when the knife is dropped, it drops another weapon alongside it. So, when you drop the knife it will get dropped (and subsequently removed) and another weapon would get dropped on the floor. This isn't that big of a deal since most people who are dropping knives are dropping all of their weapons regardless, but I'm still interested in why this happens.

I cannot respond to the rest of your post at this time, but I believe why this specific issue happens is because you are not returning CommandReturn.BLOCK. Therefor, after you drop the knife, a new weapon is now your 'active' weapon, and the client command 'drop' is executed, which will drop your now active weapon.
Image
taifuhime
Junior Member
Posts: 3
Joined: Wed Nov 28, 2018 12:49 am

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby taifuhime » Wed Nov 28, 2018 11:07 pm

satoon101 wrote:
taifuhime wrote:Last question for now: I mentioned earlier that I made knives droppable. There is a small issue with that as well. I'm using this code snippet which I believe I found somewhere on the forums. I may or may not have modified it, I cannot remember. Anyhow:

Syntax: Select all

@ClientCommand('drop')
def drop_knife(command, pindex):
player = Player(pindex)
weapon = player.get_active_weapon()
if weapon.weapon_name == "weapon_knife":
player.drop_weapon(weapon.pointer, player.view_vector, NULL_VECTOR)
weapon.remove()

The issue with this is that when the knife is dropped, it drops another weapon alongside it. So, when you drop the knife it will get dropped (and subsequently removed) and another weapon would get dropped on the floor. This isn't that big of a deal since most people who are dropping knives are dropping all of their weapons regardless, but I'm still interested in why this happens.

I cannot respond to the rest of your post at this time, but I believe why this specific issue happens is because you are not returning CommandReturn.BLOCK. Therefor, after you drop the knife, a new weapon is now your 'active' weapon, and the client command 'drop' is executed, which will drop your now active weapon.


Tested and confirmed working as intended. Thank you very much for the help :)
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby L'In20Cible » Thu Nov 29, 2018 3:52 am

Your code could be something like the following:

Syntax: Select all

# ../addons/source-python/weapons/weapons.py

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
# Commands
from commands import CommandReturn
from commands.client import ClientCommand
from commands.say import SayCommand
# Players
from players.entity import Player
# Weapons
from weapons.manager import weapon_manager


# ============================================================================
# >> GLOBAL VARIABLES
# ============================================================================
# Build a list of weapons without prefix
weapons = [weapon.basename for weapon in weapon_manager.values()]


# ============================================================================
# >> COMMANDS
# ============================================================================
@ClientCommand(['sp_' + weapon for weapon in weapons])
@SayCommand(['!' + weapon for weapon in weapons])
def _weapon_commands_callback(command, player_index, team_only=False):
"""Called when a !<weapon> command is typed in chat or a sp_<weapon>
command is typed in console.
"""
# Get a Player instance of the player issuing the command
player = Player(player_index)

# If the player is dead, just exit the function early
if player.dead:
return CommandReturn.BLOCK

# Get the data of the weapon to give
data = weapon_manager[command.command_string.lstrip('!').replace(
'sp_', str()).lower().strip()]

# Loop through all weapons currently owned by the player
for weapon in player.weapons():

# Skip the current weapon if it isn't in the same slot as the weapon
# we are giving
if weapon_manager[weapon.classname].slot != data.slot:
continue

# Remove the current weapon
weapon.remove()

# Give the weapon to the player and switch to it
player.weapon_switch(player.give_named_item(data.name), 0)

# Block the command
return CommandReturn.BLOCK
taifuhime
Junior Member
Posts: 3
Joined: Wed Nov 28, 2018 12:49 am

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby taifuhime » Thu Nov 29, 2018 4:51 am

L'In20Cible wrote:Your code could be something like the following:

Syntax: Select all

# ../addons/source-python/weapons/weapons.py

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
# Commands
from commands import CommandReturn
from commands.client import ClientCommand
from commands.say import SayCommand
# Players
from players.entity import Player
# Weapons
from weapons.manager import weapon_manager


# ============================================================================
# >> GLOBAL VARIABLES
# ============================================================================
# Build a list of weapons without prefix
weapons = [weapon.replace(weapon_manager.prefix,
str()) for weapon in weapon_manager]


# ============================================================================
# >> COMMANDS
# ============================================================================
@ClientCommand(['sp_' + weapon for weapon in weapons])
@SayCommand(['!' + weapon for weapon in weapons])
def _weapon_commands_callback(command, player_index, team_only=False):
"""Called when a !<weapon> command is typed in chat or a sp_<weapon>
command is typed in console.
"""
# Get a Player instance of the player issuing the command
player = Player(player_index)

# If the player is dead, just exit the function early
if player.dead:
return CommandReturn.BLOCK

# Get the name of the weapon to give
name = weapon_manager.prefix + command.command_string.lstrip(
'!').replace('sp_', str()).lower().strip()

# Get the slot of the weapon we are giving
slot = weapon_manager[name].slot

# Loop through all weapons currently owned by the player
for weapon in player.weapons():

# Skip the current weapon if it isn't in the same slot as the weapon
# we are giving
if weapon_manager[weapon.classname].slot != slot:
continue

# Remove the current weapon
weapon.remove()

# Give the weapon to the player and switch to it
player.weapon_switch(player.give_named_item(name), 0)

# Block the command
return CommandReturn.BLOCK

Very clever and concise, thank you! I'm pretty embarrassed at how verbose (and non-functional) my code was, but I'll leave this here and feel ashamed for the sake of those who come after.

It seems like weapon_manager has a lot of the functions I was looking for, but I didn't realize its functionality when I saw it in the docs.

Thanks again, I really appreciate it.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby satoon101 » Thu Nov 29, 2018 11:28 am

A couple other minor optimizations from that code:

Technically, using the prefix would break for some other games where the prefix is not always used/consistent. Plus, we already have access to the name without any prefix, so why bother.

Syntax: Select all

# Build a list of weapons without prefix
weapons = [weapon.basename for weapon in weapon_manager]


Using weapon_manager.__getitem__ already handles the prefix properly for us, including "special_names", which would be the ones that above would not contain the consistent prefix.

Syntax: Select all

# Get the name of the weapon to give
name = command.command_string.lstrip(
'!').replace('sp_', str()).lower().strip()
Image
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Strange behavior with my implementation of player.set_active_weapon()

Postby L'In20Cible » Fri Nov 30, 2018 4:16 am

satoon101 wrote:A couple other minor optimizations from that code:

Technically, using the prefix would break for some other games where the prefix is not always used/consistent. Plus, we already have access to the name without any prefix, so why bother.

Syntax: Select all

# Build a list of weapons without prefix
weapons = [weapon.basename for weapon in weapon_manager]

Good to know! Except for TF2 that use tf_weapon_ instead of weapon_, I have no clue of the special cases you are mentioning.

satoon101 wrote:Using weapon_manager.__getitem__ already handles the prefix properly for us, including "special_names", which would be the ones that above would not contain the consistent prefix.

Syntax: Select all

# Get the name of the weapon to give
name = command.command_string.lstrip(
'!').replace('sp_', str()).lower().strip()

This would break the give_named_item call, tho it could be using the basename too I guess.

EDIT: Updated the code:
L'In20Cible wrote:Your code could be something like the following:

Syntax: Select all

# ../addons/source-python/weapons/weapons.py

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
# Commands
from commands import CommandReturn
from commands.client import ClientCommand
from commands.say import SayCommand
# Players
from players.entity import Player
# Weapons
from weapons.manager import weapon_manager


# ============================================================================
# >> GLOBAL VARIABLES
# ============================================================================
# Build a list of weapons without prefix
weapons = [weapon.basename for weapon in weapon_manager.values()]


# ============================================================================
# >> COMMANDS
# ============================================================================
@ClientCommand(['sp_' + weapon for weapon in weapons])
@SayCommand(['!' + weapon for weapon in weapons])
def _weapon_commands_callback(command, player_index, team_only=False):
"""Called when a !<weapon> command is typed in chat or a sp_<weapon>
command is typed in console.
"""
# Get a Player instance of the player issuing the command
player = Player(player_index)

# If the player is dead, just exit the function early
if player.dead:
return CommandReturn.BLOCK

# Get the data of the weapon to give
data = weapon_manager[command.command_string.lstrip('!').replace(
'sp_', str()).lower().strip()]

# Loop through all weapons currently owned by the player
for weapon in player.weapons():

# Skip the current weapon if it isn't in the same slot as the weapon
# we are giving
if weapon_manager[weapon.classname].slot != data.slot:
continue

# Remove the current weapon
weapon.remove()

# Give the weapon to the player and switch to it
player.weapon_switch(player.give_named_item(data.name), 0)

# Block the command
return CommandReturn.BLOCK

Return to “Plugin Development Support”

Who is online

Users browsing this forum: No registered users and 37 guests