Page 1 of 1

Damaging a player

Posted: Mon Jan 14, 2013 5:51 pm
by satoon101
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

Posted: Mon Jan 14, 2013 6:17 pm
by satoon101
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

Posted: Mon Jan 14, 2013 6:52 pm
by Omega_K2
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

Posted: Mon Jan 14, 2013 7:05 pm
by satoon101
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

Posted: Tue Jan 15, 2013 12:22 am
by satoon101
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

Posted: Sat Apr 19, 2014 7:06 pm
by arawra
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

Posted: Sun Apr 20, 2014 1:17 am
by satoon101
That one was my fault. The newest update should fix that issue.

Posted: Mon May 05, 2014 9:01 pm
by arawra
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

Posted: Mon May 05, 2014 9:08 pm
by satoon101
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)

Posted: Mon May 05, 2014 9:24 pm
by L'In20Cible
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

Posted: Wed Dec 31, 2014 1:43 am
by arawra
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

Posted: Wed Dec 31, 2014 6:59 am
by Predz
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)

Posted: Wed Dec 31, 2014 2:40 pm
by satoon101
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).