steam-condenser 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/LICENSE +25 -0
  2. data/README.md +30 -0
  3. data/Rakefile +42 -0
  4. data/lib/abstract_class.rb +40 -0
  5. data/lib/byte_buffer.rb +126 -0
  6. data/lib/datagram_channel.rb +70 -0
  7. data/lib/exceptions/buffer_underflow_exception.rb +10 -0
  8. data/lib/exceptions/packet_format_exception.rb +12 -0
  9. data/lib/exceptions/rcon_no_auth_exception.rb +14 -0
  10. data/lib/exceptions/steam_condenser_exception.rb +8 -0
  11. data/lib/exceptions/timeout_exception.rb +10 -0
  12. data/lib/socket_channel.rb +54 -0
  13. data/lib/steam-condenser.rb +3 -0
  14. data/lib/steam/community/dods/dods_class.rb +32 -0
  15. data/lib/steam/community/dods/dods_stats.rb +47 -0
  16. data/lib/steam/community/dods/dods_weapon.rb +54 -0
  17. data/lib/steam/community/game_achievement.rb +26 -0
  18. data/lib/steam/community/game_class.rb +15 -0
  19. data/lib/steam/community/game_stats.rb +92 -0
  20. data/lib/steam/community/game_weapon.rb +29 -0
  21. data/lib/steam/community/l4d/l4d_explosive.rb +23 -0
  22. data/lib/steam/community/l4d/l4d_map.rb +34 -0
  23. data/lib/steam/community/l4d/l4d_stats.rb +166 -0
  24. data/lib/steam/community/l4d/l4d_weapon.rb +23 -0
  25. data/lib/steam/community/steam_group.rb +16 -0
  26. data/lib/steam/community/steam_id.rb +212 -0
  27. data/lib/steam/community/tf2/tf2_class.rb +31 -0
  28. data/lib/steam/community/tf2/tf2_class_factory.rb +38 -0
  29. data/lib/steam/community/tf2/tf2_engineer.rb +25 -0
  30. data/lib/steam/community/tf2/tf2_medic.rb +22 -0
  31. data/lib/steam/community/tf2/tf2_sniper.rb +20 -0
  32. data/lib/steam/community/tf2/tf2_spy.rb +23 -0
  33. data/lib/steam/community/tf2/tf2_stats.rb +39 -0
  34. data/lib/steam/packets/a2a_ack_packet.rb +21 -0
  35. data/lib/steam/packets/a2a_ping_packet.rb +18 -0
  36. data/lib/steam/packets/a2m_get_servers_batch2_packet.rb +25 -0
  37. data/lib/steam/packets/a2s_info_packet.rb +18 -0
  38. data/lib/steam/packets/a2s_player_packet.rb +22 -0
  39. data/lib/steam/packets/a2s_rules_packet.rb +21 -0
  40. data/lib/steam/packets/a2s_serverquery_getchallenge_packet.rb +18 -0
  41. data/lib/steam/packets/m2a_server_batch_packet.rb +37 -0
  42. data/lib/steam/packets/rcon/rcon_auth_request.rb +16 -0
  43. data/lib/steam/packets/rcon/rcon_auth_response.rb +16 -0
  44. data/lib/steam/packets/rcon/rcon_exec_request.rb +16 -0
  45. data/lib/steam/packets/rcon/rcon_exec_response.rb +20 -0
  46. data/lib/steam/packets/rcon/rcon_goldsrc_request.rb +20 -0
  47. data/lib/steam/packets/rcon/rcon_goldsrc_response.rb +20 -0
  48. data/lib/steam/packets/rcon/rcon_packet.rb +36 -0
  49. data/lib/steam/packets/rcon/rcon_packet_factory.rb +37 -0
  50. data/lib/steam/packets/request_with_challenge.rb +16 -0
  51. data/lib/steam/packets/s2a_info2_packet.rb +48 -0
  52. data/lib/steam/packets/s2a_info_base_packet.rb +29 -0
  53. data/lib/steam/packets/s2a_info_detailed_packet.rb +50 -0
  54. data/lib/steam/packets/s2a_player_packet.rb +32 -0
  55. data/lib/steam/packets/s2a_rules_packet.rb +33 -0
  56. data/lib/steam/packets/s2c_challenge_packet.rb +23 -0
  57. data/lib/steam/packets/steam_packet.rb +48 -0
  58. data/lib/steam/packets/steam_packet_factory.rb +90 -0
  59. data/lib/steam/servers/game_server.rb +222 -0
  60. data/lib/steam/servers/goldsrc_server.rb +36 -0
  61. data/lib/steam/servers/master_server.rb +60 -0
  62. data/lib/steam/servers/source_server.rb +65 -0
  63. data/lib/steam/sockets/goldsrc_socket.rb +108 -0
  64. data/lib/steam/sockets/master_server_socket.rb +20 -0
  65. data/lib/steam/sockets/rcon_socket.rb +50 -0
  66. data/lib/steam/sockets/source_socket.rb +72 -0
  67. data/lib/steam/sockets/steam_socket.rb +63 -0
  68. data/lib/steam/steam_player.rb +51 -0
  69. data/test/query_tests.rb +71 -0
  70. data/test/rcon_tests.rb +79 -0
  71. data/test/steam_community_tests.rb +36 -0
  72. metadata +132 -0
