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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e1aba4b3577f72ee4c4aca45caca65e157cfa522
|
4
|
+
data.tar.gz: 9582f9f9a6c478c97de4c837ad6eadfb2c44c451
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a2a6bcaeefeddece09a26ee91e9dd608aa5d8450dc4ff943b23d01ad995f07533cdcc278336b37e92873de1f9cc4ce468e66df5806225e01c8ce23acb18eb313
|
7
|
+
data.tar.gz: f5dea5415de79a54ab9c4d5bd7e81e774daae5498d4b6005c21a68c5d9ddba6f9ca7540b1159f78cffa786507d19d25f52a1d3153320e70c64234db833616f64
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
# LZRTag base library gem
|
3
|
+
|
4
|
+
This is the full documentation of the LZRTag ruby gem library.
|
5
|
+
It includes examples for the different elements of this code, and even a basic
|
6
|
+
deathmatch game to show off the mechanics more interactively.
|
7
|
+
|
8
|
+
## Prerequisites
|
9
|
+
Let's first start off by installing and setting up everything that's needed
|
10
|
+
to run a LZRTag game. We'll assume you have at least one, preferrably as many as possible,
|
11
|
+
LZRTag hardware sets with the latest firmware - consult the [HackADay page](bit.ly/XASLZR) as well as the [GitHub Wiki page](https://github.com/XasWorks/LZRTag/wiki/Assembly) on soldering them up.
|
12
|
+
We will also need a WiFi network and a MQTT Server, either local or hosted, like
|
13
|
+
Flespi.io, with the LZRTag hardware set up to connect to the server. Successful
|
14
|
+
connection is indicated by a smooth, very slow waver of the blue connection LED
|
15
|
+
on the sets. If you want to double-check connection you can subscribe to the
|
16
|
+
Lasertag/# topic on your MQTT server and check if the sets are pinging properly.
|
17
|
+
|
18
|
+
Once we are sure that the sets are connected properly run `gem install lzrtag-base`.
|
19
|
+
The latest ruby is recommended for performance reasons, however, this code should work
|
20
|
+
with anything above 2.3.
|
21
|
+
|
22
|
+
## Testing on IRB
|
23
|
+
As a first experience, I'd recommend playing with the interface class on the interactive
|
24
|
+
ruby console. It's a great way to see the immediate effects of various commands
|
25
|
+
on the set, and is kinda fun, too.
|
26
|
+
|
27
|
+
Let's first start up the console and require the library.
|
28
|
+
```IRB
|
29
|
+
irb(main):001:0> require 'lzrtag.rb'
|
30
|
+
=> true
|
31
|
+
irb(main):002:0>
|
32
|
+
```
|
33
|
+
This will load all necessary files, and will also provide a few helper functions
|
34
|
+
to access the correct classes etc.
|
35
|
+
Next, let's establish an MQTT connection and start the handler.
|
36
|
+
|
37
|
+
```IRB
|
38
|
+
irb(main):002:0> mqtt = MQTT::SubHandler.new('localhost'); # Or your address
|
39
|
+
MQTT: localhost trying reconnect...
|
40
|
+
MQTT: localhost connected!
|
41
|
+
irb(main):003:0> handler = LZRTag.Handler.new(mqtt);
|
42
|
+
I LZR::Handler init finished
|
43
|
+
```
|
44
|
+
|
45
|
+
Awesome! The handler should now be connected to your server, and should have registered
|
46
|
+
new players. If you look at your MQTT Server, for example with MQTT-Spy, you
|
47
|
+
should see that any connected players will now have an ID.
|
48
|
+
|
49
|
+
Let's see what players the system has already registered:
|
50
|
+
|
51
|
+
```irb
|
52
|
+
irb(main):004:0> handler.each do |player| puts player; end;
|
53
|
+
#<Player:DEVICE_ID#OFFLINE, Team=0, Battery=4.19, Ping=218ms>
|
54
|
+
```
|
55
|
+
|
56
|
+
Players have a nice inspect function so they don't clutter the console too much when printed. Neat, huh?
|
57
|
+
|
58
|
+
Alright, we need to fetch one single player to play with. If you have a device ID
|
59
|
+
(their MAC, used on the MQTT network to identify them) you can use that, otherwise
|
60
|
+
use the ShotID (number, 0..255). Let's do that now - and while we're at it,
|
61
|
+
let's identify the player:
|
62
|
+
|
63
|
+
```irb
|
64
|
+
irb(main):005:0> player = handler["BC.DD.C2.D0.63.F8"]
|
65
|
+
=> #<Player:#OFFLINE, Team=0, Battery=4.19, Heap=0, Ping=218ms>
|
66
|
+
irb(main):006:0> player.hit
|
67
|
+
```
|
68
|
+
|
69
|
+
You should have seen and heard one of the sets flashing up - that's the one we
|
70
|
+
just retrieved. We can also colour it green and make it a little brighter!
|
71
|
+
|
72
|
+
```irb
|
73
|
+
irb(main):007:0> player.team = 2
|
74
|
+
=> 2
|
75
|
+
irb(main):08:0> player.brightness = :active
|
76
|
+
=> :active
|
77
|
+
```
|
78
|
+
|
79
|
+
Alright, awesome - let's not go too deep into what you can do with the player.
|
80
|
+
That's what the Ruby documentation is for, after all.
|
81
|
+
|
82
|
+
## And now?
|
83
|
+
Well, more complex behaviours can be added with so-called hooks. These are extensions
|
84
|
+
that perform automated tasks based on different events in the game, and use the
|
85
|
+
internal "event system".
|
86
|
+
There is a list of currently used events available, as well as documentation on
|
87
|
+
the hooks class. You can make your own, or use the standard hooks provided in this
|
88
|
+
library - it's up to you!
|
89
|
+
|
90
|
+
Since a game is way more than just behaviours though, there are also "Games".
|
91
|
+
Each game is a collection of hooks, which provide the behaviour of the game. However,
|
92
|
+
they also include a description of different phases - stuff like "initializing",
|
93
|
+
"playing", "selecting teams", etc.
|
94
|
+
The application can seamlessly switch between phases of a game, with each phase
|
95
|
+
defining which hooks to enable or disable and what to do every game tick.
|
96
|
+
|
97
|
+
It's best to look right into the documentation on those classes, since demonstrating
|
98
|
+
them bit by bit isn't really possible.
|
@@ -0,0 +1,117 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
This file will document all known internal Lasertag events and their purpose.
|
4
|
+
Users may add their own events via the standard system by simply sending a new event -
|
5
|
+
however, only the following event symbols and payloads will be used by the provided software.
|
6
|
+
|
7
|
+
|
8
|
+
## Game related events
|
9
|
+
|
10
|
+
###### :gameTick
|
11
|
+
Triggered in regular intervals, default 0.1s
|
12
|
+
Payload is [deltaTime since last tick].
|
13
|
+
It is only triggered when a game is present.
|
14
|
+
|
15
|
+
*Game class alternative:* Describe per-tick code using the
|
16
|
+
"phase" DSL. It allows easy and clean specification of tick code.
|
17
|
+
|
18
|
+
###### :gameStarting
|
19
|
+
Triggered when a new game is selected, either in Ruby or via MQTT.
|
20
|
+
Payload is [newGame]
|
21
|
+
|
22
|
+
###### :gamePhaseEnds
|
23
|
+
Called immediately before the phase of a game is changed. Can be used to de-init some settings, or to finalize for example a player
|
24
|
+
team selection.
|
25
|
+
The payload is [oldPhase, nextPhase]
|
26
|
+
|
27
|
+
*Game class alternative:* Descibe per-tick code using the "phase_end" DSL.
|
28
|
+
|
29
|
+
###### :gamePhaseStarts
|
30
|
+
Called immediately after the game's phase was changed to a new phase. Can, and should, be used to prepare a game's setting.
|
31
|
+
The payload is [newPhase, oldPhase]
|
32
|
+
|
33
|
+
*Game class alternative:* Descibe per-tick code using the "phase_prep" DSL.
|
34
|
+
|
35
|
+
###### :playerEnteredGame
|
36
|
+
Called whenever a player is added to the list of game players.
|
37
|
+
Payload is [thePlayer]
|
38
|
+
|
39
|
+
###### :playerLeftGame
|
40
|
+
Called whenever a player was removed from the list of game players.
|
41
|
+
Payload is [thePlayer]
|
42
|
+
|
43
|
+
## Player config options
|
44
|
+
The following options will be triggered whenever some config of the player changes, and are mainly useful to trigger events or
|
45
|
+
keep track of the players etc.
|
46
|
+
|
47
|
+
###### :playerRegistered
|
48
|
+
Triggered when a brand new player is found on the MQTT network.
|
49
|
+
This will effectively only happen at the start of the server, or when a new player is turned on.
|
50
|
+
Payload is [thePlayer]
|
51
|
+
|
52
|
+
###### :playerConnected
|
53
|
+
Triggered whenever a player reconnects to the server, either when restarting the server or mid-game after a disconnect.
|
54
|
+
Payload is [thePlayer]
|
55
|
+
|
56
|
+
###### :playerDisconnected
|
57
|
+
Triggered whenever a player looses connection to the MQTT server (detected via the LWT topic).
|
58
|
+
Payload is [thePlayer]
|
59
|
+
|
60
|
+
## Hardware player signals
|
61
|
+
These signals are sent in response to various player actions, as well as most of Hardware Player's
|
62
|
+
configuration changes.
|
63
|
+
|
64
|
+
###### :navSwitchPressed
|
65
|
+
Triggered when the player presses a navigation switch.
|
66
|
+
Payload is [thePlayer, direction (0..2)]
|
67
|
+
|
68
|
+
###### :poseChanged
|
69
|
+
Triggered when the player changes the pose of the weapon. Possible poses are listed
|
70
|
+
under the Lasertag Hardware Player's "gyroPose" attribute
|
71
|
+
Payload is [thePlayer, poseSymbol]
|
72
|
+
|
73
|
+
###### :playerTeamChanged
|
74
|
+
Triggered when the team of a player is changed.
|
75
|
+
Payload is [thePlayer, newTeam (0..7), oldTeam (0..7)]
|
76
|
+
|
77
|
+
###### :playerBrightnessChanged
|
78
|
+
Triggered when the brightness of a player is changed. For a list of brightness
|
79
|
+
symbols, see Hardware Player's "brightness" attribute.
|
80
|
+
Payload is [thePlayer, newBrightness, oldBrightness]
|
81
|
+
|
82
|
+
###### :playerGunChanged
|
83
|
+
Triggered when the gun number of the player is changed.
|
84
|
+
Payload is [thePlayer, newGun, oldGun]
|
85
|
+
|
86
|
+
*TODO:* The gun number should be replaced by an ID string, when the
|
87
|
+
gun configuration is moved to a Filesystem-based one (possibly by loading from JSON)
|
88
|
+
|
89
|
+
## Player life signals
|
90
|
+
These signals are sent whenever the player's HP count or life status is changed,
|
91
|
+
and let the application handle things like scoring damage and kills, and triggering
|
92
|
+
things like the heartbeat.
|
93
|
+
|
94
|
+
###### :playerRegenerated
|
95
|
+
Triggered whenever the player gains a bit of health, this can be by self-healing or
|
96
|
+
by a medic or similar providing health.
|
97
|
+
Payload is [thePlayer, deltaLife, sourcePlayer | nil]
|
98
|
+
|
99
|
+
###### :playerFullyRegenerated
|
100
|
+
Triggered similarly to :playerRegenerated, but only whenever the player has fully
|
101
|
+
regenerated and has reached maxLife again.
|
102
|
+
Payload is [thePlayer, sourcePlayer | nil]
|
103
|
+
|
104
|
+
###### :playerHurt
|
105
|
+
Triggered whenever a player is hurt after a hit arbitration
|
106
|
+
and damage calculation, or by other game events.
|
107
|
+
Payload is [thePlayer, deltaLife]
|
108
|
+
|
109
|
+
###### :playerRevived
|
110
|
+
Triggered whenever a player was revivied, either by a timeout
|
111
|
+
or by regenerating enough health, or by another person.
|
112
|
+
Payload is [thePlayer, sourcePlayer]
|
113
|
+
|
114
|
+
###### :playerKilled
|
115
|
+
Triggered whenever a player was killed, often by another player,
|
116
|
+
but can be by system damage (killzones etc.)
|
117
|
+
Payload is [thePlayer, sourcePlayer | nil]
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# MQTT Branch setup description
|
2
|
+
|
3
|
+
## Weapon Branches
|
4
|
+
The weapons are the main unit of the Lasertag system. As such, they use a specific
|
5
|
+
branch setup to minimize traffic to them, while making code maintaining easier.
|
6
|
+
|
7
|
+
The basic branch setup is:
|
8
|
+
> Lasertag/Players/**ID**/**Keys...**
|
9
|
+
|
10
|
+
- **ID** is the MAC of the Player, allowing unique identification of
|
11
|
+
each set out of the box.
|
12
|
+
- **Key** is a one- or two-layer key of the attribute to change.
|
13
|
+
Attributes are grouped, making it easier to subscribe to one group of attributes
|
14
|
+
without having to worry about the others.
|
15
|
+
|
16
|
+
In the following, the different groups of keys will be outlined, followed by
|
17
|
+
a description of each attribute per group.
|
18
|
+
|
19
|
+
### Non-Grouped attributes
|
20
|
+
|
21
|
+
|Keys|Data|Descrition|Retained|
|
22
|
+
|--|--|--|--|
|
23
|
+
|Connection|String|LWT Topic of the sets. "OK" if connected.|Yes|
|
24
|
+
|
25
|
+
### Config attributes
|
26
|
+
|
27
|
+
Any attributes under this category can be found under "/CFG/**KEY**".
|
28
|
+
They are slow-changing configuration parameters for the weapons.
|
29
|
+
|
30
|
+
|Key|Data|Descrition|Retained|
|
31
|
+
|--|--|--|--|
|
32
|
+
|Team|STR Number|Team number, 0 to 7|Yes|
|
33
|
+
|Brightness|STR Number|Brightness preset number. In Ruby, handled by symbols|Yes|
|
34
|
+
|Name|String|User-set name of the weapon|Yes|
|
35
|
+
|Dead|"1" or "0"|Sets the alive-ness of the gun.|Yes|
|
36
|
+
|Dead/Timed|STR Number|Number, in seconds, to kill the weapon for. Auto-revives|No|
|
37
|
+
|GunNo|STR Number|Gun preset number. 0 or "" disable shooting|Yes|
|
38
|
+
|Marked|STR Number|Color to mark the set with. 1-7 use team colors, custom colors can be used|Yes|
|
39
|
+
|Heartbeat|"1" or "0"|Enables/Disables heartbeat of the weapon|Yes|
|
40
|
+
|Hit|STR Number|Number, in seconds, to make the gun show that it's hit|No|
|
41
|
+
|Vibrate|STR Number|Number, in seconds, for which to make the gun vibrate|No|
|
42
|
+
|
43
|
+
### Sound attributes
|
44
|
+
|
45
|
+
Any attributes under this category are found under "/Sound/**KEY**"
|
46
|
+
They all relate to noises the gun can make.
|
47
|
+
|
48
|
+
|Key|Data|Descrition|Retained|
|
49
|
+
|--|--|--|--|
|
50
|
+
|Note|uint32_t[3]|Raw note play data. Frequency(Hz), Volume (0..2^16), Duration(ms)|No|
|
51
|
+
|File|String|Name of a raw audio file to play.|No|
|
52
|
+
|
53
|
+
### Hardware attributes
|
54
|
+
|
55
|
+
Any attributes under this category are found under "/HW/**KEY**"
|
56
|
+
They all relate to the hardware of the system, such as battery etc.
|
57
|
+
|
58
|
+
|Key|Data|Descrition|Retained|
|
59
|
+
|--|--|--|--|
|
60
|
+
|Ping|uint32_t[3]|Battery, free-heap and ping data|No|
|
61
|
+
|NSwitch|STR Number|Navigation switch press info (0-3)|No|
|
62
|
+
|Gyro|String|Clear string of the pose the gyroscope is reporting|Yes|
|
63
|
+
|
64
|
+
### Statistics Attributes
|
65
|
+
|
66
|
+
Any attributes under this category are found under "/Stats/**KEY**"
|
67
|
+
They all relate to various game statistics that do not directly influence the hardware, but are still relevant to the game and how it plays out.
|
68
|
+
|
69
|
+
|
70
|
+
|Key|Data|Descrition|Retained|
|
71
|
+
|--|--|--|--|
|
72
|
+
|HP|STR Number|Float number, 0 to 100 (or more with shields)|Yes|
|
73
|
+
|Ammo|uint32_t[2]|Current and Max ammo of the weapon|Yes|
|
74
|
+
|Ammo/Set|STR Number|Give the player some ammo (*WIP*)|No|
|
data/lib/lzrtag.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module LZRTag
|
3
|
+
def self.Handler()
|
4
|
+
return LZRTag::Handler::Game;
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative 'lzrtag/handler/game_handler.rb'
|
9
|
+
require_relative 'lzrtag/hooks/standard_hooks.rb'
|
10
|
+
|
11
|
+
require_relative 'lzrtag/game/base_game.rb'
|
12
|
+
|
13
|
+
require_relative 'lzrtag/map/map_set.rb'
|
@@ -0,0 +1,220 @@
|
|
1
|
+
|
2
|
+
require_relative '../hooks/base_hook.rb'
|
3
|
+
|
4
|
+
# @author Xasin
|
5
|
+
module LZRTag
|
6
|
+
module Game
|
7
|
+
# The base game class.
|
8
|
+
# It implements a DSL that allows users to easily
|
9
|
+
# define their own games, and hooks in with the
|
10
|
+
# game event system
|
11
|
+
# @example
|
12
|
+
# class OwnGame < LZRTag::Game::Base
|
13
|
+
# def initialize(handler)
|
14
|
+
# super(handler);
|
15
|
+
# @tickTime = 1;
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# hook :dmgHook, LZRTag::Hook::Damager
|
19
|
+
#
|
20
|
+
# phase :running do |deltaTick|
|
21
|
+
# puts "I tick!"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# on :playerRegistered do |newPlayer|
|
25
|
+
# puts "Hello new player!";
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# handler.start_game(OwnGame)
|
30
|
+
# # Alternatively, register the game and let it be activated via MQTT
|
31
|
+
# handler.register_game("My Game", OwnGame);
|
32
|
+
class Base < Hook::Base
|
33
|
+
# Returns a list of all currently instantiated hooks
|
34
|
+
attr_reader :hookList
|
35
|
+
# Returns the current per-tick target time. Can, and should, be set during
|
36
|
+
# constructor to control the granularity of the game
|
37
|
+
attr_reader :tickTime
|
38
|
+
|
39
|
+
# Returns a list of the known phases of this game
|
40
|
+
attr_reader :phases
|
41
|
+
|
42
|
+
# @private
|
43
|
+
# This function is meant for the DSL,
|
44
|
+
# to allow adding to the class itself
|
45
|
+
def self.get_phase_map()
|
46
|
+
@globalPhaseMap ||= Hash.new();
|
47
|
+
return @globalPhaseMap;
|
48
|
+
end
|
49
|
+
# @private
|
50
|
+
# @see get_phase_map()
|
51
|
+
def self.get_phase_prep_map()
|
52
|
+
@globalPhasePrepMap ||= Hash.new();
|
53
|
+
return @globalPhasePrepMap;
|
54
|
+
end
|
55
|
+
# @private
|
56
|
+
# @see get_phase_map()
|
57
|
+
def self.get_phase_end_map()
|
58
|
+
@globalPhaseEndMap ||= Hash.new();
|
59
|
+
return @globalPhaseEndMap;
|
60
|
+
end
|
61
|
+
# @private
|
62
|
+
# @see get_phase_map()
|
63
|
+
def self.get_hooks()
|
64
|
+
@globalHookList ||= Hash.new();
|
65
|
+
return @globalHookList;
|
66
|
+
end
|
67
|
+
|
68
|
+
# This function returns a list of possible phases
|
69
|
+
# and their tick callbacks
|
70
|
+
def get_phase_map()
|
71
|
+
return @phaseMap
|
72
|
+
end
|
73
|
+
|
74
|
+
# Initializes a generic game handler.
|
75
|
+
# This function is usually not called by the user,
|
76
|
+
# but instead by the LZRTag::Handler::Game when
|
77
|
+
# starting a game.
|
78
|
+
# @param handler [LZRTag::Handler::Base] any valid
|
79
|
+
# LZRTag handler that the game shall follow
|
80
|
+
#
|
81
|
+
def initialize(handler)
|
82
|
+
super(handler)
|
83
|
+
|
84
|
+
@hookList = Array.new();
|
85
|
+
self.class.get_hooks().each do |hookID, hookData|
|
86
|
+
@hookList << hookData[0].new(@handler, **hookData[1])
|
87
|
+
end
|
88
|
+
|
89
|
+
@tickTime = 0.1;
|
90
|
+
|
91
|
+
@phaseMap = self.class.get_phase_map();
|
92
|
+
@phasePrepMap = self.class.get_phase_prep_map();
|
93
|
+
@phaseEndMap = self.class.get_phase_end_map();
|
94
|
+
|
95
|
+
@phases = [@phaseMap.keys, @phasePrepMap.keys].flatten.uniq
|
96
|
+
|
97
|
+
@phaseTime = 0;
|
98
|
+
@phaseLastTime = 0;
|
99
|
+
end
|
100
|
+
|
101
|
+
# @!group DSL functions
|
102
|
+
|
103
|
+
# DSL function to add a Hook type to this game.
|
104
|
+
# Any hook type added by this function will be instantiated when the game
|
105
|
+
# itself is instantiated, and will be linked in with the internal game
|
106
|
+
# signals.
|
107
|
+
# @param hookID [Symbol] The ID of the hook, used for later referencing
|
108
|
+
# @param hookType [Hook::Base] The class of the hook to instantiate
|
109
|
+
# @param hookOptions [Hash] A hash of options to pass to the constructor of the hook
|
110
|
+
def self.hook(hookID, hookType, hookOptions = {})
|
111
|
+
raise ArgumentError, "Hook ID needs to be a symbol!" unless hookID.is_a? Symbol
|
112
|
+
unless hookType.is_a? Class and hookType < LZRTag::Hook::Base
|
113
|
+
raise ArgumentError, "Hook needs to be a LZR::Hook!"
|
114
|
+
end
|
115
|
+
raise ArgumentError, "Hook options need to be a hash" unless hookOptions.is_a? Hash
|
116
|
+
get_hooks()[hookID] << [hookType, hookOptions];
|
117
|
+
end
|
118
|
+
|
119
|
+
# DSL function to provide a phase tick code to this game
|
120
|
+
# The block provided to this function will be executed every game tick,
|
121
|
+
# with the delta-time since last tick as parameter.
|
122
|
+
# @param phaseName [Symbol] The name of the phase during which to execute this code
|
123
|
+
# @yield [deltaTime] Calls this block every game-tick during the specified phase
|
124
|
+
def self.phase(phaseName, &block)
|
125
|
+
raise ArgumentError, "Block needs to be given!" unless block_given?
|
126
|
+
|
127
|
+
phaseName = [phaseName].flatten
|
128
|
+
phaseName.each do |evt|
|
129
|
+
unless (evt.is_a? Symbol)
|
130
|
+
raise ArgumentError, "Phase needs to be a symbol or array of symbols!"
|
131
|
+
end
|
132
|
+
self.get_phase_map()[evt] = block;
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# DSL function to provide a callback immediately after switching to a new phase.
|
137
|
+
# The provided block will be called only once, right after a phase switch happened.
|
138
|
+
# As such, it can be used to prepare game timer settings, player classes, etc.
|
139
|
+
# @param phaseName [Symbol] The name of the phase before which to execute
|
140
|
+
# @yield [oldPhase] Calls the block right after a switch, with the old phase as parameter
|
141
|
+
def self.phase_prep(phaseName, &block)
|
142
|
+
raise ArgumentError, "Block needs to be given!" unless block_given?
|
143
|
+
|
144
|
+
phaseName = [phaseName].flatten
|
145
|
+
phaseName.each do |evt|
|
146
|
+
unless (evt.is_a? Symbol)
|
147
|
+
raise ArgumentError, "Phase needs to be a symbol or array of symbols!"
|
148
|
+
end
|
149
|
+
self.get_phase_prep_map()[evt] ||= Array.new()
|
150
|
+
self.get_phase_prep_map()[evt] << block;
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# DSL function to provide a callback immediately before switching to a new phase.
|
155
|
+
# The provided block is guaranteed to execute before any phase and phase_prep blocks,
|
156
|
+
# giving the user an option to reset and clean up after themselves.
|
157
|
+
# @param phaseName [Symbol] Name of the phase after which to call this block
|
158
|
+
# @yield [newPhase] Calls the block right before the switch, with the upcoming phase as parameter
|
159
|
+
def self.phase_end(phaseName, &block)
|
160
|
+
raise ArgumentError, "Block needs to be given!" unless block_given?
|
161
|
+
|
162
|
+
phaseName = [phaseName].flatten
|
163
|
+
phaseName.each do |evt|
|
164
|
+
unless (evt.is_a? Symbol)
|
165
|
+
raise ArgumentError, "Phase needs to be a symbol or array of symbols!"
|
166
|
+
end
|
167
|
+
self.get_phase_end_map()[evt] ||= Array.new()
|
168
|
+
self.get_phase_end_map()[evt] << block;
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# @!endgroup
|
173
|
+
|
174
|
+
# @private
|
175
|
+
def consume_event(evt, data)
|
176
|
+
super(evt, data);
|
177
|
+
|
178
|
+
case evt
|
179
|
+
when :gameTick
|
180
|
+
handle_game_tick(*data);
|
181
|
+
when :gamePhaseStarts
|
182
|
+
handle_phase_change();
|
183
|
+
when :gamePhaseEnds
|
184
|
+
if @phaseEndMap[data[0]]
|
185
|
+
@phaseEndMap[data[0]].each do |cb|
|
186
|
+
instance_exec(&cb);
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
@hookList.each do |hook|
|
192
|
+
hook.consume_event(evt, data);
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# @private
|
197
|
+
def handle_phase_change
|
198
|
+
@phaseTime = 0;
|
199
|
+
|
200
|
+
return unless @phasePrepMap[@handler.gamePhase]
|
201
|
+
@phasePrepMap[@handler.gamePhase].each do |cb|
|
202
|
+
instance_exec(&cb);
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# @private
|
207
|
+
def handle_game_tick(dT)
|
208
|
+
phase = @handler.gamePhase
|
209
|
+
return unless @phaseMap[phase];
|
210
|
+
return if phase == :idle;
|
211
|
+
|
212
|
+
@phaseLastTime = @phaseTime;
|
213
|
+
@phaseTime += dT;
|
214
|
+
@handler.mqtt.publish_to "Lasertag/Game/Timer", @phaseTime
|
215
|
+
|
216
|
+
instance_exec(dT, &@phaseMap[phase]);
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|