hlockey 5 → 6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/data/election.json +20 -0
- data/data/information.json +5 -0
- data/data/league.json +1984 -0
- data/data/links.json +5 -0
- data/data/previous_election_results.json +98 -0
- data/lib/hlockey/actions.rb +22 -0
- data/lib/hlockey/data.rb +6 -5
- data/lib/hlockey/game/fight.rb +28 -17
- data/lib/hlockey/game/weather/audacity.rb +1 -1
- data/lib/hlockey/game/weather/chicken.rb +1 -1
- data/lib/hlockey/game/weather/sunset.rb +1 -1
- data/lib/hlockey/game/weather/waves.rb +11 -5
- data/lib/hlockey/game/weather/weatherable.rb +8 -8
- data/lib/hlockey/game.rb +58 -33
- data/lib/hlockey/league.rb +60 -63
- data/lib/hlockey/message.rb +54 -56
- data/lib/hlockey/mod/fencebuilder.rb +16 -0
- data/lib/hlockey/mod/fencedestroyer.rb +16 -0
- data/lib/hlockey/mod/handholding.rb +55 -0
- data/lib/hlockey/mod/immaterial.rb +26 -0
- data/lib/hlockey/mod/locked.rb +22 -0
- data/lib/hlockey/mod/moddable.rb +49 -0
- data/lib/hlockey/mod/nonconfrontational.rb +17 -0
- data/lib/hlockey/mod/powernapper.rb +46 -0
- data/lib/hlockey/mod/punchy.rb +17 -0
- data/lib/hlockey/mod.rb +25 -0
- data/lib/hlockey/selfdescribable.rb +9 -0
- data/lib/hlockey/team/player.rb +37 -58
- data/lib/hlockey/team/stadium.rb +10 -7
- data/lib/hlockey/team.rb +56 -41
- data/lib/hlockey/version.rb +1 -1
- metadata +27 -17
- data/data/election.yaml +0 -21
- data/data/external/names.txt +0 -19948
- data/data/information.yaml +0 -24
- data/data/league.yaml +0 -1694
- data/data/links.yaml +0 -3
- data/data/previous_election_results.yaml +0 -65
- data/lib/hlockey/game/actions.rb +0 -24
data/lib/hlockey/league.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require("hlockey/constants")
|
2
2
|
require("hlockey/data")
|
3
3
|
require("hlockey/game")
|
4
|
+
require("hlockey/team")
|
4
5
|
require("hlockey/utils")
|
5
6
|
require("hlockey/version")
|
6
7
|
|
@@ -31,32 +32,69 @@ module Hlockey
|
|
31
32
|
# @return [Array<Team>]
|
32
33
|
attr_reader(:teams, :playoff_teams)
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
# @param start_time [Array<Integer>, nil] args to Time.utc
|
36
|
+
# @param divisions [Hash<Symbol => Array<Hash>, nil]
|
37
|
+
def initialize(start_time: nil, divisions: nil)
|
38
|
+
if start_time.nil? || divisions.nil?
|
39
|
+
league_hash = Data.league
|
40
|
+
start_time ||= league_hash[:start_time]
|
41
|
+
divisions ||= league_hash[:divisions]
|
42
|
+
end
|
43
|
+
|
44
|
+
@start_time = Time.utc(*start_time)
|
36
45
|
@start_time.localtime
|
46
|
+
|
47
|
+
@divisions = divisions.transform_values { |teams| teams.map { Team.new(**_1) } }
|
48
|
+
|
37
49
|
@day = 0
|
50
|
+
|
38
51
|
@games = []
|
39
52
|
@games_in_progress = []
|
40
53
|
@alerts = []
|
54
|
+
|
41
55
|
@champion_team = nil
|
56
|
+
|
42
57
|
@last_update_time = @start_time
|
43
58
|
@passed_updates = 0
|
59
|
+
|
44
60
|
@prng = Random.new(@start_time.to_i)
|
61
|
+
|
45
62
|
@teams = @divisions.values.flatten
|
46
63
|
@sorted_teams_by_wins = @teams.shuffle(random: @prng)
|
47
64
|
@playoff_teams = []
|
48
|
-
|
65
|
+
|
49
66
|
# warm vs warm / cool vs cool
|
50
|
-
rotated_divisions =
|
67
|
+
rotated_divisions = @divisions.values.rotate
|
51
68
|
@shuffled_division_pairs = Array.new(2) do |i|
|
52
69
|
(rotated_divisions[i] + rotated_divisions[-i - 1]).shuffle(random: @prng)
|
53
70
|
end
|
54
71
|
|
55
|
-
@game_in_matchup = 3
|
56
|
-
|
72
|
+
@game_in_matchup = @matchup_game_amt = 3
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Hash]
|
76
|
+
def to_h(simple: false)
|
77
|
+
res = {
|
78
|
+
start_time: @start_time.getutc.to_a.first(6).reverse,
|
79
|
+
divisions: @divisions.transform_values { |d| d.map { |t| t.to_h(simple:) } }
|
80
|
+
}
|
81
|
+
|
82
|
+
unless simple
|
83
|
+
# At this point, we don't need to pass simple to any of the to_h calls,
|
84
|
+
# because we know it is false
|
85
|
+
res[:last_update_time] = @last_update_time.getutc.to_a.first(6).reverse
|
86
|
+
res[:day] = @day
|
87
|
+
res[:games] = @games.map(&:to_h)
|
88
|
+
res[:games_in_progress] = @games_in_progress.map(&:to_h)
|
89
|
+
res[:alerts] = @alerts
|
90
|
+
res[:champion_team] = @champion_team&.name
|
91
|
+
res[:teams] = @teams.map(&:to_h)
|
92
|
+
res[:playoff_teams] = @playoff_teams.map(&:to_h)
|
93
|
+
end
|
94
|
+
|
95
|
+
res
|
57
96
|
end
|
58
97
|
|
59
|
-
##
|
60
98
|
# Updates the league to the current state
|
61
99
|
# This should be called whenever you need the current state of the league
|
62
100
|
def update_state
|
@@ -94,12 +132,12 @@ module Hlockey
|
|
94
132
|
if games_remaining.zero?
|
95
133
|
@playoff_teams = sort_teams_by_wins(playoff_qualifiers)
|
96
134
|
@playoff_teams.each { |team| team.status = :qualified }
|
97
|
-
(@teams
|
135
|
+
(@teams - @playoff_teams).each { |team| team.status = :partying }
|
98
136
|
return
|
99
137
|
end
|
100
138
|
|
101
139
|
set_to_qualify = playoff_qualifiers
|
102
|
-
@teams
|
140
|
+
@teams.each_with_index do |team, i|
|
103
141
|
next unless games_remaining < set_to_qualify[i / 5].wins - team.wins &&
|
104
142
|
games_remaining < set_to_qualify.last.wins - team.wins
|
105
143
|
|
@@ -123,7 +161,7 @@ module Hlockey
|
|
123
161
|
def new_games
|
124
162
|
if @game_in_matchup != @matchup_game_amt
|
125
163
|
# New game in matchups
|
126
|
-
@games.map! {
|
164
|
+
@games.map! { new_game(_1.away, _1.home) }
|
127
165
|
@game_in_matchup += 1
|
128
166
|
return
|
129
167
|
end
|
@@ -135,7 +173,7 @@ module Hlockey
|
|
135
173
|
|
136
174
|
case @day
|
137
175
|
when 1..27
|
138
|
-
@shuffled_division_pairs.each {
|
176
|
+
@shuffled_division_pairs.each { new_matchups(_1, reverse: @day.even?) }
|
139
177
|
when 28..37
|
140
178
|
@shuffled_division_pairs.transpose.each_with_index do |pair, i|
|
141
179
|
@games << new_game(*pair, reverse: i.even?)
|
@@ -143,62 +181,29 @@ module Hlockey
|
|
143
181
|
@shuffled_division_pairs.first.rotate!
|
144
182
|
when 38
|
145
183
|
@matchup_game_amt = 5
|
146
|
-
@playoff_teams = sort_teams_by_wins(playoff_qualifiers).map do
|
147
|
-
cloned_team =
|
184
|
+
@playoff_teams = sort_teams_by_wins(playoff_qualifiers).map do
|
185
|
+
cloned_team = _1.clone
|
148
186
|
cloned_team.status = :playoffs
|
149
187
|
cloned_team
|
150
188
|
end
|
151
189
|
new_playoff_matchups
|
152
190
|
when 39, 40
|
153
|
-
@playoff_teams.select!
|
191
|
+
@playoff_teams.select!(&:positive_record?)
|
154
192
|
new_playoff_matchups
|
155
193
|
when 41
|
156
|
-
|
157
|
-
|
158
|
-
champion.losses = 0
|
159
|
-
@playoff_teams = [@teams.last, champion]
|
160
|
-
@alerts = ["The #{@teams.last} have awoken..."]
|
161
|
-
new_playoff_matchups
|
162
|
-
when 42
|
163
|
-
return unless @champion_team.nil?
|
164
|
-
|
165
|
-
sleepers, @champion_team = @playoff_teams
|
166
|
-
@alerts = []
|
167
|
-
|
168
|
-
if @champion_team.to_s == "Baden Hallucinations"
|
169
|
-
@alerts << "The Baden Hallucinations have reached 5 championships."
|
170
|
-
@alerts << "They will evolve soon."
|
171
|
-
end
|
172
|
-
|
173
|
-
if sleepers.wins > @champion_team.wins
|
174
|
-
@alerts << "The #{sleepers} have beat your champions."
|
175
|
-
@alerts << "The #{sleepers} are evolving!"
|
176
|
-
old_name = sleepers.to_s
|
177
|
-
sleepers.to_s = "#{old_name}z"
|
178
|
-
@alerts << "#{old_name} -> #{sleepers}"
|
179
|
-
return
|
180
|
-
end
|
181
|
-
|
182
|
-
@alerts << "The #{sleepers} have lost."
|
183
|
-
@sleepers_changes.each do |(player, team_idx)|
|
184
|
-
player.team = @teams[team_idx]
|
185
|
-
player.team.shadows << player
|
186
|
-
@alerts << "#{player} has adventured into the #{player.team} shadows."
|
187
|
-
end
|
194
|
+
@champion_team = @playoff_teams.find(&:positive_record?)
|
195
|
+
@alerts << Message.SeasonChampion(@champion_team)
|
188
196
|
end
|
189
197
|
end
|
190
198
|
|
191
199
|
def update_games
|
192
200
|
@games_in_progress.each(&:update)
|
193
|
-
@games_in_progress = @games.select(&:in_progress)
|
201
|
+
@games_in_progress = @games.select(&:in_progress?)
|
194
202
|
@divisions.transform_values!(&method(:sort_teams_by_wins))
|
195
203
|
end
|
196
204
|
|
197
205
|
def new_playoff_matchups
|
198
|
-
@playoff_teams.each
|
199
|
-
team.wins = 0
|
200
|
-
team.losses = 0
|
201
|
-
end
|
206
|
+
@playoff_teams.each { _1.wins = _1.losses = 0 }
|
202
207
|
|
203
208
|
new_matchups(@playoff_teams, rotate: false)
|
204
209
|
end
|
@@ -208,7 +213,7 @@ module Hlockey
|
|
208
213
|
# @param reverse [Boolean]
|
209
214
|
def new_matchups(matchup_teams, rotate: true, reverse: false)
|
210
215
|
(matchup_teams.length / 2).times do |i|
|
211
|
-
@games << new_game(matchup_teams[i], matchup_teams[-i - 1], reverse:
|
216
|
+
@games << new_game(matchup_teams[i], matchup_teams[-i - 1], reverse:)
|
212
217
|
end
|
213
218
|
matchup_teams.insert(1, matchup_teams.pop) if rotate
|
214
219
|
end
|
@@ -225,7 +230,7 @@ module Hlockey
|
|
225
230
|
|
226
231
|
# @return [Array<Team>]
|
227
232
|
def playoff_qualifiers
|
228
|
-
top_divs =
|
233
|
+
top_divs = @divisions.values.map { sort_teams_by_wins(_1).first }
|
229
234
|
# Prevents tied teams from being unfairly decided by which division they're in
|
230
235
|
@sorted_teams_by_wins = sort_teams_by_wins(@sorted_teams_by_wins)
|
231
236
|
rest = (@sorted_teams_by_wins - top_divs).first(4)
|
@@ -233,17 +238,9 @@ module Hlockey
|
|
233
238
|
top_divs + rest
|
234
239
|
end
|
235
240
|
|
241
|
+
# A stable sort of the teams in the array by their wins
|
236
242
|
# @param teams [Array<Team>]
|
237
243
|
# @return [Array<Team>]
|
238
|
-
def sort_teams_by_wins(teams)
|
239
|
-
# A stable sort (Ruby's normal sort isn't always stable)
|
240
|
-
# t.wins is negative because it sorts the opposite way than I want otherwise
|
241
|
-
teams.sort_by.with_index { |t, i| [-t.wins, i] }
|
242
|
-
end
|
243
|
-
|
244
|
-
# @return [Hash<Symbol => Array<Team>]
|
245
|
-
def game_divisions
|
246
|
-
@divisions.except(:"Sleepy Tired")
|
247
|
-
end
|
244
|
+
def sort_teams_by_wins(teams) = teams.sort_by.with_index { |t, i| [-t.wins, i] }
|
248
245
|
end
|
249
246
|
end
|
data/lib/hlockey/message.rb
CHANGED
@@ -33,12 +33,12 @@ module Hlockey
|
|
33
33
|
# To control how this colors things, see #colorize=
|
34
34
|
# @param obj [Team, Team::Player, Team::Stadium]
|
35
35
|
# @return String
|
36
|
-
def color(obj)
|
36
|
+
def color(obj) =
|
37
37
|
@colorize.call(obj.instance_of?(Team) ? obj.color : obj.team.color, obj.to_s)
|
38
|
-
end
|
39
38
|
|
40
|
-
# These are messages logged to game streams
|
39
|
+
# These are messages logged to game streams / league alerts
|
41
40
|
[
|
41
|
+
# Game stream messages
|
42
42
|
%i[StartOfGame],
|
43
43
|
%i[EndOfGame winning_team],
|
44
44
|
%i[StartOfPeriod period],
|
@@ -51,93 +51,96 @@ module Hlockey
|
|
51
51
|
%i[Partying player],
|
52
52
|
%i[FightStarted home_player away_player],
|
53
53
|
%i[FightAttack attacking_player defending_player blocked],
|
54
|
-
%i[PlayerJoinedFight
|
54
|
+
%i[PlayerJoinedFight player],
|
55
55
|
%i[FightEnded],
|
56
56
|
%i[MoraleChange team amount],
|
57
57
|
%i[ChickenedOut prev_player next_player],
|
58
58
|
%i[InclineFavors team],
|
59
59
|
%i[StarsAlign team],
|
60
|
-
%i[WavesWashedAway prev_player next_player]
|
60
|
+
%i[WavesWashedAway prev_player next_player],
|
61
|
+
%i[ImmaterialDodge player hitting_player],
|
62
|
+
# League alert messages
|
63
|
+
%i[SeasonChampion team]
|
61
64
|
].each do |event, *fields|
|
62
65
|
define_method(event) { |*data| new(event, fields, data) }
|
63
66
|
end
|
64
67
|
|
65
68
|
# These are messages used elsewhere
|
66
69
|
|
67
|
-
def SeasonDay(day)
|
68
|
-
"Season #{VERSION} day #{day}"
|
69
|
-
end
|
70
|
+
def SeasonDay(day) = "Season #{VERSION} day #{day}"
|
70
71
|
|
71
|
-
def SeasonStarts(time)
|
72
|
+
def SeasonStarts(time) =
|
72
73
|
time.strftime("Season #{VERSION} starts at %H:%M, %A, %B %d (%Z).")
|
73
|
-
end
|
74
74
|
|
75
|
-
def
|
76
|
-
"Your season #{VERSION} champions are the #{team}!"
|
77
|
-
end
|
78
|
-
|
79
|
-
def NoGames
|
75
|
+
def NoGames =
|
80
76
|
"no games right now. it is the offseason. join the Hlockey Discord for updates"
|
81
|
-
end
|
82
77
|
end
|
83
78
|
|
84
|
-
def to_s
|
79
|
+
def to_s(do_color: true)
|
80
|
+
c = -> { color(_1, do_color:) }
|
81
|
+
|
85
82
|
case @event
|
86
83
|
when :StartOfGame
|
87
84
|
"Hocky!"
|
88
85
|
when :EndOfGame
|
89
|
-
"Game over.\n#{
|
86
|
+
"Game over.\n#{c.call(@winning_team)} win!"
|
90
87
|
when :StartOfPeriod
|
91
88
|
"Start#{of_period}"
|
92
89
|
when :EndOfPeriod
|
93
|
-
"End#{of_period}#{score}"
|
90
|
+
"End#{of_period}#{score(do_color:)}"
|
94
91
|
when :FaceOff
|
95
|
-
"#{
|
92
|
+
"#{c.call(@winning_player)} wins the faceoff!#{possession_change(do_color:)}"
|
96
93
|
when :Hit
|
97
|
-
str = "#{
|
98
|
-
str += takes + score_chance if @puck_taken
|
94
|
+
str = "#{c.call(@defender)} hits #{c.call(@puck_holder)}"
|
95
|
+
str += takes(do_color:) + score_chance if @puck_taken
|
99
96
|
str
|
100
97
|
when :Pass
|
101
|
-
str = "#{
|
98
|
+
str = "#{c.call(@sender)} passes to #{c.call(@receiver)}."
|
102
99
|
unless @interceptor.nil?
|
103
|
-
str += "..\nIntercepted by #{
|
100
|
+
str += "..\nIntercepted by #{c.call(@interceptor)}!" +
|
101
|
+
possession_change(do_color:)
|
104
102
|
end
|
105
103
|
str += score_chance
|
106
104
|
str
|
107
105
|
when :ShootScore
|
108
|
-
"#{shot} and scores!#{score}"
|
106
|
+
"#{shot(do_color:)} and scores!#{score(do_color:)}"
|
109
107
|
when :ShootBlock
|
110
|
-
"#{shot}...\n#{
|
108
|
+
"#{shot(do_color:)}...\n#{c.call(@blocker)} blocks the shot" +
|
109
|
+
takes(do_color:) + score_chance
|
111
110
|
when :Partying
|
112
|
-
"#{
|
111
|
+
"#{c.call(@player)} is partying!"
|
113
112
|
when :FightStarted
|
114
|
-
"#{
|
113
|
+
"#{c.call(@home_player)} and #{c.call(@away_player)} " \
|
114
|
+
"start fighting!"
|
115
115
|
when :FightAttack
|
116
|
-
|
117
|
-
str
|
116
|
+
def_player = c.call(@defending_player)
|
117
|
+
str = "#{c.call(@attacking_player)} punches #{def_player}!"
|
118
|
+
str += "\n#{def_player} blocks the punch!" if @blocked
|
118
119
|
str
|
119
120
|
when :PlayerJoinedFight
|
120
|
-
"#{
|
121
|
+
"#{c.call(@player)} from #{c.call(@player.team)} joins the fight!"
|
121
122
|
when :FightEnded
|
122
123
|
"The fight has ended."
|
123
124
|
when :MoraleChange
|
124
|
-
"#{
|
125
|
+
"#{c.call(@team)} #{@amount.negative? ? "loses" : "gains"} #{@amount.abs} morale."
|
125
126
|
when :ChickenedOut
|
126
|
-
"#{
|
127
|
+
"#{c.call(@prev_player)} chickened out!#{replaces("game", do_color:)}"
|
127
128
|
when :InclineFavors
|
128
|
-
"The incline favors #{
|
129
|
+
"The incline favors #{c.call(@team)}."
|
129
130
|
when :StarsAlign
|
130
|
-
"The stars align for #{
|
131
|
+
"The stars align for #{c.call(@team)}. They get half a goal."
|
131
132
|
when :WavesWashedAway
|
132
|
-
"#{
|
133
|
+
"#{c.call(@prev_player)} is washed away by the waves...#{replaces(do_color:)}"
|
134
|
+
when :ImmaterialDodge
|
135
|
+
"#{c.call(@hitting_player)} goes right through #{c.call(@player)}!"
|
136
|
+
when :SeasonChampion
|
137
|
+
"Your season #{VERSION} champions are the #{c.call(@team)}!"
|
133
138
|
end
|
134
139
|
end
|
135
140
|
|
136
141
|
private
|
137
142
|
|
138
|
-
def color(obj)
|
139
|
-
self.class.color(obj)
|
140
|
-
end
|
143
|
+
def color(obj, do_color: true) = do_color ? self.class.color(obj) : obj.to_s
|
141
144
|
|
142
145
|
def score_chance
|
143
146
|
return "" if @shooting_chance.nil?
|
@@ -155,28 +158,23 @@ module Hlockey
|
|
155
158
|
"\nChance of scoring: #{chance_str} (#{@shooting_chance})"
|
156
159
|
end
|
157
160
|
|
158
|
-
def of_period
|
159
|
-
" of period #{@period}."
|
160
|
-
end
|
161
|
+
def of_period = " of period #{@period}."
|
161
162
|
|
162
|
-
def score
|
163
|
-
"\n#{color(@home)} #{@home_score.round(2)},
|
164
|
-
|
163
|
+
def score(do_color: true) =
|
164
|
+
"\n#{color(@home, do_color:)} #{@home_score.round(2)}, " \
|
165
|
+
"#{color(@away, do_color:)} #{@away_score.round(2)}"
|
165
166
|
|
166
|
-
def shot
|
167
|
-
"#{color(@shooter)} takes a#{"n audacious" if @audacity} shot"
|
168
|
-
end
|
167
|
+
def shot(do_color: true) =
|
168
|
+
"#{color(@shooter, do_color:)} takes a#{"n audacious" if @audacity} shot"
|
169
169
|
|
170
|
-
def takes
|
171
|
-
@puck_taken ? " and takes the puck!#{possession_change}" : "!"
|
172
|
-
end
|
170
|
+
def takes(do_color: true) =
|
171
|
+
@puck_taken ? " and takes the puck!#{possession_change(do_color:)}" : "!"
|
173
172
|
|
174
|
-
def possession_change
|
175
|
-
"\n#{color(@new_puck_team)} have possession."
|
176
|
-
end
|
173
|
+
def possession_change(do_color: true) =
|
174
|
+
"\n#{color(@new_puck_team, do_color:)} have possession."
|
177
175
|
|
178
|
-
def replaces(period = nil)
|
179
|
-
str = "\n#{color(@next_player)} replaces them"
|
176
|
+
def replaces(period = nil, do_color: true)
|
177
|
+
str = "\n#{color(@next_player, do_color:)} replaces them"
|
180
178
|
str += " for the rest of the #{period}." unless period.nil?
|
181
179
|
str
|
182
180
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# The player this mod is applied to only gets swapped into offense
|
7
|
+
class Fencebuilder
|
8
|
+
include(Moddable)
|
9
|
+
|
10
|
+
DESCRIPTION = "This player can only play offense.".freeze
|
11
|
+
|
12
|
+
def on_swap(into_roster, pos, prev_pos, _prng) =
|
13
|
+
fence_swap(into_roster, pos, prev_pos)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# The player this mod is applied to only gets swapped into defense
|
7
|
+
class Fencedestroyer
|
8
|
+
include(Moddable)
|
9
|
+
|
10
|
+
DESCRIPTION = "This player can only play defense.".freeze
|
11
|
+
|
12
|
+
def on_swap(into_roster, pos, prev_pos, _prng) =
|
13
|
+
fence_swap(into_roster, pos, prev_pos, type: :destroyer)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# Specified player follows the player this mod is applied to
|
7
|
+
# when they are swapped in/out
|
8
|
+
class Handholding
|
9
|
+
include(Moddable)
|
10
|
+
|
11
|
+
DESCRIPTION = "This player is holding another player's hand.".freeze
|
12
|
+
|
13
|
+
# @param other_player [String] name of other player
|
14
|
+
def initialize(player, other_player)
|
15
|
+
super(player)
|
16
|
+
@other_player = other_player
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_data = [to_s, @other_player.to_s]
|
20
|
+
|
21
|
+
def on_swap(into_roster, pos, _prev_pos, prng)
|
22
|
+
unless @other_player.is_a?(Team::Player)
|
23
|
+
@other_player = @team.players.find { _1.name == @other_player }
|
24
|
+
end
|
25
|
+
|
26
|
+
if into_roster
|
27
|
+
prev_pos = @team.shadows.index(@other_player)
|
28
|
+
return if prev_pos.nil?
|
29
|
+
|
30
|
+
other_pos = with_deleted(@team.roster.keys, pos).sample(random: prng)
|
31
|
+
|
32
|
+
@team.shadows[prev_pos] = @team.roster[other_pos]
|
33
|
+
@team.roster[other_pos] = @other_player
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
prev_pos = @team.roster.key(@other_player)
|
38
|
+
return if prev_pos.nil?
|
39
|
+
|
40
|
+
other_pos = with_deleted(@team.shadows.each_index.to_a, pos).sample(random: prng)
|
41
|
+
|
42
|
+
@team.roster[prev_pos] = @team.shadows[other_pos]
|
43
|
+
@team.shadows[other_pos] = @other_player
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# For `on_swap`, because array.delete doesn't return the array for no reason
|
49
|
+
def with_deleted(array, object)
|
50
|
+
array.delete(object)
|
51
|
+
array
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require("hlockey/message")
|
2
|
+
require("hlockey/mod/moddable")
|
3
|
+
|
4
|
+
module Hlockey
|
5
|
+
module Mod
|
6
|
+
##
|
7
|
+
# The player this mod is applied to has a chance of avoiding hits
|
8
|
+
# by temporarily losing their physical form
|
9
|
+
class Immaterial
|
10
|
+
include(Moddable)
|
11
|
+
|
12
|
+
DESCRIPTION = "This player may be a hallucination.".freeze
|
13
|
+
|
14
|
+
def on_got_hit(game, hitting_player)
|
15
|
+
return if game.prng.rand(3).zero?
|
16
|
+
|
17
|
+
if game.pre_tmp_change_stats[@player].nil?
|
18
|
+
game.pre_tmp_change_stats[@player] = @player.stats.clone
|
19
|
+
end
|
20
|
+
@player.stats[:defense] += 0.5
|
21
|
+
|
22
|
+
Message.ImmaterialDodge(@player, hitting_player)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# This player can not swap positions.
|
7
|
+
class Locked
|
8
|
+
include(Moddable)
|
9
|
+
|
10
|
+
DESCRIPTION = "This player can not swap positions.".freeze
|
11
|
+
|
12
|
+
def on_swap(into_roster, pos, prev_pos, _prng) =
|
13
|
+
if into_roster
|
14
|
+
@team.roster[pos] = @team.shadows[prev_pos]
|
15
|
+
@team.shadows[prev_pos] = @player
|
16
|
+
else
|
17
|
+
@team.shadows[pos] = @team.roster[prev_pos]
|
18
|
+
@team.roster[prev_pos] = @player
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require("hlockey/selfdescribable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# Module with needed methods for mods, that every mod includes
|
7
|
+
# All methods starting with "on" represent player events
|
8
|
+
module Moddable
|
9
|
+
include(SelfDescribable)
|
10
|
+
|
11
|
+
DESCRIPTION = "A mod with no description.".freeze # Default description
|
12
|
+
|
13
|
+
# If the mod has no extra parameters, same as #to_s
|
14
|
+
# If it has extra parameters, returns an array with #to_s as the first element,
|
15
|
+
# and the other parameters as the rest of the elements.
|
16
|
+
# Mods that accept parameters should implement the array version themselves,
|
17
|
+
# as in the module here it is simply an alias.
|
18
|
+
alias to_data to_s
|
19
|
+
|
20
|
+
def initialize(player)
|
21
|
+
@player = player
|
22
|
+
@team = player.team
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_swap(into_roster, pos, prev_pos, prng) end
|
26
|
+
|
27
|
+
def on_action(game) end
|
28
|
+
|
29
|
+
def on_got_hit(game, hitting_player) end
|
30
|
+
|
31
|
+
def on_join_fight() end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def fence_swap(into_roster, pos, prev_pos, type: :builder)
|
36
|
+
return unless into_roster
|
37
|
+
|
38
|
+
new_pos_hash = { ldef: :lwing, goalie: :center, rdef: :rwing }
|
39
|
+
new_pos_hash = new_pos_hash.invert unless type == :builder
|
40
|
+
new_pos = new_pos_hash[pos]
|
41
|
+
return if new_pos.nil?
|
42
|
+
|
43
|
+
@team.roster[pos] = @team.shadows[prev_pos]
|
44
|
+
@team.shadows[prev_pos] = @team.roster[new_pos]
|
45
|
+
@team.roster[new_pos] = @player
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# The first player in the shadows will join fights instead of this player,
|
7
|
+
# if they do not also have the mod.
|
8
|
+
class Nonconfrontational
|
9
|
+
include(Moddable)
|
10
|
+
|
11
|
+
DESCRIPTION = "This player tries to avoid fights.".freeze
|
12
|
+
|
13
|
+
def on_join_fight =
|
14
|
+
@team.shadows.select { |player| player.mods.none? { _1.is_a?(self.class) } }.first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
require("hlockey/actions")
|
3
|
+
|
4
|
+
module Hlockey
|
5
|
+
module Mod
|
6
|
+
##
|
7
|
+
# Player has James stats added to their own at random
|
8
|
+
class Powernapper
|
9
|
+
include(Moddable)
|
10
|
+
include(Actions)
|
11
|
+
|
12
|
+
DESCRIPTION = "This player gets temporary stat boosts by powernapping.".freeze
|
13
|
+
|
14
|
+
def initialize(player)
|
15
|
+
super
|
16
|
+
@currently_boosted_for = -1
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_action(game)
|
20
|
+
if @currently_boosted_for.negative?
|
21
|
+
return unless random_event_occurs?(prng: game.prng)
|
22
|
+
|
23
|
+
change_stats(game)
|
24
|
+
end
|
25
|
+
@currently_boosted_for += 1
|
26
|
+
return if @currently_boosted_for < 5
|
27
|
+
|
28
|
+
change_stats(game, reset: true)
|
29
|
+
@currently_boosted_for = -1
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def change_stats(game, reset: false)
|
35
|
+
stat_change = { offense: 0.5, defense: 1.0 } # James stats (excluding 0 agility)
|
36
|
+
stat_change.transform_values!(&:-@) if reset
|
37
|
+
|
38
|
+
pre_tmp_change_stats = game.pre_tmp_change_stats[@player]
|
39
|
+
stat_change.each do |stat, change|
|
40
|
+
@player.stats[stat] += change
|
41
|
+
pre_tmp_change_stats[stat] += change unless pre_tmp_change_stats.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require("hlockey/mod/moddable")
|
2
|
+
|
3
|
+
module Hlockey
|
4
|
+
module Mod
|
5
|
+
##
|
6
|
+
# Player can get into a fight at any time
|
7
|
+
class Punchy
|
8
|
+
include(Moddable)
|
9
|
+
include(Actions)
|
10
|
+
|
11
|
+
DESCRIPTION = "This player may randomly start fights.".freeze
|
12
|
+
|
13
|
+
def on_action(game) =
|
14
|
+
random_event_occurs?(prng: game.prng) && game.start_fight(@player)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|