1 /* $Id: comShakeCut.cpp,v 1.19 2003/07/25 17:28:27 nan Exp $ */
2 
3 // Copyright (C) 2000-2003  $B?@Fn(B $B5H9((B(Kanna Yoshihiro)
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19 #include "ttinc.h"
20 #include "comShakeCut.h"
21 #include "Control.h"
22 #include "Ball.h"
23 #include "Player.h"
24 #include "PlayGame.h"
25 #include "RCFile.h"
26 
27 extern RCFile *theRC;
28 
29 extern Ball   theBall;
30 
ComShakeCut()31 ComShakeCut::ComShakeCut() : ShakeCut(), ComPlayer() {
32 }
33 
ComShakeCut(long side)34 ComShakeCut::ComShakeCut(long side) : ShakeCut(side), ComPlayer() {
35 }
36 
ComShakeCut(long playerType,long side,double x,double y,double z,double vx,double vy,double vz,long status,long swing,long swingType,bool swingSide,long afterSwing,long swingError,double targetX,double targetY,double eyeX,double eyeY,double eyeZ,long pow,double spin,double stamina,long statusMax)37 ComShakeCut::ComShakeCut( long playerType, long side,
38 			  double x, double y, double z,
39 			  double vx, double vy, double vz,
40 			  long status, long swing,
41 			  long swingType, bool swingSide,
42 			  long afterSwing, long swingError,
43 			  double targetX, double targetY,
44 			  double eyeX, double eyeY, double eyeZ,
45 			  long pow, double spin, double stamina,
46 			  long statusMax ) :
47   ShakeCut( playerType, side, x, y, z, vx, vy, vz, status, swing, swingType,
48 	    swingSide, afterSwing, swingError, targetX, targetY,
49 	    eyeX, eyeY, eyeZ, pow, spin, stamina, statusMax ), ComPlayer() {
50 }
51 
~ComShakeCut()52 ComShakeCut::~ComShakeCut() {
53 }
54 
55 bool
Move(SDL_keysym * KeyHistory,long * MouseXHistory,long * MouseYHistory,unsigned long * MouseBHistory,int Histptr)56 ComShakeCut::Move( SDL_keysym *KeyHistory, long *MouseXHistory,
57 		 long *MouseYHistory, unsigned long *MouseBHistory,
58 		 int Histptr ) {
59   double prevVx = m_vx;
60   double prevVy = m_vy;
61 
62   ShakeCut::Move( KeyHistory, MouseXHistory, MouseYHistory, MouseBHistory,
63 		Histptr );
64   Think();
65 
66 // Calc status
67   if ( hypot( m_vx-prevVx, m_vy-prevVy ) > 0.8-theRC->gameLevel*0.1 ) {
68     AddStatus(-1);
69   }
70 
71   return true;
72 }
73 
74 bool
Think()75 ComShakeCut::Think() {
76   double hitT;	// estimation time until ball reaches _hitX, _hitY
77   double mx;
78 
79   // If the ball status changes, change _hitX, _hitY
80   if ( _prevBallstatus != theBall.GetStatus() && theBall.GetStatus() >= 0 ){
81     Hitarea( _hitX, _hitY );
82 
83     _prevBallstatus = theBall.GetStatus();
84   }
85 
86   if ( theBall.GetVY() != 0.0 )
87     hitT = (_hitY - theBall.GetY())/theBall.GetVY()-TICK;
88   else
89     hitT = -1.0;
90 
91   if ( fabs( _hitX-(m_x+m_side*0.3) ) < fabs( _hitX-(m_x-m_side*0.3) ) ||
92        theBall.GetStatus() == 8 || _hitX*m_side > 0 )
93     mx = m_x+m_side*0.3;
94   else
95     mx = m_x-m_side*0.3;
96 
97   if ( m_swing > 10 && m_swing <= 20 ) {
98   } else {
99     if ( hitT > 0.0 ) {
100       double vx = (_hitX-mx)/hitT;
101       if ( vx > m_vx+0.1 )
102 	m_vx += 0.1;
103       else if ( vx < m_vx-0.1 )
104 	m_vx -= 0.1;
105       else
106 	m_vx = vx;
107     } else {
108       if ( m_vx*fabs(m_vx*0.1)/2 < _hitX - mx )
109 	m_vx += 0.1;
110       else
111 	m_vx -= 0.1;
112     }
113   }
114 
115   if ( m_swing > 10 && m_swing <= 20 ) {
116   } else {
117     if ( hitT > 0.0 ) {
118       double vy = (_hitY-m_y)/hitT;
119       if ( vy > m_vy+0.1 )
120 	m_vy += 0.1;
121       else if ( vy < m_vy-0.1 )
122 	m_vy -= 0.1;
123       else
124 	m_vy = vy;
125     } else {
126       if ( m_vy*fabs(m_vy*0.1)/2 < _hitY - m_y )
127 	m_vy += 0.1;
128       else
129 	m_vy -= 0.1;
130     }
131   }
132 
133   if ( m_swing == 19 ) {
134     Player *opponent;
135     if ( m_side == -1 )
136       opponent = Control::TheControl()->GetThePlayer();
137     else
138       opponent = Control::TheControl()->GetComPlayer();
139 
140     SetTargetX( opponent );
141   }
142 
143   if ( (theBall.GetStatus() == 0 && m_side == -1) ||
144        (theBall.GetStatus() == 1 && m_side == -1) ||
145        (theBall.GetStatus() == 2 && m_side == 1) ||
146        (theBall.GetStatus() == 3 && m_side == 1) ||
147        (theBall.GetStatus() == 4 && m_side == -1) ||
148        (theBall.GetStatus() == 5 && m_side == 1) ) {
149     if ( m_vx > 5.0 )
150       m_vx = 5.0;
151     else if ( m_vx < -5.0 )
152       m_vx = -5.0;
153     if ( m_vy > 5.0 )
154       m_vy = 5.0;
155     else if ( m_vy < -5.0 )
156       m_vy = -5.0;
157   } else {
158     if ( hypot( m_vx, m_vy ) >= 2.0 ) {
159       double v = hypot( m_vx, m_vy );
160       m_vx = m_vx/v*1.99;
161       m_vy = m_vy/v*1.99;
162     }
163   }
164 
165   // Toss
166   if ( theBall.GetStatus() == 8 &&
167        ( (Control::TheControl()->IsPlaying() &&
168 	  ((PlayGame *)Control::TheControl())->GetService() == GetSide()) ||
169 	 (!Control::TheControl()->IsPlaying() && GetSide() == 1) ) &&
170        fabs(m_vx) < 0.1 && fabs(m_vy) < 0.1 &&
171        fabs(m_x+m_side*0.3-_hitX) < 0.1 && fabs(m_y-_hitY) < 0.1 &&
172        m_swing == 0 ) {
173     theBall.Toss( this, 2 );
174     StartSwing( 3 );
175     m_targetY = TABLELENGTH/6*m_side;
176 
177     return true;
178   }
179 
180   if ( fabs( theBall.GetY()+theBall.GetVY()*0.1 - _hitY ) < 0.2 /*&&
181 	  m_swing == 0*/ ){
182     // Calc the ball location of 0.1 second later.
183     // This part seems to be the same as Swing(). Consider again.
184     Ball *tmpBall;
185     double tmpBallX, tmpBallY, tmpBallZ;
186     double tmpX, tmpY;
187 
188     tmpBall = new Ball( theBall.GetX(), theBall.GetY(), theBall.GetZ(),
189 			theBall.GetVX(), theBall.GetVY(), theBall.GetVZ(),
190 			theBall.GetSpin(), theBall.GetStatus() );
191 
192     for ( int i = 0 ; i < 9 ; i++ )
193       tmpBall->Move();
194     tmpX = m_x+m_vx*0.08;
195     tmpY = m_y+m_vy*0.08;
196 
197     if ( ((tmpBall->GetStatus() == 3 && m_side == 1) ||
198 	  (tmpBall->GetStatus() == 1 && m_side == -1)) &&
199 	 (tmpY-tmpBall->GetY())*m_side < 0.3 &&
200 	 (tmpY-tmpBall->GetY())*m_side > -0.6 &&
201 	 m_swing <= 10 ) {
202       tmpBallX = tmpBall->GetX();
203       tmpBallY = tmpBall->GetY();
204       tmpBallZ = tmpBall->GetZ();
205 
206       // If the ball location becomes better at 1/100 second later, wait.
207       tmpBall->Move();
208       if ( fabs(tmpY+m_vy*0.01-tmpBall->GetY()) < fabs(tmpY-tmpBallY) &&
209 	   fabs(tmpY-tmpBallY) > LEVELMARGIN ) {
210 	delete tmpBall;
211 	return true;
212       }
213 
214       _hitX = tmpBallX;
215       _hitY = tmpBallY;
216 
217       if ( (tmpBallZ-TABLEHEIGHT)/fabs(m_y - m_targetY) < 0.0 )
218 	m_targetY = TABLELENGTH/4*m_side;
219       else if ( (tmpBallZ-TABLEHEIGHT)/fabs(m_y-m_targetY) < 0.1 )
220 	m_targetY = TABLELENGTH/3*m_side;
221       else
222 	m_targetY = TABLELENGTH/16*6*m_side;
223 
224       if ( (m_x-tmpBallX)*m_side < 0 )
225 	Swing(3);
226       else
227 	Swing(1);
228 
229       m_pow = 7 + theRC->gameLevel;
230     }
231     delete tmpBall;
232   }
233 
234   return true;
235 }
236 
237 // Calc hit point
238 // (1) If the ball haven't bound, calc bound point
239 // (2) Calc hit point from current ball location or bound location
240 bool
Hitarea(double & hitX,double & hitY)241 ComShakeCut::Hitarea( double &hitX, double &hitY ) {
242   Ball *tmpBall;
243   double max = -1.0;             /* highest point of the ball */
244   double bestX = AREAXSIZE, bestY = AREAYSIZE;
245 
246   if ( (theBall.GetStatus() == 3 && m_side == 1) ||
247        (theBall.GetStatus() == 2 && m_side == 1) ||
248        (theBall.GetStatus() == 0 && m_side == -1) ||
249        (theBall.GetStatus() == 1 && m_side == -1) ||
250        (theBall.GetStatus() == 4 && m_side == -1) ||
251        (theBall.GetStatus() == 5 && m_side == 1) ) {
252     tmpBall = new Ball( theBall.GetX(), theBall.GetY(), theBall.GetZ(),
253 			theBall.GetVX(), theBall.GetVY(), theBall.GetVZ(),
254 			theBall.GetSpin(), theBall.GetStatus() );
255 
256     while ( tmpBall->GetStatus() != -1 ) {
257       if ( (tmpBall->GetStatus() == 3 && m_side == 1) ||
258 	   (tmpBall->GetStatus() == 1 && m_side == -1) ) {
259 	if ( tmpBall->GetZ() > max ) {
260 	  max = tmpBall->GetZ();
261 	  if ( tmpBall->GetY() < TABLELENGTH/2 ) {
262 	    bestX = tmpBall->GetX();
263 	    bestY = tmpBall->GetY();
264 	  }
265 	}
266 	if ( tmpBall->GetVZ() < 0.0 && tmpBall->GetY() > TABLELENGTH/2 &&
267 	     tmpBall->GetZ() > TABLEHEIGHT ) {
268 	  bestX = tmpBall->GetX();
269 	  bestY = tmpBall->GetY();
270 	}
271       }
272       tmpBall->Move();
273     }
274 
275     delete tmpBall;
276 
277     if ( bestX != AREAXSIZE && bestY != AREAYSIZE ) {
278       hitX = bestX;
279       hitY = bestY;
280     }
281   } else if ( theBall.GetStatus() == 8 ) {
282     if ( (Control::TheControl()->IsPlaying() &&
283 	  ((PlayGame *)Control::TheControl())->GetService() == GetSide()) ||
284 	 GetSide() == 1 ) {
285       if ( RAND(2) )
286 	hitX = m_targetX;
287       else
288 	hitX = -m_targetX;
289     } else
290       hitX = 0.0;
291     hitY = -(TABLELENGTH/2+0.2)*m_side;
292   } else if ( theBall.GetStatus() < 6 ) {
293     hitX = 0.0;
294     hitY = -(TABLELENGTH/2+1.0)*m_side;
295   }
296 
297   return true;
298 }
299 
300 bool
SetTargetX(Player * opponent)301 ComShakeCut::SetTargetX( Player *opponent ) {
302   double width;
303 
304   switch ( theRC->gameLevel ) {
305   case LEVEL_EASY:
306     width = TABLEWIDTH/4;
307     break;
308   case LEVEL_NORMAL:
309     width = TABLEWIDTH/2;
310     break;
311   case LEVEL_HARD:
312   case LEVEL_TSUBORISH:
313     width = TABLEWIDTH;
314     break;
315   default:
316     return false;
317   }
318 
319   if ( opponent->GetPlayerType() == PLAYER_PENDRIVE ) {
320     switch ( RAND(4) ) {
321     case 0:
322       m_targetX = -width*7/16;
323       break;
324     case 1:
325       m_targetX = -width*5/16;
326       break;
327     case 2:
328       m_targetX = -width*3/16;
329       break;
330     case 3:
331       m_targetX = -width*1/16;
332       break;
333     }
334   } else {
335     switch ( RAND(8) ) {
336     case 0:
337       m_targetX = -width*7/16;
338       break;
339     case 1:
340       m_targetX = width*5/16;
341       break;
342     case 2:
343       m_targetX = -width*3/16;
344       break;
345     case 3:
346       m_targetX = -width*1/16;
347       break;
348     case 4:
349       m_targetX = width*1/16;
350       break;
351     case 5:
352       m_targetX = width*3/16;
353       break;
354     case 6:
355       m_targetX = width*5/16;
356       break;
357     case 7:
358       m_targetX = width*7/16;
359       break;
360     }
361   }
362 
363   if ( theRC->gameLevel == LEVEL_TSUBORISH ) {
364     if ( opponent->GetX()+opponent->GetVX()*0.5 < 0.0 ) {
365       m_targetX = width*7/16;
366     } else {
367       m_targetX = -width*7/16;
368     }
369 
370     if ( RAND(4) == 0 ) {
371       m_targetX = -m_targetX;
372     }
373   }
374 
375   if ( m_vx > 1.5 ) {
376     m_targetX += TABLEWIDTH/2;
377   } else if ( m_vx > 0.5 ) {
378     m_targetX += TABLEWIDTH/4;
379   } else if ( m_vx < -1.5 ) {
380     m_targetX -= TABLEWIDTH/2;
381   } else if ( m_vx < -0.5 ) {
382     m_targetX -= TABLEWIDTH/4;
383   }
384 
385   if ( m_targetX > TABLEWIDTH/2 )
386     m_targetX = TABLEWIDTH*7/16;
387   if ( m_targetX < -TABLEWIDTH/2 )
388     m_targetX = -TABLEWIDTH*7/16;
389 
390   return true;
391 }
392