Two ways to give a weapon + something about CS:GO weapons

Post Python examples to help other users.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Two ways to give a weapon + something about CS:GO weapons

Postby iPlayer » Mon Aug 21, 2017 1:33 am

Approach #1. give_named_item
As it seems, not everybody knows that this function returns a pointer that you can use to gain control of the created weapon.

Syntax: Select all

from commands.typed import TypedSayCommand
from memory import make_object
from messages import SayText2
from players.entity import Player
from weapons.entity import Weapon


@TypedSayCommand('!gimme')
def cmd_on_gimme(command_info, weapon_name):
player = Player(command_info.index)

weapon = make_object(Weapon, player.give_named_item(weapon_name))

SayText2(f"Given {weapon.class_name} to {player.name}").send()


Approach #2. Weapon.create
When you create the entity by youself, obviously you have the reference to it, but the question is completely the opposite: how to give this weapon to a player. This is done by telling the player to touch a weapon. Doesn't even matter where the weapon was spawned.

Syntax: Select all

from commands.typed import TypedSayCommand
from messages import SayText2
from players.entity import Player
from weapons.entity import Weapon


@TypedSayCommand('!gimme')
def cmd_on_gimme(command_info, weapon_name):
player = Player(command_info.index)

weapon = Weapon.create(weapon_name)
weapon.spawn()
player.touch(weapon.pointer)

SayText2(f"Given {weapon.class_name} to {player.name}").send()


Now some information about CS:GO. As you may know, CS:GO introduced some weapons that share the classname (USP-S and HKP2000, for example, are both weapon_hkp2000). The difference between such weapons lies in their properties.
For your convenience, Source.Python's Weapon.create converts aliases (say, weapon_usp_silencer) to the real classname and assigns correct properties to the created entity to make sure that the resulting weapon will be USP-S and not HKP2000.

Well, at least it tries to do so. From my testing,

Syntax: Select all

Weapon.create('weapon_usp_silencer')

still spawns HKP2000, but if you drop it and look at it, the hint will caption it as "USP-S".

What about give_named_item then? It's a native Source function and it should do all the magic by itself. And it successfully recognizes the weapon_usp_silencer alias. The only problem is that this function is too good. Nobody asks for it, but the function will look up the player's inventory to see what weapon - USP or HKP2000 - the player has equipped. Then... it just ignores the alias and spawns the one it thinks "correct". I.e. it can spawn USP-S when you wanted it to spawn HKP2000 and vice versa.

How to prevent it? The inventory lookup only succeeds if the player is on the correct team. Remember that USP/HKP2000 can only be bought by Counter-Terrorists? Then when the player is on the Terrorists team, give_named_item will spawn the weapon that we ask for.

The following code:

Syntax: Select all

entity = Entity(player.index)
old_team = entity.team
entity.team = 2 # Terrorists
player.give_named_item('weapon_hkp2000')
entity.team = old_team


basically saves player's current team, moves them to terrorists, gives the correct weapon and then restores their team.
You might ask, why won't I use player.team? Because player.team is a shortcut to player.playerinfo.team, and changing the team using this shortcut will kill the player in process.
entity.team, on the other side, is more raw and allows us to change player's team without any casualties.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Mon Aug 21, 2017 2:51 am

iPlayer wrote:You might ask, why won't I use player.team? Because player.team is a shortcut to player.playerinfo.team, and changing the team using this shortcut will kill the player in process.
entity.team, on the other side, is more raw and allows us to change player's team without any casualties.

This seems redundant to me. We should rather rename Entity.team to Entity,team_index in order to avoid duplicate with different behaviors from derived class.
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby iPlayer » Mon Aug 21, 2017 3:06 am

L'In20Cible wrote:This seems redundant to me. We should rather rename Entity.team to Entity,team_index in order to avoid duplicate with different behaviors from derived class.

Splitting it into team_index and team is definitely better than having to instantiate an Entity when you already possess a Player instance.
But maybe remove the team definition from the Player entirely? Leave the inherited team, and if anybody wants to use the PlayerInfo one, they can (and could) always access it via player.playerinfo.team.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Wed Sep 13, 2017 1:41 am

iPlayer wrote:
L'In20Cible wrote:This seems redundant to me. We should rather rename Entity.team to Entity,team_index in order to avoid duplicate with different behaviors from derived class.

Splitting it into team_index and team is definitely better than having to instantiate an Entity when you already possess a Player instance.
But maybe remove the team definition from the Player entirely? Leave the inherited team, and if anybody wants to use the PlayerInfo one, they can (and could) always access it via player.playerinfo.team.

