hlockey 5 → 6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/data/election.json +20 -0
  3. data/data/information.json +5 -0
  4. data/data/league.json +1984 -0
  5. data/data/links.json +5 -0
  6. data/data/previous_election_results.json +98 -0
  7. data/lib/hlockey/actions.rb +22 -0
  8. data/lib/hlockey/data.rb +6 -5
  9. data/lib/hlockey/game/fight.rb +28 -17
  10. data/lib/hlockey/game/weather/audacity.rb +1 -1
  11. data/lib/hlockey/game/weather/chicken.rb +1 -1
  12. data/lib/hlockey/game/weather/sunset.rb +1 -1
  13. data/lib/hlockey/game/weather/waves.rb +11 -5
  14. data/lib/hlockey/game/weather/weatherable.rb +8 -8
  15. data/lib/hlockey/game.rb +58 -33
  16. data/lib/hlockey/league.rb +60 -63
  17. data/lib/hlockey/message.rb +54 -56
  18. data/lib/hlockey/mod/fencebuilder.rb +16 -0
  19. data/lib/hlockey/mod/fencedestroyer.rb +16 -0
  20. data/lib/hlockey/mod/handholding.rb +55 -0
  21. data/lib/hlockey/mod/immaterial.rb +26 -0
  22. data/lib/hlockey/mod/locked.rb +22 -0
  23. data/lib/hlockey/mod/moddable.rb +49 -0
  24. data/lib/hlockey/mod/nonconfrontational.rb +17 -0
  25. data/lib/hlockey/mod/powernapper.rb +46 -0
  26. data/lib/hlockey/mod/punchy.rb +17 -0
  27. data/lib/hlockey/mod.rb +25 -0
  28. data/lib/hlockey/selfdescribable.rb +9 -0
  29. data/lib/hlockey/team/player.rb +37 -58
  30. data/lib/hlockey/team/stadium.rb +10 -7
  31. data/lib/hlockey/team.rb +56 -41
  32. data/lib/hlockey/version.rb +1 -1
  33. metadata +27 -17
  34. data/data/election.yaml +0 -21
  35. data/data/external/names.txt +0 -19948
  36. data/data/information.yaml +0 -24
  37. data/data/league.yaml +0 -1694
  38. data/data/links.yaml +0 -3
  39. data/data/previous_election_results.yaml +0 -65
  40. data/lib/hlockey/game/actions.rb +0 -24
@@ -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
- def initialize
35
- @start_time, @divisions = Data.league
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
- @sleepers_changes = @teams.last.roster.values.zip([18, 4, 13, 19, 6, 0])
65
+
49
66
  # warm vs warm / cool vs cool
50
- rotated_divisions = game_divisions.values.rotate
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
- @matchup_game_amt = 3
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[...-1] - @playoff_teams).each { |team| team.status = :partying }
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[...-1].each_with_index do |team, i|
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! { |game| new_game(game.away, game.home) }
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 { |p| new_matchups(p, reverse: @day.even?) }
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 |team|
147
- cloned_team = team.clone
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! { |team| team.wins > team.losses }
191
+ @playoff_teams.select!(&:positive_record?)
154
192
  new_playoff_matchups
155
193
  when 41
156
- champion = @playoff_teams.find { |team| team.wins > team.losses }
157
- champion.wins = 0
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 do |team|
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: 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 = game_divisions.values.map { |teams| sort_teams_by_wins(teams).first }
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
@@ -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 team player],
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 SeasonChampion(team)
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#{color(@winning_team)} win!"
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
- "#{color(@winning_player)} wins the faceoff!#{possession_change}"
92
+ "#{c.call(@winning_player)} wins the faceoff!#{possession_change(do_color:)}"
96
93
  when :Hit
97
- str = "#{color(@defender)} hits #{color(@puck_holder)}"
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 = "#{color(@sender)} passes to #{color(@receiver)}."
98
+ str = "#{c.call(@sender)} passes to #{c.call(@receiver)}."
102
99
  unless @interceptor.nil?
103
- str += "..\nIntercepted by #{color(@interceptor)}!#{possession_change}"
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#{color(@blocker)} blocks the shot#{takes}#{score_chance}"
108
+ "#{shot(do_color:)}...\n#{c.call(@blocker)} blocks the shot" +
109
+ takes(do_color:) + score_chance
111
110
  when :Partying
112
- "#{color(@player)} is partying!"
111
+ "#{c.call(@player)} is partying!"
113
112
  when :FightStarted
114
- "#{color(@home_player)} and #{color(@away_player)} start fighting!"
113
+ "#{c.call(@home_player)} and #{c.call(@away_player)} " \
114
+ "start fighting!"
115
115
  when :FightAttack
116
- str = "#{color(@attacking_player)} punches #{color(@defending_player)}!"
117
- str += "\n#{color(@defending_player)} blocks the punch!" if @blocked
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
- "#{color(@player)} from #{color(@team)} joins the fight!"
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
- "#{color(@team)} #{@amount.negative? ? "loses" : "gains"} #{@amount.abs} morale."
125
+ "#{c.call(@team)} #{@amount.negative? ? "loses" : "gains"} #{@amount.abs} morale."
125
126
  when :ChickenedOut
126
- "#{color(@prev_player)} chickened out!#{replaces("game")}"
127
+ "#{c.call(@prev_player)} chickened out!#{replaces("game", do_color:)}"
127
128
  when :InclineFavors
128
- "The incline favors #{color(@team)}."
129
+ "The incline favors #{c.call(@team)}."
129
130
  when :StarsAlign
130
- "The stars align for #{color(@team)}. They get half a goal."
131
+ "The stars align for #{c.call(@team)}. They get half a goal."
131
132
  when :WavesWashedAway
132
- "#{color(@prev_player)} is washed away by the waves...#{replaces}"
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)}, #{color(@away)} #{@away_score.round(2)}"
164
- end
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