pnxenopoulos / awpy

Python library to parse, analyze and visualize Counter-Strike 2 data
http://www.awpycs.com
MIT License
387 stars 58 forks source link

Kills accumulated function + Plot map improvements: player colors, bomb new icon, annotations #133

Open pablonieto0981 opened 2 years ago

pablonieto0981 commented 2 years ago

colors_annotations

Hi there,

Based on some testing and needs from my CSGO team (a bunch of GN2/GN1...), I have made the following changes locally, which you might want to implement in the code:

1. Accumulated kills function

I might be mistaken, but I think there is no entry in the parsed data to track total kills by a player at any given round start. I created a function for that, as we thought understanding behavior of player by skill ("or who is just having a good day") might be interesting.

Here below is the function:

def create_kills_dictionary(demo_parsed):

    kills_dict = {}

    ''' Get names '''

    for Round in demo_parsed['gameRounds']:
        for frame in Round['frames']:
            for side in ['ct','t']:
                for player in frame[side]['players']:
                    kills_dict[player['name']] = 0

    for X in range(0,len(demo_parsed['gameRounds'])):
        for kill in demo_parsed['gameRounds'][X]['kills']:
            try: # Sometimes "attackerName" gives None, this is to manage the exception
                kills_dict[kill['attackerName']] = kills_dict[kill['attackerName']] + 1
            except:
                pass
        demo_parsed['gameRounds'][X]['kill_scores'] = dict(kills_dict)

    return demo_parsed

This adds the accumulated number of kills at round start for each round in the demo.

Other changes I have made locally are an option to plot player colors over the default T or CT marker in the round plot, and to add annotations on top of each player. See below (2. and 3. all go into plot.py):

2. Function to plot annotations

def plot_annotations(plot_variable,positions=[],annotations=[],map_name="de_ancient",apply_transformation=False):
    for p, ann in zip(positions, annotations):
        if apply_transformation:
            plot_variable.annotate(ann,(position_transform(map_name, p[0], "x")-15, position_transform(map_name, p[1], "y")-20),fontsize=5,color='white')
        else:
            plot_variable.annotate(ann,(p[0]-15,p[1]-20),fontsize=5,color='white')

    return plot_variable

3. Plot round function with annotations and color input for players and color choice for the bomb

Note that _player_namescolors is a dictionary that should take the exact names used by the players in the game, and a color of choice for each. Here below is an example from Saturday's match:

player_names_colors={'Glaurung':'blue','0_o Draco':'orange','Peluche81 [I CH]':'green',"Tum Tu'pabs":'yellow','R0x0r':'purple','mig2504':'purple','labuguresti':'orange','Hammood taking 1taps':'blue','cascasvelos.PT':'yellow','99p.exe':'green'}

If the function kill_stats={}, the annotations are the health points; if player_names_colors is empty, then no colors are given, and only red / cyan outer marker is used.

An option to decide the color of the bomb is given, and I have made the bomb a Y marker, as it layers well with the team and player color markers.

And here is the function...

def plot_round(
    filename, frames, kill_stats={}, map_name="de_ancient", map_type="original", dark=False, player_names_colors={}, bomb_color='pink'):
    """Creates gif from frame. Writes to filename"""
    if os.path.isdir("csgo_tmp"):
        shutil.rmtree("csgo_tmp/")
    os.mkdir("csgo_tmp")
    image_files = []
    for i, f in tqdm(enumerate(frames)):
        positions = []
        colors = []
        markers = []
        annotations = []

        '''This was the usual drawing of players...'''

        for side in ["ct", "t"]:
            for p in f[side]["players"]:
                if side == "ct":
                    colors.append("cyan")
                else:
                    colors.append("red")
                if p["hp"] == 0:
                    markers.append("x")
                else:
                    markers.append("o")

                pos = (
                    position_transform(map_name, p["x"], "x"),
                    position_transform(map_name, p["y"], "y"),
                )
                positions.append(pos)

                if kill_stats == {}:
                    annotations.append(p['hp'])
                else:
                    annotations.append(kill_stats[p['name']])

        '''Now the bomb is also drawn here...'''

        for object_type in f['world']:

            if object_type['objectType'] == 'bomb':

                colors.append(bomb_color)
                markers.append("1")

                pos = (
                    position_transform(map_name, object_type["x"], "x"),
                    position_transform(map_name, object_type["y"], "y"),
                )

                positions.append(pos)

            else:
                pass   

        '''Here we overlay the colors assigned to each player...'''                

        for side in ["ct", "t"]:
            for p in f[side]["players"]:
                if p['name'] in player_names_colors.keys():
                    markers.append(".")
                    colors.append(player_names_colors[p['name']])
                else:
                    markers.append(".")
                    if side == 't':
                        colors.append('red')
                    if side == 'ct':
                        colors.append('cyan')                                             
                pos = (
                    position_transform(map_name, p["x"], "x"),
                    position_transform(map_name, p["y"], "y"),
                )
                positions.append(pos)   

        f, a = plot_positions(
            positions=positions,
            colors=colors,
            markers=markers,
            map_name=map_name,
            map_type=map_type,
            dark=dark,
        )

        ''' Plotting annotations (HP, kills) '''

        a = plot_annotations(a,positions=positions,annotations=annotations,map_name=map_name)

        image_files.append("csgo_tmp/{}.png".format(i))
        f.savefig(image_files[-1], dpi=300, bbox_inches="tight")
        plt.close()
    images = []
    for file in image_files:
        images.append(imageio.imread(file))
    imageio.mimsave(filename, images)
    shutil.rmtree("csgo_tmp/")
    return True

...yes, we lost the game.

pnxenopoulos commented 2 years ago

Once again with a wonderful set of functions! Everything looks good, it's just a matter of figuring out where to place it. I'll get to this over the coming days.

JanEricNitschke commented 1 year ago

Might make sense to allow for custom color, marker and annotation functions in general in the plot_round function. They should just default to what is currently there.

     try: # Sometimes "attackerName" gives None, this is to manage the exception
         kills_dict[kill['attackerName']] = kills_dict[kill['attackerName']] + 1
     except:
         pass

This should probably be an explicit None check i think.