I thought about this and I don't think this is the best approach and I would rather prefer renaming Entity.team to Entity.team_index over removing Player.team behavior. The reasoning behind this is quite simple, it does make sense that the setter method for Player.team is redirected to PlayerInfo.team (or more precisely IPlayerInfo::ChangeTeam internally) because that method is responsible to correctly change the team of the player (which includes adding the player to the new CTeam array along by removing it from the old and sending the class selection HUD for the player to pick a class). For example, on games that have classes (at least, this is true for CS games), only changing the m_nTeamNumber value of a player doesn't change his team and cause a lot of weird things (player not respawning on new round being one of them).
By renaming Entity.team, we keep the correct behavior for Player instances and we add a more "raw" attribute to the hierarchy. Unless anyone have any objections or reasons to go a different route, I will go ahead and make that change.
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby BackRaw » Sun Sep 17, 2017 3:11 pm

Great info, thanks! I have a little OT question about Approach #1 though:

iPlayer wrote:Approach #1. give_named_item
As it seems, not everybody knows that this function returns a pointer that you can use to gain control of the created weapon.

Syntax: Select all

from commands.typed import TypedSayCommand
from memory import make_object
from messages import SayText2
from players.entity import Player
from weapons.entity import Weapon


@TypedSayCommand('!gimme')
def cmd_on_gimme(command_info, weapon_name):
player = Player(command_info.index)

weapon = make_object(Weapon, player.give_named_item(weapon_name))

SayText2(f"Given {weapon.class_name} to {player.name}").send()


What does the f"" string flag at the very last line do exactly? Is it what I'm thinking and it takes the locals to format the string? I really missed that lol.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Re: Two ways to give a weapon + something about CS:GO weapons

Postby satoon101 » Sun Sep 17, 2017 3:43 pm

Image
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby BackRaw » Sun Sep 17, 2017 4:01 pm

Awesome, thanks for the hint.
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Mon Oct 30, 2017 12:25 pm

Guys, I did not find any info about weapon.spawn() you mentioned in second approach. Wiki does not know about it.
My goal is to spawn a weapon at specific coordinatess (CS:GO if it is important). Should i use spawn + teleport or somehow spawn() alone handles this?
User avatar
iPlayer
Developer
Posts: 590
Joined: Sat Nov 14, 2015 8:37 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby iPlayer » Mon Oct 30, 2017 12:31 pm

Weapon.spawn is inherited from BaseEntity.spawn (through Entity). That method is defined directly in C++ part of Source.Python right here.

It doesn't accept any arguments, it just spawns the entity. So yeah, you'll have to teleport it by yourself. Try this:

Syntax: Select all

from mathlib import Vector
from weapons.entity import Weapon

weapon = Weapon.create("weapon_ak47")
weapon.spawn()
weapon.teleport(Vector(100, 100, 100))


That teleport method can also accept optional second (angle) and third (velocity) arguments.

EDIT: Actually, you don't even need teleport, simply setting the .origin property should work as well.
Image /id/its_iPlayer
My plugins: Map Cycle • Killstreaker • DeadChat • Infinite Jumping • TripMines • AdPurge • Bot Damage • PLRBots • Entity AntiSpam

Hail, Companion. [...] Hands to yourself, sneak thief. Image
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Mon Oct 30, 2017 5:58 pm

iPlayer wrote:EDIT: Actually, you don't even need teleport, simply setting the .origin property should work as well.
And is definitely better, as well. Teleport to set the origin should mainly be used (and is the main reason why it got added to the data to begins with) when the entity declares m_vecOrigin as excluded property (which is the case for players, since this is handled and networked by the IGameMovement interface).

Another good reason to avoid it when not absolutely required: #157
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Wed Nov 01, 2017 8:49 am

