lzrtag-base 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|