1 /* $Id: comPenAttack.cpp,v 1.24 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 "comPenAttack.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 
ComPenAttack()31 ComPenAttack::ComPenAttack() : PenAttack(), ComPlayer() {
32 }
33 
ComPenAttack(long side)34 ComPenAttack::ComPenAttack(long side) : PenAttack(side), ComPlayer() {
35 }
36 
ComPenAttack(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 ComPenAttack::ComPenAttack( 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   PenAttack( 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 
~ComPenAttack()52 ComPenAttack::~ComPenAttack() {
53 }
54 
55 bool
Move(SDL_keysym * KeyHistory,long * MouseXHistory,long * MouseYHistory,unsigned long * MouseBHistory,int Histptr)56 ComPenAttack::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   PenAttack::Move( KeyHistory, MouseXHistory, MouseYHistory, MouseBHistory,
63 		Histptr );
64 
65   Think();
66 
67 // Calc status
68   if ( hypot( m_vx-prevVx, m_vy-prevVy ) > 0.8-theRC->gameLevel*0.1 ) {
69     AddStatus(-1);
70   }
71 
72   return true;
73 }
74 
75 bool
Think()76 ComPenAttack::Think() {
77   double hitT;	// estimation time until ball reaches _hitX, _hitY
78   double mx;
79 
80   // If the ball status changes, change _hitX, _hitY
81   if ( _prevBallstatus != theBall.GetStatus() && theBall.GetStatus() >= 0 ){
82     Hitarea( _hitX, _hitY );
83 
84     _prevBallstatus = theBall.GetStatus();
85   }
86 
87   if ( theBall.GetVY() != 0.0 )
88     hitT = (_hitY - theBall.GetY())/theBall.GetVY()-TICK;
89   else
90     hitT = -1.0;
91 
92   if ( theBall.GetStatus() == 8 ||
93        fabs( _hitX-(m_x+m_side*0.3) ) < fabs( _hitX-(m_x-m_side*0.3) ) )
94     mx = m_x+m_side*0.3;
95   else
96     mx = m_x-m_side*0.3;
97 
98   if ( m_swing > 10 && m_swing <= 20 ) {
99   } else {
100     if ( hitT > 0.0 ) {
101       double vx = (_hitX-mx)/hitT;
102       if ( vx > m_vx+0.1 )
103 	m_vx += 0.1;
104       else if ( vx < m_vx-0.1 )
105 	m_vx -= 0.1;
106       else
107 	m_vx = vx;
108     } else {
109       if ( m_vx*fabs(m_vx*0.1)/2 < _hitX - mx )
110 	m_vx += 0.1;
111       else
112 	m_vx -= 0.1;
113     }
114   }
115 
116   if ( m_swing > 10 && m_swing <= 20 ) {
117   } else {
118     if ( hitT > 0.0 ) {
119       double vy = (_hitY-m_y)/hitT;
120       if ( vy > m_vy+0.1 )
121 	m_vy += 0.1;
122       else if ( vy < m_vy-0.1 )
123 	m_vy -= 0.1;
124       else
125 	m_vy = vy;
126     } else {
127       if ( m_vy*fabs(m_vy*0.1)/2 < _hitY - m_y )
128 	m_vy += 0.1;
129       else
130 	m_vy -= 0.1;
131     }
132   }
133 
134   if ( m_swing == 19 ) {
135     Player *opponent;
136     if ( m_side == -1 )
137       opponent = Control::TheControl()->GetThePlayer();
138     else
139       opponent = Control::TheControl()->GetComPlayer();
140 
141     SetTargetX( opponent );
142   }
143 
144   if ( (theBall.GetStatus() == 0 && m_side == -1) ||
145        (theBall.GetStatus() == 1 && m_side == -1) ||
146        (theBall.GetStatus() == 2 && m_side == 1) ||
147        (theBall.GetStatus() == 3 && m_side == 1) ||
148        (theBall.GetStatus() == 4 && m_side == -1) ||
149        (theBall.GetStatus() == 5 && m_side == 1) ) {
150     if ( m_vx > 5.0 )
151       m_vx = 5.0;
152     else if ( m_vx < -5.0 )
153       m_vx = -5.0;
154     if ( m_vy > 5.0 )
155       m_vy = 5.0;
156     else if ( m_vy < -5.0 )
157       m_vy = -5.0;
158   } else {
159     if ( hypot( m_vx, m_vy ) >= 1.0 ) {
160       double v = hypot( m_vx, m_vy );
161       m_vx = m_vx/v*1.0;
162       m_vy = m_vy/v*1.0;
163     }
164   }
165 
166   // Toss
167 #ifdef SCREENSHOT
168   if ( theBall.GetStatus() == 8 &&
169        ( (Control::TheControl()->IsPlaying() &&
170 	  ((PlayGame *)Control::TheControl())->GetService() == GetSide()) ) &&
171        fabs(m_vx) < 0.1 && fabs(m_vy) < 0.1 &&
172        fabs(m_x+m_side*0.3-_hitX) < 0.1 && fabs(m_y-_hitY) < 0.1 &&
173        m_swing == 0 ){
174 #else
175   if ( theBall.GetStatus() == 8 &&
176        ( (Control::TheControl()->IsPlaying() &&
177 	  ((PlayGame *)Control::TheControl())->GetService() == GetSide()) ||
178 	 (!Control::TheControl()->IsPlaying() && GetSide() == 1) ) &&
179        fabs(m_vx) < 0.1 && fabs(m_vy) < 0.1 &&
180        fabs(m_x+m_side*0.3-_hitX) < 0.1 && fabs(m_y-_hitY) < 0.1 &&
181        m_swing == 0 ){
182 #endif
183     theBall.Toss( this, 2 );
184     StartSwing( 3 );
185     m_targetY = TABLELENGTH/6*m_side;
186 
187     return true;
188   }
189 
190   if ( fabs( theBall.GetY()+theBall.GetVY()*0.1 - _hitY ) < 0.2 /*&&
191        m_swing == 0*/ ){
192     // Calc the ball location of 0.1 second later.
193     // This part seems to be the same as Swing(). Consider again.
194     Ball *tmpBall;
195     double tmpBallX, tmpBallY, tmpBallZ;
196     double tmpX, tmpY;
197 
198     tmpBall = new Ball( theBall.GetX(), theBall.GetY(), theBall.GetZ(),
199 			theBall.GetVX(), theBall.GetVY(), theBall.GetVZ(),
200 			theBall.GetSpin(), theBall.GetStatus() );
201 
202     for ( int i = 0 ; i < 9 ; i++ )
203       tmpBall->Move();
204     tmpX = m_x+m_vx*0.08;
205     tmpY = m_y+m_vy*0.08;
206 
207     if ( ((tmpBall->GetStatus() == 3 && m_side == 1) ||
208 	  (tmpBall->GetStatus() == 1 && m_side == -1)) &&
209 	 (tmpY-tmpBall->GetY())*m_side < 0.3 &&
210 	 (tmpY-tmpBall->GetY())*m_side > -0.6 &&
211 	 m_swing <= 10 ) {
212 
213       tmpBallX = tmpBall->GetX();
214       tmpBallY = tmpBall->GetY();
215       tmpBallZ = tmpBall->GetZ();
216 
217       // If the ball location becomes better at 1/100 second later, wait.
218       tmpBall->Move();
219       if ( fabs(tmpY+m_vy*0.01-tmpBall->GetY()) < fabs(tmpY-tmpBallY) &&
220 	   fabs(tmpY-tmpBallY) > LEVELMARGIN ) {
221 	delete tmpBall;
222 	return true;
223       }
224 
225       _hitX = tmpBallX;
226       _hitY = tmpBallY;
227 
228       if ( (tmpBallZ-TABLEHEIGHT)/fabs(tmpBallY-m_targetY) < 0.0 )
229 	m_targetY = TABLELENGTH/4*m_side;
230       else if ( (tmpBallZ-TABLEHEIGHT)/fabs(tmpBallY-m_targetY) < 0.1 )
231 	m_targetY = TABLELENGTH/3*m_side;
232       else
233 	m_targetY = TABLELENGTH/16*6*m_side;
234 
235       if ( (m_x-tmpBallX)*m_side < 0 )
236 	Swing(3);
237       else
238 	Swing(1);
239 
240       m_pow = 7 + theRC->gameLevel;
241     }
242     delete tmpBall;
243   }
244 
245   return true;
246 }
247 
248 // Calc hit point
249 // (1) If the ball hasn't bound, calc bound point
250 // (2) Calc hit point from current ball location or bound location
251 bool
252 ComPenAttack::Hitarea( double &hitX, double &hitY ) {
253   double max = -1.0;             /* highest point of the ball */
254   double maxX = 0.0, maxY = 0.0;
255 
256   if ( (theBall.GetStatus() == 3 && m_side == 1) ||
257        (theBall.GetStatus() == 2 && m_side == 1) ||
258        (theBall.GetStatus() == 0 && m_side == -1) ||
259        (theBall.GetStatus() == 1 && m_side == -1) ||
260        (theBall.GetStatus() == 4 && m_side == -1) ||
261        (theBall.GetStatus() == 5 && m_side == 1) ) {
262     max = GetBallTop( maxX, maxY, this );
263 
264     if ( max > 0 ) {
265       hitX = maxX;
266       hitY = maxY;
267     }
268   } else if ( theBall.GetStatus() == 8 ) {
269     if ( (Control::TheControl()->IsPlaying() &&
270 	  ((PlayGame *)Control::TheControl())->GetService() == GetSide()) ||
271 	 GetSide() == 1 ) {
272       if ( RAND(2) )
273 	hitX = m_targetX;
274       else
275 	hitX = -m_targetX;
276     } else
277       hitX = 0.0;
278     hitY = -(TABLELENGTH/2+0.2)*m_side;
279   } else if ( theBall.GetStatus() < 6 ) {
280     hitX = 0.0;
281     hitY = -(TABLELENGTH/2+0.5)*m_side;
282   }
283 
284   return true;
285 }
286 
287 bool
288 ComPenAttack::SetTargetX( Player *opponent ) {
289   double width;
290 
291   switch ( theRC->gameLevel ) {
292   case LEVEL_EASY:
293     width = TABLEWIDTH/4;
294     break;
295   case LEVEL_NORMAL:
296     width = TABLEWIDTH/2;
297     break;
298   case LEVEL_HARD:
299   case LEVEL_TSUBORISH:
300     width = TABLEWIDTH;
301     break;
302   default:
303     return false;
304   }
305 
306   if ( opponent->GetPlayerType() == PLAYER_PENDRIVE ) {
307     switch ( RAND(4) ) {
308     case 0:
309       m_targetX = -width*7/16;
310       break;
311     case 1:
312       m_targetX = -width*5/16;
313       break;
314     case 2:
315       m_targetX = -width*3/16;
316       break;
317     case 3:
318       m_targetX = -width*1/16;
319       break;
320     }
321   } else {
322     switch ( RAND(8) ) {
323     case 0:
324       m_targetX = -width*7/16;
325       break;
326     case 1:
327       m_targetX = -width*5/16;
328       break;
329     case 2:
330       m_targetX = -width*3/16;
331       break;
332     case 3:
333       m_targetX = -width*1/16;
334       break;
335     case 4:
336       m_targetX = width*1/16;
337       break;
338     case 5:
339       m_targetX = width*3/16;
340       break;
341     case 6:
342       m_targetX = width*5/16;
343       break;
344     case 7:
345       m_targetX = width*7/16;
346       break;
347     }
348   }
349 
350   if ( theRC->gameLevel == LEVEL_TSUBORISH ) {
351     if ( opponent->GetX()+opponent->GetVX()*0.5 < 0.0 ) {
352       m_targetX = width*7/16;
353     } else {
354       m_targetX = -width*7/16;
355     }
356 
357     if ( RAND(4) == 0 ) {
358       m_targetX = -m_targetX;
359     }
360   }
361 
362   if ( m_vx > 1.5 ) {
363     m_targetX += TABLEWIDTH/2;
364   } else if ( m_vx > 0.5 ) {
365     m_targetX += TABLEWIDTH/4;
366   } else if ( m_vx < -1.5 ) {
367     m_targetX -= TABLEWIDTH/2;
368   } else if ( m_vx < -0.5 ) {
369     m_targetX -= TABLEWIDTH/4;
370   }
371 
372   if ( m_targetX > TABLEWIDTH/2 )
373     m_targetX = TABLEWIDTH*7/16;
374   if ( m_targetX < -TABLEWIDTH/2 )
375     m_targetX = -TABLEWIDTH*7/16;
376 
377   return true;
378 }
379