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