lzrtag-base 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +98 -0
- data/docs/EventSymbols.md +117 -0
- data/docs/MQTTBranches.md +74 -0
- data/lib/lzrtag.rb +13 -0
- data/lib/lzrtag/game/base_game.rb +220 -0
- data/lib/lzrtag/handler/base_handler.rb +169 -0
- data/lib/lzrtag/handler/count_handler.rb +55 -0
- data/lib/lzrtag/handler/game_handler.rb +258 -0
- data/lib/lzrtag/handler/hitArb_handler.rb +80 -0
- data/lib/lzrtag/hooks/base_hook.rb +143 -0
- data/lib/lzrtag/hooks/standard_hooks.rb +174 -0
- data/lib/lzrtag/map/map_set.rb +48 -0
- data/lib/lzrtag/map/map_zone.rb +95 -0
- data/lib/lzrtag/map/myMaps_parser.rb +136 -0
- data/lib/lzrtag/player/base_player.rb +104 -0
- data/lib/lzrtag/player/effects_player.rb +96 -0
- data/lib/lzrtag/player/hardware_player.rb +251 -0
- data/lib/lzrtag/player/life_player.rb +116 -0
- metadata +131 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
|
2
|
+
require 'xmlsimple'
|
3
|
+
|
4
|
+
require_relative 'map_zone.rb'
|
5
|
+
|
6
|
+
module LZRTag
|
7
|
+
module Map
|
8
|
+
class MyMapsParser
|
9
|
+
attr_accessor :styles
|
10
|
+
attr_accessor :polygons
|
11
|
+
attr_accessor :points
|
12
|
+
|
13
|
+
def initialize(filename)
|
14
|
+
@xmlStructure = XmlSimple.xml_in(filename)["Document"][0];
|
15
|
+
|
16
|
+
@styles = Hash.new();
|
17
|
+
_fetch_styles();
|
18
|
+
|
19
|
+
@polygons = Hash.new();
|
20
|
+
@polygons[""] = _fetch_polygons();
|
21
|
+
|
22
|
+
@points = Hash.new();
|
23
|
+
@points[""] = _fetch_marks();
|
24
|
+
|
25
|
+
if(folders = @xmlStructure["Folder"])
|
26
|
+
folders.each do |folder|
|
27
|
+
@polygons[folder["name"][0]] = _fetch_polygons(folder)
|
28
|
+
@points[folder["name"][0]] = _fetch_marks(folder);
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def _fetch_styles()
|
34
|
+
@xmlStructure["Style"].each do |s|
|
35
|
+
id = s["id"];
|
36
|
+
if(id =~ /(.*)-normal$/)
|
37
|
+
id = $1;
|
38
|
+
else
|
39
|
+
next;
|
40
|
+
end
|
41
|
+
|
42
|
+
next unless(s.has_key?("PolyStyle") && s.has_key?("LineStyle"))
|
43
|
+
|
44
|
+
@styles["#" + id] = {
|
45
|
+
color: s["PolyStyle"][0]["color"][0],
|
46
|
+
borderColor: s["LineStyle"][0]["color"][0]
|
47
|
+
};
|
48
|
+
end
|
49
|
+
end
|
50
|
+
private :_fetch_styles
|
51
|
+
|
52
|
+
def _fetch_polygons(folder = nil)
|
53
|
+
folder ||= @xmlStructure;
|
54
|
+
|
55
|
+
outZones = Array.new();
|
56
|
+
|
57
|
+
return outZones unless(placemarks = folder["Placemark"])
|
58
|
+
|
59
|
+
placemarks.each do |zone|
|
60
|
+
next unless zone["Polygon"];
|
61
|
+
|
62
|
+
outZone = Hash.new();
|
63
|
+
outZone[:name] = zone["name"][0];
|
64
|
+
outZone[:description] = (zone["description"] || [""])[0];
|
65
|
+
|
66
|
+
outZone[:arguments] = Hash.new();
|
67
|
+
outZone[:description].split("<br>").each do |tag|
|
68
|
+
if(tag =~ /([^:]*):([^:]*)/)
|
69
|
+
outZone[:arguments][$1] = $2;
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
outZone[:style] = @styles[zone["styleUrl"][0]];
|
74
|
+
|
75
|
+
rawPolyData = zone["Polygon"][0]["outerBoundaryIs"][0]["LinearRing"][0]["coordinates"][0];
|
76
|
+
rawPolyData.gsub!(" ", "");
|
77
|
+
rawPolyArray = rawPolyData.split("\n");
|
78
|
+
|
79
|
+
outZone[:polygon] = Array.new();
|
80
|
+
rawPolyArray.each do |point|
|
81
|
+
point = point.split(",");
|
82
|
+
next if point.empty?
|
83
|
+
outZone[:polygon] << [point[0].to_f, point[1].to_f];
|
84
|
+
end
|
85
|
+
|
86
|
+
outZones << outZone;
|
87
|
+
end
|
88
|
+
|
89
|
+
return outZones;
|
90
|
+
end
|
91
|
+
private :_fetch_polygons
|
92
|
+
|
93
|
+
def _fetch_marks(folder = nil)
|
94
|
+
folder ||= @xmlStructure;
|
95
|
+
|
96
|
+
outPoints = Array.new();
|
97
|
+
return outPoints unless(placemarks = folder["Placemark"])
|
98
|
+
|
99
|
+
placemarks.each do |pmark|
|
100
|
+
next unless pmark["Point"];
|
101
|
+
|
102
|
+
outPoint = Hash.new();
|
103
|
+
outPoint[:name] = pmark["name"][0];
|
104
|
+
outPoint[:description] = (pmark["description"] || [""])[0];
|
105
|
+
|
106
|
+
outPoint[:description].split("<br>").each do |tag|
|
107
|
+
if(tag =~ /([^:]*):([^:]*)/)
|
108
|
+
outPoint[:arguments][$1] = $2;
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
outPoint[:point] = pmark["Point"][0]["coordinates"][0].gsub(/\s/, "").split(",")[0..1];
|
113
|
+
|
114
|
+
outPoints << outPoint
|
115
|
+
end
|
116
|
+
|
117
|
+
return outPoints
|
118
|
+
end
|
119
|
+
|
120
|
+
def generate_zones(zoneSet = "")
|
121
|
+
if(zoneSet.is_a? String)
|
122
|
+
zoneSet = @polygons[zoneSet];
|
123
|
+
end
|
124
|
+
|
125
|
+
zoneSet = [zoneSet].flatten;
|
126
|
+
|
127
|
+
outZones = Array.new();
|
128
|
+
zoneSet.each do |rawZone|
|
129
|
+
outZones << Zone.from_raw_zone(rawZone);
|
130
|
+
end
|
131
|
+
|
132
|
+
return outZones;
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
require 'mqtt/sub_handler'
|
3
|
+
|
4
|
+
module LZRTag
|
5
|
+
module Player
|
6
|
+
# The base player class.
|
7
|
+
# This class is not instantiated by the user, but instead on a on-demand basis
|
8
|
+
# by the LZRTag::Handler::Base when a new PlayerID needs to be registered. The player classes
|
9
|
+
# process and send MQTT data, handle events, and keep track of per-player infos like life,
|
10
|
+
# damage, ammo, team, etc. etc.
|
11
|
+
class Base
|
12
|
+
attr_reader :handler
|
13
|
+
# @return [String] The player's DeviceID, which is derived from the ESP's MAC
|
14
|
+
attr_reader :DeviceID
|
15
|
+
|
16
|
+
# @return [String] Name of the player, set externally
|
17
|
+
attr_reader :name
|
18
|
+
|
19
|
+
# @return [String] status-string of the player. Should be "OK"
|
20
|
+
attr_reader :status
|
21
|
+
|
22
|
+
# @return [Integer] 0..255, shot ID of the player
|
23
|
+
attr_reader :id
|
24
|
+
# @return [Hash<Time>] Hash of the last few recorded shot times,
|
25
|
+
# used for hit arbitration
|
26
|
+
attr_accessor :hitIDTimetable
|
27
|
+
|
28
|
+
def initialize(deviceID, handler)
|
29
|
+
@handler = handler;
|
30
|
+
@mqtt = handler.mqtt;
|
31
|
+
|
32
|
+
@DeviceID = deviceID;
|
33
|
+
|
34
|
+
@status = "";
|
35
|
+
@name = "";
|
36
|
+
|
37
|
+
@hitIDTimetable = Hash.new(Time.new(0));
|
38
|
+
end
|
39
|
+
|
40
|
+
def _pub_to(key, data, retain: false)
|
41
|
+
@mqtt.publish_to("Lasertag/Players/#{@DeviceID}/#{key}", data, retain: retain);
|
42
|
+
end
|
43
|
+
private :_pub_to
|
44
|
+
|
45
|
+
# @private
|
46
|
+
def on_mqtt_data(data, topic)
|
47
|
+
case topic[1..topic.length].join("/")
|
48
|
+
when "Connection"
|
49
|
+
return if @status == data;
|
50
|
+
oldStatus = @status;
|
51
|
+
@status = data;
|
52
|
+
if(@status == "OK")
|
53
|
+
@handler.send_event(:playerConnected, self);
|
54
|
+
elsif(oldStatus == "OK")
|
55
|
+
@handler.send_event(:playerDisconnected, self);
|
56
|
+
end
|
57
|
+
when "CFG/Name"
|
58
|
+
@name = data;
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Boolean] Whether this player is connected
|
63
|
+
def connected?()
|
64
|
+
return @status == "OK"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Set the Shot ID of the player.
|
68
|
+
# @note Do not call this function yourself - the Handler must
|
69
|
+
# assign unique IDs to ensure proper game functionality!
|
70
|
+
# @private
|
71
|
+
def id=(n)
|
72
|
+
return if @id == n;
|
73
|
+
|
74
|
+
if(!n.nil?)
|
75
|
+
raise ArgumentError, "ID must be integer or nil!" unless n.is_a? Integer;
|
76
|
+
raise ArgumentError, "ID out of range (0<ID<256)" unless n < 256 and n > 0;
|
77
|
+
|
78
|
+
@id = n;
|
79
|
+
else
|
80
|
+
@id = nil;
|
81
|
+
end
|
82
|
+
|
83
|
+
_pub_to("CFG/ID", @id, retain: true);
|
84
|
+
end
|
85
|
+
|
86
|
+
# Trigger a clear of all topics
|
87
|
+
# @note Do not call this function yourself, except when deregistering a player!
|
88
|
+
# @private
|
89
|
+
def clear_all_topics()
|
90
|
+
self.id = nil;
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect()
|
94
|
+
iString = "#<Player:#{@deviceID}##{@id ? @id : "OFFLINE"}, Team=#{@team}";
|
95
|
+
iString += ", DEAD" if @dead
|
96
|
+
iString += ", Battery=#{@battery.round(2)}"
|
97
|
+
iString += ", Ping=#{@ping.ceil}ms>";
|
98
|
+
|
99
|
+
return iString;
|
100
|
+
end
|
101
|
+
alias to_s inspect
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
require_relative 'hardware_player.rb'
|
3
|
+
|
4
|
+
module LZRTag
|
5
|
+
module Player
|
6
|
+
# This class extends the pure hardware class, adding various hooks
|
7
|
+
# that can be used to send effects and other events to the weapon.
|
8
|
+
# These do not change the game itself, but instead just look and feel good!
|
9
|
+
class Effects < Hardware
|
10
|
+
# Heartbeat status of the player
|
11
|
+
# The "heartbeat" is a regular vibration pattern on the weapon,
|
12
|
+
# which can be used to indicate things like low life or other tense events.
|
13
|
+
# Set it to true/false.
|
14
|
+
attr_reader :heartbeat
|
15
|
+
|
16
|
+
# Mark a player in a given color
|
17
|
+
# This function can be used to "mark" a player, sending flashes of
|
18
|
+
# light across their LEDs in a given color. This can either be
|
19
|
+
# false, to turn the marking off, or 0..7 to set it to a team color.
|
20
|
+
# Alternatively, any RGB Number (0x?? ?? ??) can be used for arbitrary marking color
|
21
|
+
attr_reader :marked
|
22
|
+
|
23
|
+
def initialize(*data)
|
24
|
+
super(*data);
|
25
|
+
|
26
|
+
@marked = false;
|
27
|
+
end
|
28
|
+
|
29
|
+
# Vibrate the weapon for a number of seconds
|
30
|
+
# @param duration [Numeric] Number (in s) to vibrate for.
|
31
|
+
def vibrate(duration)
|
32
|
+
raise ArgumentError, "Vibration-duration out of range (between 0 and 65.536)" unless duration.is_a? Numeric and duration <= 65.536 and duration >= 0
|
33
|
+
_pub_to("CFG/Vibrate", duration);
|
34
|
+
end
|
35
|
+
|
36
|
+
def heartbeat=(data)
|
37
|
+
return if (@heartbeat == data);
|
38
|
+
|
39
|
+
@heartbeat = data;
|
40
|
+
_pub_to("CFG/Heartbeat", @heartbeat ? "1" : "0", retain: true);
|
41
|
+
end
|
42
|
+
|
43
|
+
def marked=(data)
|
44
|
+
return if (@marked == data);
|
45
|
+
|
46
|
+
@marked = data;
|
47
|
+
if data.is_a? Numeric
|
48
|
+
_pub_to("CFG/Marked", @marked.to_s, retain: true)
|
49
|
+
else
|
50
|
+
_pub_to("CFG/Marked", "0", retain: true)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Make the weapon play a given note.
|
55
|
+
# This function can make the set play a note of given frequency, volume
|
56
|
+
# and duration.
|
57
|
+
# @param duration [Numeric] Length in seconds
|
58
|
+
# @param frequency [Numeric] Frequency of the note
|
59
|
+
# @param volume [Numeric] Volume (0..1) of the note
|
60
|
+
def noise(duration: 0.5, frequency: 440, volume: 0.5)
|
61
|
+
return false unless duration.is_a? Numeric and frequency.is_a? Integer
|
62
|
+
_pub_to("Sound/Note", [frequency, volume*20000, duration*1000].pack("L3"))
|
63
|
+
end
|
64
|
+
|
65
|
+
# Play a given sound file.
|
66
|
+
# Depending on the weapon, various sounds are available to be played,
|
67
|
+
# such as:
|
68
|
+
# - "GAME START"
|
69
|
+
# - "HIT"
|
70
|
+
# - "OWN DEATH"
|
71
|
+
# - "KILL SCORE"
|
72
|
+
# etc.
|
73
|
+
# This list may be expanded in the future
|
74
|
+
def sound(sName)
|
75
|
+
_pub_to("Sound/File", sName);
|
76
|
+
end
|
77
|
+
|
78
|
+
# Make the weapon display a hit.
|
79
|
+
# When a weapon is hit, it will flash bright white and vibrate
|
80
|
+
# for a short moment. The length can be specified.
|
81
|
+
# @param hitLength [Numeric,nil] Length (in s) of the hit.
|
82
|
+
def hit(hitLength = nil)
|
83
|
+
_pub_to("CFG/Hit", hitLength || 0.7)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
def clear_all_topics()
|
88
|
+
super();
|
89
|
+
|
90
|
+
["CFG/Heartbeat", "CFG/Marked"].each do |t|
|
91
|
+
_pub_to(t, "", retain: true)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
|
2
|
+
require 'mqtt/mqtt_hash.rb'
|
3
|
+
|
4
|
+
require_relative 'base_player.rb'
|
5
|
+
|
6
|
+
module LZRTag
|
7
|
+
module Player
|
8
|
+
# Hardware-handling player class.
|
9
|
+
# This class extends the base player, adding more hardware-related
|
10
|
+
# functionality and interfaces, such as:
|
11
|
+
# - Team setting
|
12
|
+
# - Brightness setting
|
13
|
+
# - Note playing
|
14
|
+
# - Gyro and button readout
|
15
|
+
# - Ping and Battery reading
|
16
|
+
# etc.
|
17
|
+
class Hardware < Base
|
18
|
+
# The team (0..7) of this player.
|
19
|
+
# Setting it to a number will publish to /DeviceID/CFG/Team,
|
20
|
+
# changing the color of the weapon. Interpret it as binary string,
|
21
|
+
# with 1 being red, 2 green and 4 blue.
|
22
|
+
#
|
23
|
+
# Changes trigger the :playerTeamChanged event, with [player, oldTeam] data
|
24
|
+
attr_reader :team
|
25
|
+
# Current brightness of the weapon.
|
26
|
+
# @return [Symbol] Symbol describing the current brightness.
|
27
|
+
# Possible brightnesses are:
|
28
|
+
# - :idle (low, slow brightness, white with slight team hue)
|
29
|
+
# - :teamSelect (team-colored with rainbow overlay)
|
30
|
+
# - :dead (low brightness, team colored with white overlay)
|
31
|
+
# - :active (bright, flickering and in team color)
|
32
|
+
#
|
33
|
+
# A change will trigger the :playerBrightnessChanged event, with data [player, oldBrightness]
|
34
|
+
attr_reader :brightness
|
35
|
+
|
36
|
+
# Whether or not the player is currently dead.
|
37
|
+
# Set this to kill the player. Will trigger a :playerKilled or :playerRevived event,
|
38
|
+
# although kill_by is preferred to also specify which player killed.
|
39
|
+
attr_reader :dead
|
40
|
+
# Last time the death status changed (killed/revivied).
|
41
|
+
# Especially useful to determine when to revive a player
|
42
|
+
attr_reader :deathChangeTime
|
43
|
+
|
44
|
+
# Current ammo of the weapon.
|
45
|
+
# TODO one day this should be settable. Right now, it's just reading
|
46
|
+
attr_reader :ammo
|
47
|
+
# Maximum ammo the weapon can have with the currently equipped gun.
|
48
|
+
attr_reader :maxAmmo
|
49
|
+
# Number of the current gun.
|
50
|
+
# The application can freely choose which gun profile the set is using,
|
51
|
+
# which influences shot speeds, sounds, reloading, etc.
|
52
|
+
attr_reader :gunNo
|
53
|
+
|
54
|
+
# Returns the gyro pose of the set.
|
55
|
+
# This is either:
|
56
|
+
# - :active
|
57
|
+
# - :laidDown
|
58
|
+
# - :pointsUp
|
59
|
+
# - :pointsDown
|
60
|
+
# Changes are triggered by the set itself, if it has a gyro.
|
61
|
+
# The :poseChanged event is sent on change with [player, newPose] data
|
62
|
+
attr_reader :gyroPose
|
63
|
+
|
64
|
+
attr_reader :battery, :ping, :heap
|
65
|
+
|
66
|
+
def self.getBrightnessKeys()
|
67
|
+
return [:idle, :teamSelect, :dead, :active]
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(*data)
|
71
|
+
super(*data);
|
72
|
+
|
73
|
+
@team = 0;
|
74
|
+
@brightness = :idle;
|
75
|
+
|
76
|
+
@dead = false;
|
77
|
+
@deathChangeTime = Time.now();
|
78
|
+
|
79
|
+
@ammo = 0;
|
80
|
+
@maxAmmo = 0;
|
81
|
+
@gunNo = 0;
|
82
|
+
|
83
|
+
@gyroPose = :unknown;
|
84
|
+
|
85
|
+
@position = {x: 0, y: 0}
|
86
|
+
@zoneIDs = Hash.new();
|
87
|
+
|
88
|
+
@battery = 0; @ping = 0; @heap = 0;
|
89
|
+
|
90
|
+
@BrightnessMap = self.class.getBrightnessKeys();
|
91
|
+
|
92
|
+
# These values are configured for a DPS ~1, equal to all weapons
|
93
|
+
# Including reload timings and other penalties
|
94
|
+
@GunDamageMultipliers = [
|
95
|
+
0.9138,
|
96
|
+
1.85,
|
97
|
+
0.6166,
|
98
|
+
];
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
# This function processes incoming MQTT data.
|
103
|
+
# The user must not call this, since it is handled by the
|
104
|
+
# LZRTag base handler
|
105
|
+
def on_mqtt_data(data, topic)
|
106
|
+
case topic[1..topic.length].join("/")
|
107
|
+
when "HW/Ping"
|
108
|
+
if(data.size == 3*4)
|
109
|
+
parsedData = data.unpack("L<*");
|
110
|
+
|
111
|
+
@battery = parsedData[0].to_f/1000;
|
112
|
+
@ping = parsedData[2].to_f;
|
113
|
+
end
|
114
|
+
when "CFG/Dead"
|
115
|
+
dead = (data == "1")
|
116
|
+
return if @dead == dead;
|
117
|
+
@dead = dead;
|
118
|
+
|
119
|
+
@deathChangeTime = Time.now();
|
120
|
+
|
121
|
+
@handler.send_event(@dead ? :playerKilled : :playerRevived, self);
|
122
|
+
when "Stats/Ammo"
|
123
|
+
return if(data.size != 8)
|
124
|
+
|
125
|
+
outData = data.unpack("L<*");
|
126
|
+
@ammo = outData[0];
|
127
|
+
@maxAmmo = outData[1];
|
128
|
+
when "Position"
|
129
|
+
begin
|
130
|
+
@position = JSON.parse(data, symbolize_names: true);
|
131
|
+
rescue
|
132
|
+
end
|
133
|
+
when "HW/NSwitch"
|
134
|
+
@handler.send_event(:navSwitchPressed, self, data.to_i)
|
135
|
+
when "HW/Gyro"
|
136
|
+
@gyroPose = data.to_sym
|
137
|
+
@handler.send_event(:poseChanged, self, @gyroPose);
|
138
|
+
when "ZoneUpdate"
|
139
|
+
begin
|
140
|
+
data = JSON.parse(data, symbolize_names: true);
|
141
|
+
rescue JSON::ParserError
|
142
|
+
return;
|
143
|
+
end
|
144
|
+
|
145
|
+
@zoneIDs = data[:data];
|
146
|
+
if(data[:entered])
|
147
|
+
@handler.send_event(:playerEnteredZone, self, data[:entered])
|
148
|
+
end
|
149
|
+
if(data[:exited])
|
150
|
+
@handler.send_event(:playerExitedZone, self, data[:exited])
|
151
|
+
end
|
152
|
+
else
|
153
|
+
super(data, topic);
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def team=(n)
|
158
|
+
n = n.to_i;
|
159
|
+
raise ArgumentError, "Team out of range (must be between 0 and 7)" unless n <= 7 and n >= 0;
|
160
|
+
|
161
|
+
return if @team == n;
|
162
|
+
oldT = @team;
|
163
|
+
@team = n;
|
164
|
+
|
165
|
+
_pub_to "CFG/Team", @team, retain: true;
|
166
|
+
@handler.send_event :playerTeamChanged, self, oldT;
|
167
|
+
|
168
|
+
@team;
|
169
|
+
end
|
170
|
+
def brightness=(n)
|
171
|
+
raise ArgumentError, "Brightness must be a valid symbol!" unless @BrightnessMap.include? n;
|
172
|
+
|
173
|
+
return if @brightness == n;
|
174
|
+
oldB = @brightness;
|
175
|
+
@brightness = n;
|
176
|
+
|
177
|
+
n = @BrightnessMap.find_index(n)
|
178
|
+
|
179
|
+
_pub_to "CFG/Brightness", n, retain: true;
|
180
|
+
@handler.send_event :playerBrightnessChanged, self, oldB;
|
181
|
+
|
182
|
+
@brightness;
|
183
|
+
end
|
184
|
+
|
185
|
+
def _set_dead(d, player = nil)
|
186
|
+
dead = (d ? true : false);
|
187
|
+
return if @dead == dead;
|
188
|
+
@dead = dead;
|
189
|
+
|
190
|
+
@deathChangeTime = Time.now();
|
191
|
+
|
192
|
+
_pub_to "CFG/Dead", @dead ? "1" : "0", retain: true;
|
193
|
+
@handler.send_event(@dead ? :playerKilled : :playerRevived, self, player);
|
194
|
+
end
|
195
|
+
def dead=(d)
|
196
|
+
_set_dead(d);
|
197
|
+
end
|
198
|
+
def kill_by(player)
|
199
|
+
return if @dead;
|
200
|
+
_set_dead(true, player);
|
201
|
+
end
|
202
|
+
def revive_by(player)
|
203
|
+
return unless @dead
|
204
|
+
_set_dead(false, player)
|
205
|
+
end
|
206
|
+
|
207
|
+
def ammo=(n)
|
208
|
+
unless (n.is_a?(Integer) and (n >= 0))
|
209
|
+
raise ArgumentError, "Ammo amount needs to be a positive number!"
|
210
|
+
end
|
211
|
+
|
212
|
+
@ammo = n;
|
213
|
+
|
214
|
+
_pub_to("Stats/Ammo/Set", n);
|
215
|
+
end
|
216
|
+
|
217
|
+
def gunNo=(n)
|
218
|
+
unless (n.is_a?(Integer) and (n >= 0))
|
219
|
+
raise ArgumentError, "Gun ID needs to be a positive integer!"
|
220
|
+
end
|
221
|
+
|
222
|
+
return if(@gunNo == n)
|
223
|
+
|
224
|
+
oldGun = @gunNo;
|
225
|
+
@gunNo = n;
|
226
|
+
@handler.send_event(:playerGunChanged, self, n, oldGun);
|
227
|
+
|
228
|
+
_pub_to("CFG/GunNo", n, retain: true);
|
229
|
+
end
|
230
|
+
|
231
|
+
# Return the averaged damage the player's gun should do.
|
232
|
+
# This function is very useful to calculate the damage a player did
|
233
|
+
# per shot. The returned number tries to average damage to "1 DPS" for
|
234
|
+
# all weapons regardless of speed etc., which the application can
|
235
|
+
# multiply for a given total damage, creating a more balanced game.
|
236
|
+
def gunDamage(number = nil)
|
237
|
+
number ||= @gunNo
|
238
|
+
|
239
|
+
return @GunDamageMultipliers[number-1] || 1;
|
240
|
+
end
|
241
|
+
|
242
|
+
def clear_all_topics()
|
243
|
+
super();
|
244
|
+
|
245
|
+
[ "CFG/Dead", "CFG/GunNo", "CFG/Brightness", "CFG/Team"].each do |t|
|
246
|
+
_pub_to(t, "", retain: true);
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|