Damaging a player

Post Python examples to help other users.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Damaging a player

Postby satoon101 » Mon Jan 14, 2013 5:51 pm

Not sure if you all have been keeping track of the repository, but L'In20Cible and I worked through the issues we had with getting damage to work, and it now works properly (with the exception of the hitgroup offset). All entities, not just players, can damage any other entity, so we added it to the BaseEntity class instead of just the PlayerEntity class.

We also added constants for damage types, so you do not have to remember them, and if they change, it will be a quick plugin-side fix instead of a script-by-script fix. The test script I use is as follows:

Syntax: Select all

from events import Event
from players.entity import PlayerEntity
from players.helpers import index_from_userid
from filters.players import PlayerIter
from entities.constants import DamageTypes

hitgroups = [
'Generic',
'Head',
'Chest',
'Stomach',
'Left Arm',
'Right Arm',
'Left Leg',
'Right Leg'
]

@Event
def round_freeze_end(GameEvent):
for player in PlayerIter(return_types='player'):
player.freeze = True

@Event
def player_say(GameEvent):
userid = GameEvent.GetInt('userid')
index = index_from_userid(userid)
player = PlayerEntity(index)
team = player.team
other = get_other_player(team)
player.damage(other, 400, DamageTypes.HEADSHOT, hitgroup=2, iObjectsPenetrated=2)

def get_other_player(team):
for player in PlayerIter(return_types='player'):
if player.team != team and not player.isdead:
return player.index
return None

@Event
def player_hurt(GameEvent):
userid = GameEvent.GetInt('userid')
index = index_from_userid(userid)
player = PlayerEntity(index)
attackerid = GameEvent.GetInt('attacker')
attacker_index = index_from_userid(attackerid)
attacker = PlayerEntity(attacker_index)
damage = GameEvent.GetInt('dmg_health')
armor = GameEvent.GetInt('dmg_armor')
weapon = GameEvent.GetString('weapon')
hitgroup = hitgroups[GameEvent.GetInt('hitgroup')]
print('%s damaged %s for %s/%s health with his %s in the %s' % (
attacker.name, player.name, damage, armor, weapon, hitgroup))
Again, the hitgroup offset is wrong, and I still need to look that up again. If you have any other questions on this please feel free to ask away.

Satoon
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Jan 14, 2013 6:17 pm

I should probably add a bit more information on some of the things that script does internally.


The "damage" method takes one required argument and 4 optional arguments:
  1. <victim_index> - index of the entity being damaged
  2. [damage] - the amount of damage to be done (more below)
  3. [damage_types] - the type of damage to be done (more below)
  4. [weapon_index] - the index of the weapon used to do the damage
  5. [hitgroup] - the hitgroup that should be damaged (only works for damaging player entities)
The damage method also can take keyword arguments. This is done so that you can set (as shown in my example above) other CTakeDamageInfo values when calling TakeDamage. I have not yet implemented them all, but here are the ones for CS:GO:
  • vecDamageForce.x
  • vecDamageForce.y
  • vecDamageForce.z
  • vecDamagePosition.x
  • vecDamagePosition.y
  • vecDamagePosition.z
  • vecReportedPosition.x
  • vecReportedPosition.y
  • vecReportedPosition.z
  • flMaxDamage
  • flBaseDamage
  • iDamageCustom
  • iDamageStats
  • iAmmoType
  • flRadius
  • iDamagedOtherPlayers
  • iObjectsPenetrated
  • uiBulletID
  • uiRecoilIndex
Notice that hAttacker, hInflictor, hWeapon, and bitsDamageType are not listed as those are already set using the arguments. Make sure you are passing the proper type of value when using keywords, otherwise you will encounter errors.


The damage amount that you pass will not necessarily be the actual damage done to the player's health. I will probably be looking into the algorithms that Valve uses to backtrack them to set the proper amount of damage to be done, at some point.


The damage types are now available via the entity.constants module. The values for CS:GO (and CS:S) are as follows:
  • GENERIC = 0
  • CRUSH = 1
  • BULLET = 2
  • SLASH = 4
  • BURN = 8
  • VEHICLE = 16
  • FALL = 32
  • BLAST = 64
  • CLUB = 128
  • SHOCK = 256
  • SONIC = 512
  • ENERGYBEAM = 1024
  • PREVENT_PHYSICS_FORCE = 2048
  • NEVERGIB = 4096
  • ALWAYSGIB = 8192
  • DROWN = 16384
  • PARALYZE = 32768
  • NERVEGAS = 65536
  • POISON = 131072
  • RADIATION = 262144
  • DROWNRECOVER = 524288
  • ACID = 1048576
  • SLOWBURN = 2097152
  • REMOVENORAGDOLL = 4194304
  • PHYSGUN = 8388608
  • PLASMA = 16777216
  • AIRBOAT = 33554432
  • DISSOLVE = 67108864
  • BLAST_SURFACE = 134217728
  • DIRECT = 268435456
  • BUCKSHOT = 536870912
  • HEADSHOT = 1073741824
