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