@@ -0,0 +1,3 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
@@ -0,0 +1,32 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_class'
7
+
8
+ # Represents the stats for a DoD:S class for a specific user
9
+ class DoDSClass < GameClass
10
+
11
+ attr_reader :blocks, :bombs_defused, :bombs_planted, :captures, :deaths,
12
+ :dominations, :key, :kills, :name, :rounds_lost, :rounds_won,
13
+ :revenges
14
+
15
+ # Creates a new instance of DoDSClass based on the assigned XML data
16
+ def initialize(class_data)
17
+ @blocks = class_data.elements['blocks'].text.to_i
18
+ @bombs_defused = class_data.elements['bombsdefused'].text.to_i
19
+ @bombs_planted = class_data.elements['bombsplanted'].text.to_i
20
+ @captures = class_data.elements['captures'].text.to_i
21
+ @deaths = class_data.elements['deaths'].text.to_i
22
+ @dominations = class_data.elements['dominations'].text.to_i
23
+ @key = class_data.attributes['key']
24
+ @kills = class_data.elements['kills'].text.to_i
25
+ @name = class_data.elements["name"].text
26
+ @play_time = class_data.elements["playtime"].text.to_i
27
+ @rounds_lost = class_data.elements['roundslost'].text.to_i
28
+ @rounds_won = class_data.elements['roundswon'].text.to_i
29
+ @revenges = class_data.elements['revenges'].text.to_i
30
+ end
31
+
32
+ end
@@ -0,0 +1,47 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/dods/dods_weapon'
7
+ require 'steam/community/game_stats'
8
+
9
+ class DoDSStats < GameStats
10
+
11
+ # Creates a DoDSStats object by calling the super constructor with the game
12
+ # name "DoD:S"
13
+ def initialize(steam_id)
14
+ super steam_id, 'DoD:S'
15
+ end
16
+
17
+ # Returns a Hash of DoDSClass for this user containing all DoD:S classes.
18
+ # If the classes haven't been parsed already, parsing is done now.
19
+ def class_stats
20
+ return unless public?
21
+
22
+ if @class_stats.nil?
23
+ @class_stats = {}
24
+ @xml_data.elements.each('stats/classes/class') do |class_data|
25
+ @class_stats[class_data.attributes["key"]] = DoDSClass.new class_data
26
+ end
27
+ end
28
+
29
+ @class_stats
30
+ end
31
+
32
+ # Returns a Hash of DoDSWeapon for this user containing all DoD:S weapons.
33
+ # If the weapons haven't been parsed already, parsing is done now.
34
+ def weapon_stats
35
+ return unless public?
36
+
37
+ if @weapon_stats.nil?
38
+ @weapon_stats = {}
39
+ @xml_data.elements.each('stats/weapons/weapon') do |weapon_data|
40
+ @weapon_stats[weapon_data.attributes['key']] = DoDSWeapon.new(weapon_data)
41
+ end
42
+ end
43
+
44
+ @weapon_stats
45
+ end
46
+
47
+ end
@@ -0,0 +1,54 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_weapon'
7
+
8
+ # Represents the stats for a Day of Defeat: Source weapon for a specific user
9
+ class DoDSWeapon < GameWeapon
10
+
11
+ attr_reader :headshots, :name, :hits
12
+
13
+ # Creates a new instance of DoDSWeapon based on the assigned XML data
14
+ def initialize(weapon_data)
15
+ super weapon_data
16
+
17
+ @headshots = weapon_data.elements['headshots'].text.to_i
18
+ @id = weapon_data.attributes['key']
19
+ @name = weapon_data.elements['name'].text
20
+ @shots = weapon_data.elements['shotsfired'].text.to_i
21
+ @hits = weapon_data.elements['shotshit'].text.to_i
22
+ end
23
+
24
+ # Returns the average number of hits needed for a kill with this weapon.
25
+ # Calculates the value if needed.
26
+ def avg_hits_per_kill
27
+ if @avg_hits_per_kill.nil?
28
+ @avg_hits_per_kill = @hits.to_f / @kills
29
+ end
30
+
31
+ @avg_hits_per_kill
32
+ end
33
+
34
+ # Returns the percentage of headshots relative to the shots hit with this
35
+ # weapon. Calculates the value if needed.
36
+ def headshot_percentage
37
+ if @headshot_percentage.nil?
38
+ @headshot_percentage = @headshots.to_f / @hits
39
+ end
40
+
41
+ @headshot_percentage
42
+ end
43
+
44
+ # Returns the percentage of hits relative to the shots fired with this
45
+ # weapon. Calculates the value if needed.
46
+ def hit_percentage
47
+ if @hit_percentage.nil?
48
+ @hit_percentage = @hits.to_f / @shots
49
+ end
50
+
51
+ @hit_percentage
52
+ end
53
+
54
+ end
@@ -0,0 +1,26 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ # The GameAchievement class represents a specific achievement for a single game
7
+ # and for a single user
8
+ class GameAchievement
9
+
10
+ attr_reader :app_id, :name, :steam_id
11
+
12
+ # Creates the achievement with the given name for the given user and game and
13
+ # marks it as already done if set to true
14
+ def initialize(steam_id, app_id, name, done)
15
+ @app_id = app_id
16
+ @done = done
17
+ @name = name
18
+ @steam_id = steam_id
19
+ end
20
+
21
+ # Returns whether this achievement has been done by its owner
22
+ def done?
23
+ @done
24
+ end
25
+
26
+ end
@@ -0,0 +1,15 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'abstract_class'
7
+
8
+ # Super class for classes representing player classes
9
+ class GameClass
10
+
11
+ include AbstractClass
12
+
13
+ attr_reader :name, :play_time
14
+
15
+ end
@@ -0,0 +1,92 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2008-2009, Sebastian Staudt
5
+
6
+ require "open-uri"
7
+ require "rexml/document"
8
+
9
+ require "steam/community/game_achievement"
10
+
11
+ class GameStats
12
+ end
13
+
14
+ require "steam/community/l4d/l4d_stats"
15
+ require "steam/community/dods/dods_stats"
16
+ require "steam/community/tf2/tf2_stats"
17
+
18
+ # The GameStats class represents the game statistics for a single user and a
19
+ # specific game
20
+ class GameStats
21
+
22
+ protected :initialize
23
+
24
+ attr_reader :app_id, :game_friendly_name, :game_name, :hours_played,
25
+ :privacy_state, :steam_id
26
+
27
+ # Creates a GameStats (or one of its subclasses) object for the given user
28
+ # depending on the game selected
29
+ def self.create_game_stats(steam_id, game_name)
30
+ case game_name
31
+ when "DoD:S":
32
+ DoDSStats.new(steam_id)
33
+ when "L4D":
34
+ L4DStats.new(steam_id)
35
+ when "TF2":
36
+ TF2Stats.new(steam_id)
37
+ else
38
+ new(steam_id, game_name)
39
+ end
40
+ end
41
+
42
+ # Creates a GameStats object and fetchs data from Steam Community for the
43
+ # given user and game
44
+ def initialize(steam_id, game_name)
45
+ @steam_id = steam_id
46
+
47
+ @xml_data = REXML::Document.new(open("http://www.steamcommunity.com/id/#{@steam_id}/stats/#{game_name}?xml=1", {:proxy => true}).read).elements["playerstats"]
48
+
49
+ @privacy_state = @xml_data.elements["privacyState"].text
50
+ if public?
51
+ @app_id = @xml_data.elements["game"].elements["gameLink"].text.match("http://store.steampowered.com/app/([1-9][0-9]+)")[1]
52
+ @game_friendly_name = @xml_data.elements["game"].elements["gameFriendlyName"].text
53
+ @game_name = @xml_data.elements["game"].elements["gameName"].text
54
+ @hours_played = @xml_data.elements["stats"].elements["hoursPlayed"].text
55
+ end
56
+ end
57
+
58
+ # Returns the achievements for this stats' user and game. If the achievements
59
+ # haven't been parsed already, parsing is done now.
60
+ def achievements
61
+ return unless public?
62
+
63
+ if @achievements.nil?
64
+ @achievements = Array.new
65
+ @xml_data.elements["achievements"].elements.each("achievement") do |achievement|
66
+ @achievements << GameAchievement.new(@steam_id, @app_id, achievement.elements["name"].text, (achievement.attributes["closed"].to_i == 1))
67
+ end
68
+
69
+ @achievements_done = @achievements.reject{ |a| !a.done? }.size
70
+ end
71
+
72
+ return @achievements
73
+ end
74
+
75
+ # Returns the count of achievements done by this player. If achievements
76
+ # haven't been parsed yet, parsing is done now.
77
+ def achievements_done
78
+ achievements if @achievements_done.nil?
79
+ @achievements_done
80
+ end
81
+
82
+ # Returns a float value representing the percentage of achievements done by
83
+ # this player.
84
+ def achievements_percentage
85
+ achievements_done.to_f / @achievements.size
86
+ end
87
+
88
+ def public?
89
+ @privacy_state == 'public'
90
+ end
91
+
92
+ end
@@ -0,0 +1,29 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'abstract_class'
7
+
8
+ # Super class for classes representing game weapons
9
+ class GameWeapon
10
+
11
+ include AbstractClass
12
+
13
+ attr_reader :kills, :id, :shots
14
+
15
+ def initialize(weapon_data)
16
+ @kills = weapon_data.elements['kills'].text.to_i
17
+ end
18
+
19
+ # Returns the average number of shots needed for a kill with this weapon.
20
+ # Calculates the value if needed.
21
+ def avg_shots_per_kill
22
+ if @avg_shots_per_kill.nil?
23
+ @avg_shots_per_kill = @shots.to_f / @kills
24
+ end
25
+
26
+ @avg_shots_per_kill
27
+ end
28
+
29
+ end
@@ -0,0 +1,23 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_weapon'
7
+
8
+ class L4DExplosive < GameWeapon
9
+
10
+ # Creates a new instance of L4DExplosive based on the assigned XML data
11
+ def initialize(weapon_data)
12
+ super weapon_data
13
+
14
+ @id = weapon_data.name
15
+ @shots = weapon_data.elements['thrown'].text.to_i
16
+ end
17
+
18
+ # Returns the average number of kills for one shot of this explosive.
19
+ def avg_kills_per_shot
20
+ 1 / avg_shots_per_kill
21
+ end
22
+
23
+ end
@@ -0,0 +1,34 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ class L4DMap
7
+
8
+ attr_reader :best_time, :id, :medal, :name, :times_played
9
+
10
+ GOLD = 1
11
+ SILVER = 2
12
+ BRONZE = 3
13
+ NONE = 0
14
+
15
+ # Creates a new instance of L4DMap based on the assigned XML data
16
+ def initialize(map_data)
17
+ @best_time = map_data.elements['besttimeseconds'].text.to_f
18
+ @id = map_data.name
19
+ @name = map_data.elements['name'].text
20
+ @times_played = map_data.elements['timesplayed'].text.to_i
21
+
22
+ case map_data.elements['medal'].text
23
+ when 'gold':
24
+ @medal = L4DMap::GOLD
25
+ when 'silver':
26
+ @medal = L4DMap::SILVER
27
+ when 'bronze':
28
+ @medal = L4DMap::BRONZE
29
+ else
30
+ @medal = L4DMap::NONE
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,166 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'steam/community/game_stats'
7
+ require 'steam/community/l4d/l4d_explosive'
8
+ require 'steam/community/l4d/l4d_map'
9
+ require 'steam/community/l4d/l4d_weapon'
10
+
11
+ class L4DStats < GameStats
12
+
13
+ attr_reader :xml_data
14
+
15
+ # Creates a L4DStats object by calling the super constructor with the game
16
+ # name "l4d"
17
+ def initialize(steam_id)
18
+ super steam_id, 'l4d'
19
+
20
+ if public?
21
+ @most_recent_game = {}
22
+ @most_recent_game['difficulty'] = @xml_data.elements['stats/mostrecentgame/difficulty'].text
23
+ @most_recent_game['escaped'] = (@xml_data.elements['stats/mostrecentgame/bEscaped'].text == 1)
24
+ @most_recent_game['movie'] = @xml_data.elements['stats/mostrecentgame/movie'].text
25
+ @most_recent_game['time_played'] = @xml_data.elements['stats/mostrecentgame/time'].text
26
+ end
27
+ end
28
+
29
+ # Returns a Hash of favorites for this user like weapons and character.
30
+ # If the favorites haven't been parsed already, parsing is done now.
31
+ def favorites
32
+ return unless public?
33
+
34
+ if @favorites.nil?
35
+ @favorites = {}
36
+ @favorites['campaign'] = @xml_data.elements['stats/favorites/campaign'].text
37
+ @favorites['campaign_percentage'] = @xml_data.elements['stats/favorites/campaignpct'].text.to_i
38
+ @favorites['character'] = @xml_data.elements['stats/favorites/character'].text
39
+ @favorites['character_percentage'] = @xml_data.elements['stats/favorites/characterpct'].text.to_i
40
+ @favorites['level1_weapon'] = @xml_data.elements['stats/favorites/weapon1'].text
41
+ @favorites['level1_weapon_percentage'] = @xml_data.elements['stats/favorites/weapon1pct'].text.to_i
42
+ @favorites['level2_weapon'] = @xml_data.elements['stats/favorites/weapon2'].text
43
+ @favorites['level2_weapon_percentage'] = @xml_data.elements['stats/favorites/weapon2pct'].text.to_i
44
+ end
45
+
46
+ @favorites
47
+ end
48
+
49
+ # Returns a Hash of lifetime statistics for this user like the time played.
50
+ # If the lifetime statistics haven't been parsed already, parsing is done now.
51
+ def lifetime_stats
52
+ return unless public?
53
+
54
+ if @lifetime_stats.nil?
55
+ @lifetime_stats = {}
56
+ @lifetime_stats['finales_survived'] = @xml_data.elements['stats/lifetime/finales'].text.to_i
57
+ @lifetime_stats['games_played'] = @xml_data.elements['stats/lifetime/gamesplayed'].text.to_i
58
+ @lifetime_stats['finales_survived_percentage'] = @lifetime_stats['finales_survived'].to_f / @lifetime_stats['games_played']
59
+ @lifetime_stats['infected_killed'] = @xml_data.elements['stats/lifetime/infectedkilled'].text.to_i
60
+ @lifetime_stats['kills_per_hour'] = @xml_data.elements['stats/lifetime/killsperhour'].text.to_f
61
+ @lifetime_stats['avg_kits_shared'] = @xml_data.elements['stats/lifetime/kitsshared'].text.to_f
62
+ @lifetime_stats['avg_kits_used'] = @xml_data.elements['stats/lifetime/kitsused'].text.to_f
63
+ @lifetime_stats['avg_pills_shared'] = @xml_data.elements['stats/lifetime/pillsshared'].text.to_f
64
+ @lifetime_stats['avg_pills_used'] = @xml_data.elements['stats/lifetime/pillsused'].text.to_f
65
+ @lifetime_stats['time_played'] = @xml_data.elements['stats/lifetime/timeplayed'].text
66
+ end
67
+
68
+ @lifetime_stats
69
+ end
70
+
71
+ # Returns a Hash of Survival statistics for this user like revived teammates.
72
+ # If the Survival statistics haven't been parsed already, parsing is done now.
73
+ def survival_stats
74
+ return unless public?
75
+
76
+ if @survival_stats.nil?
77
+ @survival_stats = {}
78
+ @survival_stats['gold_medals'] = @xml_data.elements['stats/survival/goldmedals'].text.to_i
79
+ @survival_stats['silver_medals'] = @xml_data.elements['stats/survival/silvermedals'].text.to_i
80
+ @survival_stats['bronze_medals'] = @xml_data.elements['stats/survival/bronzemedals'].text.to_i
81
+ @survival_stats['rounds_played'] = @xml_data.elements['stats/survival/roundsplayed'].text.to_i
82
+ @survival_stats['best_time'] = @xml_data.elements['stats/survival/besttime'].text.to_f
83
+
84
+ @survival_stats['maps'] = {}
85
+ @xml_data.elements.each('stats/survival/maps/*') do |map_data|
86
+ @survival_stats['maps'][map_data.name] = L4DMap.new(map_data)
87
+ end
88
+ end
89
+
90
+ @survival_stats
91
+ end
92
+
93
+ # Returns a Hash of teamplay statistics for this user like revived teammates.
94
+ # If the teamplay statistics haven't been parsed already, parsing is done now.
95
+ def teamplay_stats
96
+ return unless public?
97
+
98
+ if @teamplay_stats.nil?
99
+ @teamplay_stats = {}
100
+ @teamplay_stats['revived'] = @xml_data.elements['stats/teamplay/revived'].text.to_i
101
+ @teamplay_stats['most_revived_difficulty'] = @xml_data.elements['stats/teamplay/reviveddiff'].text
102
+ @teamplay_stats['avg_revived'] = @xml_data.elements['stats/teamplay/revivedavg'].text.to_f
103
+ @teamplay_stats['avg_was_revived'] = @xml_data.elements['stats/teamplay/wasrevivedavg'].text.to_f
104
+ @teamplay_stats['protected'] = @xml_data.elements['stats/teamplay/protected'].text.to_i
105
+ @teamplay_stats['most_protected_difficulty'] = @xml_data.elements['stats/teamplay/protecteddiff'].text
106
+ @teamplay_stats['avg_protected'] = @xml_data.elements['stats/teamplay/protectedavg'].text.to_f
107
+ @teamplay_stats['avg_was_protected'] = @xml_data.elements['stats/teamplay/wasprotectedavg'].text.to_f
108
+ @teamplay_stats['friendly_fire_damage'] = @xml_data.elements['stats/teamplay/ffdamage'].text.to_i
109
+ @teamplay_stats['most_friendly_fire_difficulty'] = @xml_data.elements['stats/teamplay/ffdamagediff'].text
110
+ @teamplay_stats['avg_friendly_fire_damage'] = @xml_data.elements['stats/teamplay/ffdamageavg'].text.to_f
111
+ end
112
+
113
+ @teamplay_stats
114
+ end
115
+
116
+ # Returns a Hash of Versus statistics for this user like percentage of rounds
117
+ # won.
118
+ # If the Versus statistics haven't been parsed already, parsing is done now.
119
+ def versus_stats
120
+ return unless public?
121
+
122
+ if @versus_stats.nil?
123
+ @versus_stats = {}
124
+ @versus_stats['games_played'] = @xml_data.elements['stats/versus/gamesplayed'].text.to_i
125
+ @versus_stats['games_completed'] = @xml_data.elements['stats/versus/gamescompleted'].text.to_i
126
+ @versus_stats['finales_survived'] = @xml_data.elements['stats/versus/finales'].text.to_i
127
+ @versus_stats['finales_survived_percentage'] = @versus_stats['finales_survived'].to_f / @versus_stats['games_played']
128
+ @versus_stats['points'] = @xml_data.elements['stats/versus/points'].text.to_i
129
+ @versus_stats['most_points_infected'] = @xml_data.elements['stats/versus/pointsas'].text
130
+ @versus_stats['games_won'] = @xml_data.elements['stats/versus/gameswon'].text.to_i
131
+ @versus_stats['games_lost'] = @xml_data.elements['stats/versus/gameslost'].text.to_i
132
+ @versus_stats['highest_survivor_score'] = @xml_data.elements['stats/versus/survivorscore'].text.to_i
133
+
134
+ %w(boomer hunter smoker tank).each do |infected|
135
+ @versus_stats[infected] = {}
136
+ @versus_stats[infected]['special'] = @xml_data.elements["stats/versus/#{infected}special"].text.to_i
137
+ @versus_stats[infected]['most_damage'] = @xml_data.elements["stats/versus/#{infected}dmg"].text.to_i
138
+ @versus_stats[infected]['avg_lifespan'] = @xml_data.elements["stats/versus/#{infected}lifespan"].text.to_i
139
+ end
140
+ end
141
+
142
+ @versus_stats
143
+ end
144
+
145
+ # Returns a Hash of L4DWeapon for this user containing all Left4Dead weapons.
146
+ # If the weapons haven't been parsed already, parsing is done now.
147
+ def weapon_stats
148
+ return unless public?
149
+
150
+ if @weapon_stats.nil?
151
+ @weapon_stats = {}
152
+ @xml_data.elements.each('stats/weapons/*') do |weapon_data|
153
+ unless weapon_data.name == 'molotov' or weapon_data.name == 'pipes'
154
+ weapon = L4DWeapon.new(weapon_data)
155
+ else
156
+ weapon = L4DExplosive.new(weapon_data)
157
+ end
158
+
159
+ @weapon_stats[weapon_data.name] = weapon
160
+ end
161
+ end
162
+
163
+ @weapon_stats
164
+ end
165
+
166
+ end