rubygoal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ require 'forwardable'
2
+ require 'gosu'
3
+
4
+ require 'rubygoal/ball'
5
+ require 'rubygoal/coach_loader'
6
+ require 'rubygoal/team'
7
+ require 'rubygoal/field_metrics'
8
+ require 'rubygoal/match'
9
+ require 'rubygoal/coaches/coach_home'
10
+ require 'rubygoal/coaches/coach_away'
11
+
12
+ module Rubygoal
13
+ class Field
14
+ attr_reader :ball, :team_home, :team_away
15
+
16
+ extend Forwardable
17
+ def_delegators :ball, :goal?
18
+
19
+ def initialize(game_window)
20
+ @game_window = game_window
21
+ @background_image = Gosu::Image.new(game_window, Config.field.background_image_file, true)
22
+
23
+ @ball = Ball.new(game_window, FieldMetrics.center_position)
24
+
25
+ coach_home = CoachLoader.get(CoachHome)
26
+ coach_away = CoachLoader.get(CoachAway)
27
+
28
+ puts "Home coach: #{coach_home.class.name}"
29
+ puts "Away coach: #{coach_away.class.name}"
30
+
31
+ @team_home = Team.new(game_window, :home, coach_home)
32
+ @team_away = Team.new(game_window, :away, coach_away)
33
+ end
34
+
35
+ def reinitialize
36
+ team_home.players_to_initial_position
37
+ team_away.players_to_initial_position
38
+ ball.position = FieldMetrics.center_position
39
+ end
40
+
41
+ def team_names
42
+ {
43
+ home: team_home.name,
44
+ away: team_away.name
45
+ }
46
+ end
47
+
48
+ def update
49
+ team_home.update(game_data(:home))
50
+ team_away.update(game_data(:away))
51
+ ball.update
52
+ end
53
+
54
+ def draw
55
+ background_image.draw(0, 0, 0);
56
+ ball.draw
57
+ team_home.draw
58
+ team_away.draw
59
+ end
60
+
61
+ private
62
+
63
+ def game_data(side)
64
+ case side
65
+ when :home
66
+ Match.new(
67
+ game_window.score_home,
68
+ game_window.score_away,
69
+ game_window.time,
70
+ team_away.formation
71
+ )
72
+ when :away
73
+ Match.new(
74
+ game_window.score_away,
75
+ game_window.score_home,
76
+ game_window.time,
77
+ team_home.formation
78
+ )
79
+ end
80
+ end
81
+
82
+ attr_reader :background_image, :game_window
83
+ end
84
+ end
@@ -0,0 +1,85 @@
1
+ require 'rubygoal/config'
2
+ require 'rubygoal/coordinate'
3
+ require 'rubygoal/field_metrics'
4
+
5
+ module Rubygoal
6
+ module FieldMetrics
7
+ class << self
8
+ def center_position
9
+ center = Position.new(
10
+ Config.field.width / 2,
11
+ Config.field.height / 2
12
+ )
13
+ Config.field.offset.add(center)
14
+ end
15
+
16
+ def goal_position(side)
17
+ position = center_position
18
+ case side
19
+ when :home
20
+ position.x = Config.field.offset.x
21
+ when :away
22
+ position.x = Config.field.offset.x + Config.field.width
23
+ end
24
+ position
25
+ end
26
+
27
+ def initial_player_positions(side)
28
+ Config.team.initial_player_positions.map do |pos|
29
+ case side
30
+ when :home
31
+ Position.new(
32
+ Config.field.offset.x + pos[0],
33
+ Config.field.offset.y + pos[1]
34
+ )
35
+ when :away
36
+ Position.new(
37
+ Config.field.offset.x + Config.field.width - pos[0],
38
+ Config.field.offset.y + Config.field.height - pos[1]
39
+ )
40
+ end
41
+ end
42
+ end
43
+
44
+ def goalkeeper_position(side)
45
+ initial_player_positions(side).first
46
+ end
47
+
48
+ def position_side(position)
49
+ if position.x < FieldMetrics.center_position.x
50
+ :home
51
+ else
52
+ :away
53
+ end
54
+ end
55
+
56
+ def out_of_bounds_width?(position)
57
+ lower_limit = Config.field.offset.x
58
+ upper_limit = Config.field.offset.x + Config.field.width
59
+ !(lower_limit..upper_limit).include?(position.x)
60
+ end
61
+
62
+ def out_of_bounds_height?(position)
63
+ lower_limit = Config.field.offset.y
64
+ upper_limit = Config.field.offset.y + Config.field.height
65
+ !(lower_limit..upper_limit).include?(position.y)
66
+ end
67
+
68
+ def goal?(position)
69
+ if out_of_bounds_width?(position)
70
+ lower_limit = center_position.y - Config.field.goal_height / 2
71
+ upper_limit = center_position.y + Config.field.goal_height / 2
72
+
73
+ (lower_limit..upper_limit).include?(position.y)
74
+ else
75
+ false
76
+ end
77
+ end
78
+
79
+ def close_to_goal?(position, side)
80
+ goal_position = FieldMetrics.goal_position(side)
81
+ goal_position.distance(position) < Config.field.close_to_goal_distance
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,69 @@
1
+ module Rubygoal
2
+ class Formation
3
+ attr_accessor :lineup
4
+
5
+ def initialize
6
+ @lineup = [
7
+ [:none, :none, :none, :none, :none],
8
+ [:none, :none, :none, :none, :none],
9
+ [:none, :none, :none, :none, :none],
10
+ [:none, :none, :none, :none, :none],
11
+ [:none, :none, :none, :none, :none],
12
+ ]
13
+ end
14
+
15
+ def defenders
16
+ column(0)
17
+ end
18
+
19
+ def midfielders
20
+ column(2)
21
+ end
22
+
23
+ def attackers
24
+ column(4)
25
+ end
26
+
27
+ def column(i)
28
+ lineup.map { |row| row[i] }
29
+ end
30
+
31
+ def defenders=(f)
32
+ assign_column(0, f)
33
+ end
34
+
35
+ def midfielders=(f)
36
+ assign_column(2, f)
37
+ end
38
+
39
+ def attackers=(f)
40
+ assign_column(4, f)
41
+ end
42
+
43
+ def assign_column(index, column)
44
+ column.each_with_index do |player, row_index|
45
+ lineup[row_index][index] = player
46
+ end
47
+ end
48
+
49
+ def errors
50
+ errors = {}
51
+
52
+ unified = lineup.flatten
53
+ captains = unified.count(:captain)
54
+ fast = unified.count(:fast)
55
+ average = unified.count(:average)
56
+ total = captains + fast + average
57
+
58
+ errors[:captains] = "La cantidad de capitanes es #{captains}" if captains != 1
59
+ errors[:fast] = "La cantidad de rapidos es #{fast}" if fast != 3
60
+ errors[:total] = "La cantidad total de jugadores es #{total}" if total != 10
61
+
62
+ errors
63
+ end
64
+
65
+ def valid?
66
+ errors.empty?
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,179 @@
1
+ require 'gosu'
2
+ require 'forwardable'
3
+
4
+ require 'rubygoal/coordinate'
5
+ require 'rubygoal/field'
6
+ require 'rubygoal/field_metrics'
7
+ require 'rubygoal/goal'
8
+
9
+ module Rubygoal
10
+ class Game < Gosu::Window
11
+ attr_reader :field, :time, :score_home, :score_away
12
+
13
+ extend Forwardable
14
+ def_delegators :field, :ball, :close_to_goal?
15
+
16
+ def initialize
17
+ super(1920, 1080, true)
18
+ self.caption = "Ruby Goal"
19
+
20
+ @field = Field.new(self)
21
+ @goal = Goal.new(self)
22
+
23
+ @state = :playing
24
+
25
+ @time = Config.game.time
26
+ @score_home = 0
27
+ @score_away = 0
28
+
29
+ @font = Gosu::Font.new(
30
+ self,
31
+ Gosu.default_font_name,
32
+ Config.game.default_font_size
33
+ )
34
+
35
+ @home_team_label = create_label_image(name_home)
36
+ @away_team_label = create_label_image(name_away)
37
+ end
38
+
39
+ def update
40
+ return if state == :ended
41
+
42
+ update_elapsed_time
43
+
44
+ if field.goal?
45
+ update_goal
46
+ else
47
+ update_remaining_time
48
+ field.update
49
+ end
50
+
51
+ end_match! if time <= 0
52
+ end
53
+
54
+ def draw
55
+ field.draw
56
+ draw_scoreboard
57
+ draw_team_labels
58
+ goal.draw if field.goal?
59
+ end
60
+
61
+ def button_down(id)
62
+ if id == Gosu::KbEscape
63
+ close
64
+ end
65
+ end
66
+
67
+ protected
68
+
69
+ attr_writer :time, :score_home, :score_away
70
+ attr_accessor :state, :last_time, :elapsed_time
71
+
72
+ private
73
+
74
+ attr_reader :font, :goal, :home_team_label, :away_team_label
75
+
76
+ def create_label_image(name)
77
+ name = truncate_label(name, Config.game.max_name_length)
78
+ Gosu::Image.from_text(
79
+ self,
80
+ name,
81
+ Gosu.default_font_name,
82
+ Config.game.team_label_font_size,
83
+ 1,
84
+ Config.game.team_label_width,
85
+ :center
86
+ )
87
+ end
88
+
89
+ def truncate_label(name, limit)
90
+ words = name.split
91
+
92
+ left = limit
93
+ truncated = []
94
+ words.each do |word|
95
+ break unless left > 0
96
+
97
+ truncated << word
98
+ left -= word.length + 1
99
+ end
100
+
101
+ truncated.join(' ')[0..limit]
102
+ end
103
+
104
+ def update_elapsed_time
105
+ self.last_time ||= Time.now
106
+
107
+ self.elapsed_time = Time.now - last_time
108
+ self.last_time = Time.now
109
+ end
110
+
111
+ def update_remaining_time
112
+ self.time -= elapsed_time
113
+ end
114
+
115
+ def update_goal
116
+ goal.update(elapsed_time)
117
+ reinitialize_match if goal.celebration_done?
118
+ end
119
+
120
+ def reinitialize_match
121
+ if FieldMetrics.position_side(ball.position) == :home
122
+ self.score_away += 1
123
+ else
124
+ self.score_home += 1
125
+ end
126
+
127
+ field.reinitialize
128
+ end
129
+
130
+ def draw_scoreboard
131
+ draw_text(time_text, Config.game.time_label_position, :gray)
132
+ draw_text(score_home.to_s, Config.game.score_home_label_position, :white)
133
+ draw_text(score_away.to_s, Config.game.score_away_label_position, :white)
134
+ end
135
+
136
+ def draw_team_labels
137
+ pos = Config.game.home_team_label_position
138
+ home_team_label.draw_rot(pos.x, pos.y, 1, -90)
139
+ pos = Config.game.away_team_label_position
140
+ away_team_label.draw_rot(pos.x, pos.y, 1, 90)
141
+ end
142
+
143
+ def draw_text(text, position, color, size = 1)
144
+ font.draw_rel(text, position.x, position.y, 0.5, 0.5, 1, size, size, color_to_hex(color))
145
+ end
146
+
147
+ def time_text
148
+ minutes = Integer(time) / 60
149
+ seconds = Integer(time) % 60
150
+ "%d:%02d" % [minutes, seconds]
151
+ end
152
+
153
+ def color_to_hex(color)
154
+ case color
155
+ when :white
156
+ 0xffffffff
157
+ when :gray
158
+ 0xff6d6e70
159
+ end
160
+ end
161
+
162
+ def end_match!
163
+ self.state = :ended
164
+ write_score
165
+ end
166
+
167
+ def write_score
168
+ puts "#{name_home} #{score_home} - #{score_away} #{name_away}"
169
+ end
170
+
171
+ def name_home
172
+ field.team_names[:home]
173
+ end
174
+
175
+ def name_away
176
+ field.team_names[:away]
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,43 @@
1
+ require 'gosu'
2
+
3
+ require 'rubygoal/config'
4
+
5
+ module Rubygoal
6
+ class Goal
7
+ def initialize(game_window)
8
+ @goal_image = Gosu::Image.new(game_window, Config.goal.image_file, true)
9
+
10
+ @celebration_time = 0
11
+ end
12
+
13
+ def celebration_done?
14
+ !celebrating?
15
+ end
16
+
17
+ def draw
18
+ position = Config.game.goal_image_position
19
+ goal_image.draw(position.x, position.y, 1)
20
+ end
21
+
22
+ def update(elapsed_time)
23
+ start_celebration unless celebrating?
24
+ self.celebration_time -= elapsed_time
25
+ end
26
+
27
+ protected
28
+
29
+ attr_accessor :celebration_time
30
+
31
+ private
32
+
33
+ attr_reader :goal_image
34
+
35
+ def start_celebration
36
+ self.celebration_time = 3
37
+ end
38
+
39
+ def celebrating?
40
+ celebration_time > 0
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ module Rubygoal
2
+ class Match
3
+ class Team
4
+ attr_reader :score, :result, :formation
5
+
6
+ def initialize(score, result, other_formation = nil)
7
+ @score = score
8
+ @result = result
9
+ @formation = other_formation
10
+ end
11
+
12
+ def draw?
13
+ result == :draw
14
+ end
15
+
16
+ def winning?
17
+ result == :win
18
+ end
19
+
20
+ def losing?
21
+ result == :lose
22
+ end
23
+ end
24
+
25
+ attr_reader :me, :other, :time
26
+
27
+ def initialize(my_score, other_score, time, other_formation)
28
+ @me = Match::Team.new(
29
+ my_score,
30
+ result(my_score, other_score)
31
+ )
32
+ @other = Match::Team.new(
33
+ other_score,
34
+ result(other_score, my_score),
35
+ other_formation
36
+ )
37
+ @time = time
38
+ end
39
+
40
+ private
41
+
42
+ def result(my_score, other_score)
43
+ if my_score > other_score
44
+ :win
45
+ elsif my_score < other_score
46
+ :lose
47
+ else
48
+ :draw
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ require 'gosu'
2
+
3
+ require 'rubygoal/coordinate'
4
+
5
+ module Rubygoal
6
+ module Moveable
7
+ MIN_DISTANCE = 10
8
+
9
+ attr_accessor :position, :velocity
10
+
11
+ def initialize
12
+ @position = Position.new(0, 0)
13
+ @velocity = Velocity.new(0, 0)
14
+ @speed = 0
15
+ @destination = nil
16
+ end
17
+
18
+ def moving?
19
+ velocity.nonzero?
20
+ end
21
+
22
+ def distance(position)
23
+ Gosu.distance(self.position.x, self.position.y, position.x, position.y)
24
+ end
25
+
26
+ def move_to(destination)
27
+ self.destination = destination
28
+
29
+ angle = Gosu.angle(position.x, position.y, destination.x, destination.y)
30
+ velocity.x = Gosu.offset_x(angle, speed)
31
+ velocity.y = Gosu.offset_y(angle, speed)
32
+ end
33
+
34
+ def update
35
+ return unless moving?
36
+
37
+ if destination && distance(destination) < MIN_DISTANCE
38
+ stop
39
+ else
40
+ position.x += velocity.x
41
+ position.y += velocity.y
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :speed
48
+ attr_accessor :destination
49
+
50
+ def stop
51
+ self.destination = nil
52
+ self.velocity = Velocity.new(0, 0)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,86 @@
1
+ require 'gosu'
2
+
3
+ require 'rubygoal/coordinate'
4
+ require 'rubygoal/moveable'
5
+
6
+ module Rubygoal
7
+ class Player
8
+ include Moveable
9
+
10
+ def initialize(game_window, side)
11
+ super()
12
+
13
+ @image = Gosu::Image.new(game_window, image_filename(side), false)
14
+ @time_to_kick_again = 0
15
+ @field = game_window.field
16
+ end
17
+
18
+ def can_kick?(ball)
19
+ !waiting_to_kick_again? && control_ball?(ball)
20
+ end
21
+
22
+ def kick(ball, target)
23
+ strength = Config.player.kick_strength * Random.rand(error_range)
24
+
25
+ direction = Gosu.angle(position.x, position.y, target.x, target.y)
26
+ direction *= Random.rand(error_range)
27
+
28
+ velocity = Velocity.new(
29
+ Gosu.offset_x(direction, strength),
30
+ Gosu.offset_y(direction, strength)
31
+ )
32
+ ball.velocity = velocity
33
+
34
+ reset_waiting_to_kick!
35
+ end
36
+
37
+ def update
38
+ update_waiting_to_kick!
39
+ super
40
+ end
41
+
42
+ def draw
43
+ if moving?
44
+ angle = Gosu.angle(position.x, position.y, destination.x, destination.y)
45
+ angle -= 90
46
+ else
47
+ angle = 0.0
48
+ end
49
+
50
+ image.draw_rot(position.x, position.y, 1, angle)
51
+ end
52
+
53
+ protected
54
+
55
+ attr_accessor :time_to_kick_again
56
+
57
+ private
58
+
59
+ attr_reader :field, :image, :error
60
+
61
+ def waiting_to_kick_again?
62
+ time_to_kick_again > 0
63
+ end
64
+
65
+ def reset_waiting_to_kick!
66
+ self.time_to_kick_again = Config.player.time_to_kick_again
67
+ end
68
+
69
+ def update_waiting_to_kick!
70
+ # TODO Make it time-based rather than counting ticks
71
+ self.time_to_kick_again -= 1 if waiting_to_kick_again?
72
+ end
73
+
74
+ def control_ball?(ball)
75
+ distance(ball.position) < Config.player.distance_control_ball
76
+ end
77
+
78
+ def error_range
79
+ (1.0 - error) .. (1.0 + error)
80
+ end
81
+ end
82
+ end
83
+
84
+ require 'rubygoal/players/average'
85
+ require 'rubygoal/players/fast'
86
+ require 'rubygoal/players/captain'
@@ -0,0 +1,20 @@
1
+ module Rubygoal
2
+ class AveragePlayer < Player
3
+ def initialize(*args)
4
+ super
5
+ @error = 0.10 + Random.rand(0.05)
6
+ @speed = 3.5
7
+ end
8
+
9
+ protected
10
+
11
+ def image_filename(side)
12
+ case side
13
+ when :home
14
+ Config.player.average_home_image_file
15
+ when :away
16
+ Config.player.average_away_image_file
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Rubygoal
2
+ class CaptainPlayer < Player
3
+ def initialize(*args)
4
+ super
5
+ @error = 0.05
6
+ @speed = 4.5
7
+ end
8
+
9
+ protected
10
+
11
+ def image_filename(side)
12
+ case side
13
+ when :home
14
+ Config.player.captain_home_image_file
15
+ when :away
16
+ Config.player.captain_away_image_file
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Rubygoal
2
+ class FastPlayer < Player
3
+ def initialize(*args)
4
+ super
5
+ @error = 0.10 + Random.rand(0.05)
6
+ @speed = 4
7
+ end
8
+
9
+ protected
10
+
11
+ def image_filename(side)
12
+ case side
13
+ when :home
14
+ Config.player.fast_home_image_file
15
+ when :away
16
+ Config.player.fast_away_image_file
17
+ end
18
+ end
19
+ end
20
+ end