As shown above, you would use them like this:

Syntax: Select all

from entity.constants import DamageTypes

print(DamageTypes.HEADSHOT)

Satoon
Omega_K2
Senior Member
Posts: 227
Joined: Sat Jul 07, 2012 3:05 am
Location: Europe
Contact:

Postby Omega_K2 » Mon Jan 14, 2013 6:52 pm

satoon101 wrote:The damage amount that you pass will not necessarily be the actual damage done to the player's health. I will probably be looking into the algorithms that Valve uses to backtrack them to set the proper amount of damage to be done, at some point.


Looking at the parameters I'd guess it depends on the damage type. i.e. any bullet-style stuff will also take player armor into consideration as well as hitgroups I suppose, while stuff like falldamage probably will rely more on the vectors.

Anyway, I saw the change in repo earlier today - pretty cool :D
Libraries: k2tools
Plugins (any): GSRPG (soon) | Pretty Status List | MySQLAds (soon)
Plugins (game-specific): None atm

If you happen to find a bug or need help, either post in the release threads or contact me in IRC gamesurge.net:6667 / #sourcepython
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon Jan 14, 2013 7:05 pm

Thank you :)

Yeah, the damage might get a bit too complicated based on a number of things. So, I am not sure how feasible it will be to get the proper damage, unfortunately, but we will look into it.

Also, the hitgroup for Windows servers is correct, now, but I have yet to test on Linux.

Satoon
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Tue Jan 15, 2013 12:22 am

I am having all sorts of connection issues with my Linux server, but the hitgroup offset seems to be correctly on it, as well.

Satoon
arawra
Senior Member
Posts: 190
Joined: Fri Jun 21, 2013 6:51 am

Postby arawra » Sat Apr 19, 2014 7:06 pm

Again, wondering if this is my issue or if there hasn't been an update to the methods and properties.

Code: Select all

@SayCommand('!test')
def test(player, teamonly, CCommand):
    myPlayer = PlayerEntity(index_from_playerinfo(player))
    myPlayer.damage(myPlayer.index, 1, DamageTypes.NERVEGAS, hitgroup='Generic')


Image
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Sun Apr 20, 2014 1:17 am

That one was my fault. The newest update should fix that issue.
arawra
Senior Member
Posts: 190
Joined: Fri Jun 21, 2013 6:51 am

Postby arawra » Mon May 05, 2014 9:01 pm

Just a simple type error typo, I hope?

Code: Select all

@SayCommand('!test')
def test(player, teamonly, CCommand):
    myPlayer = PlayerEntity(index_from_playerinfo(player))
    myPlayer.damage(myPlayer.index, 1, DamageTypes.NERVEGAS, hitgroup='Generic')


Image
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Mon May 05, 2014 9:08 pm

Let's take a look at that error:

Code: Select all

Boost.Python.ArgumentError: Python argument types in
    Pointer.set_int([b][color=green]Pointer[/color], [color=red]str[/color], [color=green]int[/color][/b])
did not match C++ signature:
    set_int([b][color=green]class CPointer {lvalue}[/color], [color=red]int[/color], [color=green]int offset=0[/color][/b])

So, in setting the m_LastHitGroup value, you are attempting to set it to a string when it should be an integer value. For "generic" damage, use 0.

Code: Select all

@SayCommand('!test')
def test(player, teamonly, CCommand):
    myPlayer = PlayerEntity(index_from_playerinfo(player))
    myPlayer.damage(myPlayer.index, 1, DamageTypes.NERVEGAS, hitgroup=0)


Or, since 0 is the default value, you don't even need to pass in the hitgroup:

Code: Select all

@SayCommand('!test')
def test(player, teamonly, CCommand):
    myPlayer = PlayerEntity(index_from_playerinfo(player))
    myPlayer.damage(myPlayer.index, 1, DamageTypes.NERVEGAS)
User avatar
L'In20Cible
Project Leader
Posts: 1533
Joined: Sat Jul 14, 2012 9:29 pm
Location: Québec

Postby L'In20Cible » Mon May 05, 2014 9:24 pm

Hit group is an integer:

Code: Select all

