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
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
|