About item IDs. Maybe, we should use economy ID(https://tf2b.com/itemlist.php?gid=730) instead of class weapon ID?
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Wed Nov 01, 2017 2:10 pm

Some fresh data.


Syntax: Select all

from commands.typed import TypedSayCommand
from memory import make_object
from messages import SayText2
from players.entity import Player
from weapons.entity import Weapon
from mathlib import Vector

@TypedSayCommand('!gimme')
def cmd_on_gimme(command_info, weapon_name):
player = Player(command_info.index)
weapon = Weapon.create(weapon_name)
print (weapon.__dict__)
weapon.spawn()
print (weapon.__dict__)
player.touch(weapon.pointer)
print (weapon.pointer)
print (f'origin {weapon.origin}')
print (f'model {weapon.model}')
print (f'skin {weapon.skin}')
SayText2(f"Given {weapon.class_name} to {player.name}").send()


@TypedSayCommand('!gimme2')
def cmd_on_gimme2(command_info, weapon_name):
player = Player(command_info.index)
weapon = make_object(Weapon, player.give_named_item(weapon_name))
print (weapon.__dict__)
print (f'origin {weapon.origin}')
print (f'model {weapon.model}')
print (f'skin {weapon.skin}')
SayText2(f"Given {weapon.class_name} to {player.name}").send()


Code: Select all

output
----------------------------------------------------------------------------------------------------------------------------
give weapon
----------------------------------------------------------------------------------------------------------------------------
{'_index': 191}
origin Vector(937.5, -2262.5, -39.96875)
model <engines.precache.Model object at 0xec44bbac>
skin 0
NeverAlone: !gimme2 weapon_ak47
L 11/01/2017 - 16:57:13: "NeverAlone<2><STEAM_1:0:48803246><CT>" say "!gimme2 weapon_ak47"
----------------------------------------------------------------------------------------------------------------------------
create weapon
----------------------------------------------------------------------------------------------------------------------------

{'_index': 198}
{'_index': 198}
<_memory.Pointer object at 0xeb06f890>
origin Vector(0.0, 0.0, 0.0)

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/commands/auth.py", line 44, in __call__
    return self.callback(*args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 553, in on_command
    result = cls.on_clean_command(command_info, command_node, args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 611, in on_clean_command
    return super().on_clean_command(command_info, command_node, args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 568, in on_clean_command
    return command_node.callback(command_info, *cleaned_args)
  File "../addons/source-python/plugins/sdm/sdm.py", line 45, in cmd_on_gimme
    print (f'model {weapon.model}')
  File "../addons/source-python/packages/source-python/entities/_base.py", line 308, in get_model
    return Model(self.model_name)
  File "../addons/source-python/packages/source-python/engines/precache.py", line 71, in __init__
    self._precache()
  File "../addons/source-python/packages/source-python/engines/precache.py", line 113, in _precache
    self._precache_method(self._path, self._preload)

ValueError: "" is not a valid model name.


NeverAlone: !gimme weapon_ak47
L 11/01/2017 - 16:57:19: "NeverAlone<2><STEAM_1:0:48803246><CT>" say "!gimme weapon_ak47"


If this is not only mine error,
my guess: Valve messed with create entities by name at the end of August. Consequences of getting rid CS:S pieces of code (can't be more specific ATM) It concerned CSGO only. Then, a month after there was a patch. A big topic at AlliedModders was about it. Maybe, SourcePython community just did not notice all the hustle?
Last edited by L'In20Cible on Wed Nov 01, 2017 3:06 pm, edited 1 time in total.
Reason: code → python
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Wed Nov 01, 2017 3:05 pm

This error occurs when m_szModelName is an empty string. Try the following instead:

Syntax: Select all

model = Model(
string_tables[Model.precache_table][weapon.world_model_index])

Also, please use the PYTHON syntax highlighter instead of the generic CODE when posting python code.
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Wed Nov 01, 2017 7:56 pm

Syntax: Select all

from commands.typed import TypedSayCommand
from memory import make_object
from messages import SayText2
from players.entity import Player
from weapons.entity import Weapon
from mathlib import Vector
from engines.precache import Model
from stringtables import string_tables

@TypedSayCommand('!gimme')
def cmd_on_gimme(command_info, weapon_name):
player = Player(command_info.index)
weapon = Weapon.create(weapon_name)
print (weapon.__dict__)
weapon.model = Model(string_tables[Model.precache_table][weapon.world_model_index])
weapon.spawn()
print (weapon.__dict__)
player.touch(weapon.pointer)
print (weapon.pointer)
print (f'origin {weapon.origin}')
print (f'model {weapon.model}')
print (f'skin {weapon.skin}')
SayText2(f"Given {weapon.class_name} to {player.name}").send()


Output:

Code: Select all

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/commands/auth.py", line 44, in __call__
    return self.callback(*args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 553, in on_command
    result = cls.on_clean_command(command_info, command_node, args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 611, in on_clean_command
    return super().on_clean_command(command_info, command_node, args)
  File "../addons/source-python/packages/source-python/commands/typed.py", line 568, in on_clean_command
    return command_node.callback(command_info, *cleaned_args)
  File "../addons/source-python/plugins/sdm/sdm.py", line 43, in cmd_on_gimme
    weapon.model = Model(string_tables[Model.precache_table][weapon.world_model_index])
  File "../addons/source-python/packages/source-python/engines/precache.py", line 71, in __init__
    self._precache()
  File "../addons/source-python/packages/source-python/engines/precache.py", line 113, in _precache
    self._precache_method(self._path, self._preload)

ValueError: "" is not a valid model name.


NeverAlone: !gimme weapon_awp


Follow up question: why do we create weapon entity by name without model?
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Wed Nov 01, 2017 8:07 pm

Move the spawn() call before setting the model. Weapons are special when it comes to models because this is handled by the weapon scripts and processed by the weapons being physically spawned into the world. If you read world_model_index before it being spawned, it won't returns anything. Overall, to give a weapon to a player, give_named_item is definitely the way to go because the game handles everything that is needed for you in a single call.
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Wed Nov 01, 2017 8:23 pm

L'In20Cible wrote:Move the spawn() call before setting the model.


I did as you suggested.

Syntax: Select all

weapon = Weapon.create(weapon_name)
print (weapon.__dict__)
weapon.spawn()
weapon.model = Model(string_tables[Model.precache_table][weapon.world_model_index])

Result is the same.

My goal is to spawn a weapon on the map so it can be picked up during the round. That is why giving a weapon is not enough for me.
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Wed Nov 01, 2017 8:29 pm

Well, then you don't have to set the model. And you need to set the origin of the weapon instead of calling the touch() method. The following doesn't create the named weapon where you are looking?

Syntax: Select all

weapon = Weapon.create(weapon_name)
weapon.origin = player.view_coordinates
weapon.spawn()
battleweaver
Member
Posts: 43
Joined: Mon Oct 02, 2017 7:57 am
Location: Moscow
Contact:

Re: Two ways to give a weapon + something about CS:GO weapons

Postby battleweaver » Wed Nov 01, 2017 9:08 pm

The following doesn't create the named weapon where you are looking?

Syntax: Select all

weapon = Weapon.create(weapon_name)
weapon.origin = player.view_coordinates
weapon.spawn()
[/quote]
Yes. No weapon created. While it returns

Code: Select all

{'_index': 199}
origin Vector(1243.64990234375, -1099.694091796875, -199.3569793701172)
skin 0
NeverAlone: !gimme weapon_awp


While giving weapon does the trick.

Code: Select all

{'_index': 118}
origin Vector(925.0, -1100.0, -263.96875)
model <engines.precache.Model object at 0xec4a4bcc>
skin 0
NeverAlone: !gimme2 weapon_awp
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Re: Two ways to give a weapon + something about CS:GO weapons

Postby Ayuto » Wed Nov 01, 2017 9:53 pm

I won't be able to test that before the weekend, but there is a new method to create entities in CS:GO:
https://github.com/alliedmodders/hl2sdk ... cdb8f11199
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Re: Two ways to give a weapon + something about CS:GO weapons

Postby L'In20Cible » Wed Nov 01, 2017 10:27 pm

Ayuto wrote:I won't be able to test that before the weekend, but there is a new method to create entities in CS:GO:
https://github.com/alliedmodders/hl2sdk ... cdb8f11199

Interesting. I just did some quick tests and adding the following to Entity.create seems to work just fine:

Syntax: Select all

CBaseEntity* CBaseEntityWrapper::create(const char* name)
{
#ifdef ENGINE_CSGO
void *pItem = servertools->CreateItemEntityByName(name);
if (pItem)
return (CBaseEntity *)pItem;
#endif
IServerNetworkable* pEntity = get_factory(name)->Create(name);
if (!pEntity)
BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Failed to create entity with class name '%s'.", name)

return pEntity->GetBaseEntity();
}


Syntax: Select all

from weapons.entity import Weapon

weapon = Weapon.create('weapon_ak47')
print("Model pre-spawn:", weapon.model_name)
weapon.spawn()
print('Model post-spawn:', weapon.model_name)


Result:

Code: Select all

Model pre-spawn:
Model post-spawn: models/weapons/w_rif_ak47_dropped.mdl

And that method returns NULL if the given classname is not associated with an item definition index so I think this is safe to handle that directly there since it automatically fallback to the default behavior if it was not a weapon. I went ahead and pushed that to master. Currently compiling and should be available when build #760 is done processing.

Return to “Code examples / Cookbook”

Who is online

Users browsing this forum: No registered users and 13 guests