1 /*=============================================================================
2 Blobby Volley 2
3 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de)
4 Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de)
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 =============================================================================*/
20
21 /* header include */
22 #include "BotAPICalculations.h"
23
24 /* includes */
25 #include <cmath>
26 #include <limits>
27
28 #include "GameConstants.h"
29
30 /* implementation */
31
32 bool FLAG_BOUNCE = false;
33
34 // helpers
35 float time_to_x_direct(float pos, float vel, float destination);
36 float time_to_y_direct(float pos, float vel, float destination);
37 float parabel_time_first(float pos, float vel, float gravity, float destination);
38
make_unsigned(float f)39 float make_unsigned(float f)
40 {
41 return (f > 0 ? f : std::numeric_limits<float>::infinity());
42 }
43
44
reset_flags()45 void reset_flags()
46 {
47 FLAG_BOUNCE = false;
48 }
49
time_to_x(const Vector2 & pos,const Vector2 & vel,float destination)50 float time_to_x(const Vector2& pos, const Vector2& vel, float destination)
51 {
52 // check whether velocity is valid
53 if( vel.x == 0 )
54 return std::numeric_limits<float>::max();
55
56 // direct?
57 float timedirect = time_to_x_direct(pos.x, vel.x, destination);
58
59 // needs wall bounce
60 if( timedirect < 0 )
61 {
62 FLAG_BOUNCE = true;
63
64 float wall = vel.x > 0 ? (RIGHT_PLANE - BALL_RADIUS) : BALL_RADIUS;
65 float twall = time_to_x_direct(pos.x, vel.x, wall);
66 float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS);
67 float tnet = make_unsigned(time_to_x_direct(pos.x, vel.x, net));
68
69 if ( tnet < twall )
70 {
71 Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet));
72 if ( nhitpos.y > NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS )
73 return tnet + time_to_x(nhitpos, vel.reflectX(), destination);
74
75 }
76 Vector2 whitpos = Vector2(wall, predict_y(pos, vel, twall));
77 return twall + time_to_x(whitpos, vel.reflectX(), destination);
78 }
79
80 float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS);
81 float tnet = make_unsigned(time_to_x_direct(pos.x, vel.x, net));
82 Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet));
83
84 if ( tnet > timedirect || nhitpos.y < NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS)
85 {
86 // if ball is too high or destination is reached before net, no collision can occur
87 return timedirect;
88 }
89
90 FLAG_BOUNCE = true;
91
92 // if ball hits net on false side, it is impossible to reach its destination
93 if( nhitpos.y > pos.y )
94 {
95 return std::numeric_limits<float>::max();
96 }
97
98 return tnet + time_to_x(nhitpos, vel.reflectX(), destination);
99
100 }
101
time_to_y(const Vector2 & pos,const Vector2 & vel,float destination)102 float time_to_y(const Vector2& pos, const Vector2& vel, float destination)
103 {
104 return time_to_y_direct(pos.y, vel.y, destination);
105 }
106
predict_x(const Vector2 & pos,const Vector2 & vel,float time)107 float predict_x(const Vector2& pos, const Vector2& vel, float time)
108 {
109 // can net collision occur
110 float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS);
111 float tnet = make_unsigned( time_to_x_direct(pos.x, vel.x, net) );
112
113 // calculate estimated hitpos
114 Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet));
115
116 // can ignore net bounce
117 if ( tnet > time || nhitpos.y < NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS) {
118 float spos = pos.x + vel.x*time;
119 if ( spos < BALL_RADIUS)
120 return 2 * BALL_RADIUS - spos;
121 else if ( spos > RIGHT_PLANE - BALL_RADIUS )
122 return 2*(RIGHT_PLANE - BALL_RADIUS) - spos;
123
124 return spos;
125 }
126
127 // collision with net
128 return predict_x(nhitpos, vel.reflectX(), time - tnet);
129
130
131 }
predict_y(const Vector2 & pos,const Vector2 & vel,float time)132 float predict_y(const Vector2& pos, const Vector2& vel, float time)
133 {
134 return pos.y + (vel.y + BALL_GRAVITATION/2.0 * time) * time;
135 }
136
y_at_x(const Vector2 & pos,const Vector2 & vel,float destination)137 float y_at_x(const Vector2& pos, const Vector2& vel, float destination)
138 {
139 float time = time_to_x(pos, vel, destination);
140 return predict_y(pos, vel, time);
141 }
x_at_y(const Vector2 & pos,const Vector2 & vel,float destination)142 float x_at_y(const Vector2& pos, const Vector2& vel, float destination)
143 {
144 float time = time_to_y(pos, vel, destination);
145 return predict_x(pos, vel, time);
146 }
147
next_event(const Vector2 & pos,const Vector2 & vel)148 float next_event(const Vector2& pos, const Vector2& vel)
149 {
150 // walls and net
151 float time_wall;
152 float time_net;
153 if( vel.x > 0 )
154 {
155 time_wall = time_to_x_direct(pos.x, vel.x, RIGHT_PLANE - BALL_RADIUS);
156 time_net = time_to_x_direct(pos.x, vel.x, NET_POSITION_X - NET_RADIUS - BALL_RADIUS);
157 }
158 else
159 {
160 time_wall = time_to_x_direct(pos.x, vel.x, LEFT_PLANE + BALL_RADIUS);
161 time_net = time_to_x_direct(pos.x, vel.x, NET_POSITION_X + NET_RADIUS + BALL_RADIUS);
162 }
163
164 // ground
165 float time_ground = time_to_y_direct(pos.y, vel.y, GROUND_PLANE_HEIGHT_MAX - BALL_RADIUS);
166
167 time_net = make_unsigned(time_net);
168 time_wall = make_unsigned(time_wall);
169 time_ground = make_unsigned(time_ground);
170
171 if ( time_net < time_wall && time_net < time_ground )
172 {
173 FLAG_BOUNCE = true;
174 return time_net;
175 }
176 else if ( time_wall < time_net && time_wall < time_ground )
177 {
178 FLAG_BOUNCE = true;
179 return time_wall;
180 }
181 else
182 {
183 return time_ground;
184 }
185 }
186
time_to_x_direct(float pos,float vel,float destination)187 float time_to_x_direct(float pos, float vel, float destination)
188 {
189 return (destination - pos) / vel;
190 }
191
time_to_y_direct(float pos,float vel,float destination)192 float time_to_y_direct(float pos, float vel, float destination)
193 {
194 return parabel_time_first(pos, vel, BALL_GRAVITATION, destination);
195 }
196
parabel_time_first(float pos,float vel,float grav,float destination)197 float parabel_time_first(float pos, float vel, float grav, float destination)
198 {
199 float sq = vel*vel + 2*grav*(destination - pos);
200
201 // if unreachable, return -1
202 if ( sq < 0 )
203 {
204 return -1;
205 }
206 sq = std::sqrt(sq);
207
208 float tmin = (-vel - sq) / grav;
209 float tmax = (-vel + sq) / grav;
210
211 if ( grav < 0 )
212 {
213 float temp = tmin;
214 tmin = tmax; tmax = temp;
215 }
216
217 if ( tmin > 0 )
218 return tmin;
219 else if ( tmax > 0 )
220 return tmax;
221
222 return -1;
223 }
224