#define   HITGROUP_GENERIC   0
#define   HITGROUP_HEAD      1
#define   HITGROUP_CHEST      2
#define   HITGROUP_STOMACH   3
#define HITGROUP_LEFTARM   4   
#define HITGROUP_RIGHTARM   5
#define HITGROUP_LEFTLEG   6
#define HITGROUP_RIGHTLEG   7
#define HITGROUP_GEAR      10
arawra
Senior Member
Posts: 190
Joined: Fri Jun 21, 2013 6:51 am

Postby arawra » Wed Dec 31, 2014 1:43 am

Is the damage still working?

Syntax: Select all

@Event        
def player_hurt(game_event):
attacker_userid = game_event.get_int('attacker')
victim_userid = game_event.get_int('userid')
damage = game_event.get_int('dmg_health')

attacker = dndPlayerDictionary[attacker_userid]
victim = dndPlayerDictionary[victim_userid]

tell(playerinfo_from_userid(attacker_userid), 'victim hp: %s'%victim.health)
tell(playerinfo_from_userid(attacker_userid), 'victim damage: %s'%damage)
attacker.damage(victim.index, 1, DamageTypes.NERVEGAS)
tell(playerinfo_from_userid(attacker_userid), 'victim hp: %s'%victim.health)


Code: Select all

[D&D] victim hp: 89
[D&D] victim damage: 11
[D&D] victim hp: 89
Achievements disabled: cheats turned on in this app session.
Achievements disabled: cheats turned on in this app session.
[D&D] victim hp: 85
[D&D] victim damage: 15
[D&D] victim hp: 85
Achievements disabled: cheats turned on in this app session.
[D&D] victim hp: 69
[D&D] victim damage: 16
[D&D] victim hp: 69
Predz
Senior Member
Posts: 158
Joined: Wed Aug 08, 2012 9:05 pm
Location: Bristol, United Kingdom

Postby Predz » Wed Dec 31, 2014 6:59 am

If I were you I would take a look at the available functions in PlayerEntity. "take_damage" is a function that is callable and should be supplied a "CTakeDamageInfo" instance.

It relies that you have the correct signatures/offsets though. So this method may break at some point, but is definitely the most stable method.

Example:

Syntax: Select all

from players.entity import PlayerEntity

from players.helpers import index_from_userid
from players.helpers import inthandle_from_userid

from entities.constants import CTakeDamageInfo

WARCRAFT_DAMAGE_TYPES = {'GENERIC' : 0, 'CRUSH' : 1, 'BULLET' : 2,
'SLASH' : 4, 'BURN' : 8, 'VEHICLE' : 16, 'FALL' : 32, 'BLAST' : 64,
'CLUB' : 128, 'SHOCK' : 256, 'SONIC' : 512, 'ENERGYBEAM' : 1024,
'PREVENT_PHYSICS_FORCE' : 2048, 'NEVERGIB' : 4096, 'ALWAYSGIB' : 8192,
'DROWN' : 16384, 'PARALYZE' : 32768, 'NERVEGAS' : 65536, 'POISON' : 131072,
'RADIATION' : 262144, 'DROWNRECOVER' : 524288, 'ACID' : 1048576,
'SLOWBURN' : 2097152, 'REMOVENORAGDOLL' : 4194304, 'PHYSGUN' : 8388608,
'PLASMA' : 16777216, 'AIRBOAT' : 33554432, 'DISSOLVE' : 67108864,
'BLAST_SURFACE' : 134217728, 'DIRECT' : 268435456, 'BUCKSHOT' : 536870912,
'HEADSHOT' : 1073741824}

def damage(victim, attacker, damage, type, weapon=0):
index = index_from_userid(victim)
player = PlayerEntity(index)
if player.pl.deadflag == 0:
take_damage_info = CTakeDamageInfo()
take_damage_info.hAttacker = inthandle_from_userid(attacker)
take_damage_info.hInflictor = inthandle_from_userid(attacker)
take_damage_info.hWeapon = weapon
take_damage_info.flDamage = damage
if str(type).isdigit():
take_damage_info.bitsDamageType = type
else:
take_damage_info.bitsDamageType = WARCRAFT_DAMAGE_TYPES[type]
player.take_damage(take_damage_info)
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Wed Dec 31, 2014 2:40 pm

BaseEntity already has a built-in wrapper for that. I haven't tested calling it in a while, but there are planned changes for it that should happen soon.

Also, don't use 'type' as a variable name as it is a built-in function in Python. And, all damage types are already stored in entities.constants as damage_types (DamageTypes in master branch will eventually be changed).
Image

Return to “Code examples / Cookbook”

Who is online

Users browsing this forum: No registered users and 20 guests