hlockey 5 → 6
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 +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
|