1 /* $Id: PenAttack.cpp,v 1.27 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 "PenAttack.h"
21 #include "Ball.h"
22 #include "Event.h"
23 #include "Network.h"
24 #include "Control.h"
25 #include "RCFile.h"
26 
27 extern RCFile *theRC;
28 
29 extern Ball   theBall;
30 
31 extern long mode;
32 
PenAttack()33 PenAttack::PenAttack() {
34   m_playerType = PLAYER_PENATTACK;
35 }
36 
PenAttack(long side)37 PenAttack::PenAttack(long side) : Player(side) {
38   m_playerType = PLAYER_PENATTACK;
39 }
40 
PenAttack(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)41 PenAttack::PenAttack( long playerType, long side, double x, double y, double z,
42 		      double vx, double vy, double vz,long status, long swing,
43 		      long swingType, bool swingSide, long afterSwing,
44 		      long swingError,
45 		      double targetX, double targetY, double eyeX, double eyeY,
46 		      double eyeZ, long pow, double spin, double stamina,
47 		      long statusMax ) :
48   Player( playerType, side, x, y, z, vx, vy, vz, status, swing, swingType,
49 	  swingSide, afterSwing, swingError, targetX, targetY,
50 	  eyeX, eyeY, eyeZ, pow, spin, stamina, statusMax ) {
51 }
52 
~PenAttack()53 PenAttack::~PenAttack() {
54 }
55 
56 bool
AddStatus(long diff)57 PenAttack::AddStatus( long diff ) {
58   // Add something in the future
59   return Player::AddStatus( diff );
60 }
61 
62 bool
Move(SDL_keysym * KeyHistory,long * MouseXHistory,long * MouseYHistory,unsigned long * MouseBHistory,int Histptr)63 PenAttack::Move( SDL_keysym *KeyHistory, long *MouseXHistory,
64 	      long *MouseYHistory, unsigned long *MouseBHistory,
65 	      int Histptr ) {
66   double prevVx = m_vx;
67   double prevVy = m_vy;
68 
69   Player::Move( KeyHistory, MouseXHistory, MouseYHistory,MouseBHistory,
70 		Histptr );
71 
72 // Calc status
73   if ( hypot( m_vx, m_vy ) < 1.0 ) {
74       AddStatus( 1 );
75   }
76   if ( hypot( m_vx-prevVx, m_vy-prevVy ) > 0.8-theRC->gameLevel*0.1 ) {
77     AddStatus(-1);
78   }
79 
80   return true;
81 }
82 
83 bool
Swing(long spin)84 PenAttack::Swing( long spin ) {
85   Ball *tmpBall;
86 
87   if ( theBall.GetStatus() == 6 || theBall.GetStatus() == 7 )
88     return false;
89 
90   if ( m_swing > 10 && m_swing < 30 )
91     return false;
92 
93   if ( m_swing >= 30 )
94     AddStatus( -(50-m_swing)*2 );
95 
96   m_swing = 11;
97   m_pow = 8;
98 
99   // Decide SwingType by the hit point and spin, etc.
100   // Calc the ball location of 0.1 second later
101   tmpBall = new Ball( theBall.GetX(), theBall.GetY(), theBall.GetZ(),
102 		      theBall.GetVX(), theBall.GetVY(), theBall.GetVZ(),
103 		      theBall.GetSpin(), theBall.GetStatus() );
104 
105   for ( int i = 0 ; i < 10 ; i++ )
106     tmpBall->Move();
107 
108   if ( spin < 3 )
109     m_swingSide = false;
110   else
111     m_swingSide = true;
112 
113   SwingType( tmpBall, spin );
114 
115   delete tmpBall;
116 
117   if (Control::TheControl()->GetThePlayer() == this && mode == MODE_MULTIPLAY)
118     ::SendSwing( this );
119 
120   return true;
121 }
122 
123 bool
StartSwing(long spin)124 PenAttack::StartSwing( long spin ) { // Argument is valid only on serve
125   Ball *tmpBall;
126 
127   if ( m_swing > 10 )
128     return false;
129 
130   if ( m_swing == 0 ){
131     m_swing = 1;
132     m_pow = 0;
133 
134     // Decide SwingType by the hit point and spin, etc.
135     // Calc the ball location of 0.2 second later
136     tmpBall = new Ball( theBall.GetX(), theBall.GetY(), theBall.GetZ(),
137 			theBall.GetVX(), theBall.GetVY(), theBall.GetVZ(),
138 			theBall.GetSpin(), theBall.GetStatus() );
139 
140     for ( int i = 0 ; i < 20 ; i++ )
141       tmpBall->Move();
142 
143     if ( (theBall.GetStatus() == 6 && m_side == 1) ||
144 	(theBall.GetStatus() == 7 && m_side == -1) ){	// Serve
145       switch ( spin-1 ) {
146       case 0:
147 	m_spin = 0.2;	// straight
148 	m_swingType = SWING_NORMAL;
149 	break;
150       case 1:
151 	m_spin = -0.1;	// knuckle
152 	m_swingType = SWING_POKE;
153 	break;
154       case 2:
155 	m_spin = -0.6;
156 	m_swingType = SWING_POKE;
157 	break;
158       }
159 
160       m_swingSide = true;
161 
162       if ( Control::TheControl()->GetThePlayer() == this &&
163 	   mode == MODE_MULTIPLAY )
164 	::SendSwing( this );
165     } else {
166       if ( (m_x-tmpBall->GetX())*m_side > 0 )
167 	m_swingSide = false;
168       else
169 	m_swingSide = true;
170 
171       SwingType( tmpBall, spin );
172     }
173 
174     delete tmpBall;
175   }
176 
177   return true;
178 }
179 
180 bool
HitBall()181 PenAttack::HitBall() {
182   double vx, vy, vz;
183   double diff;
184   double level;
185 
186   // Serve
187   if ( ( (m_side == 1 && theBall.GetStatus() == 6) ||
188          (m_side ==-1 && theBall.GetStatus() == 7) ) &&
189        fabs( m_x-theBall.GetX() ) < 0.6 && fabs( m_y-theBall.GetY() ) < 0.3 ){
190     AddStatus( (long)-fabs(fabs(m_x-theBall.GetX())-0.3F)*100 );
191     diff = fabs( m_y-theBall.GetY() )*0.3;
192 
193     SwingError();
194 
195     if ( fabs(m_targetY) < TABLELENGTH/16*2 )
196       level = 0.95 - diff*1.0;
197     else if ( fabs(m_targetY) < TABLELENGTH/16*4 )
198       level = 0.93-diff*1.5;
199     else if ( fabs(m_targetY) < TABLELENGTH/16*6 )
200       level = 0.90-diff*2.0;
201     else
202       level = 0.90-diff*2.0;
203 
204     theBall.TargetToVS( m_targetX, m_targetY, level, m_spin, vx, vy, vz );
205 
206     theBall.Hit( vx, vy, vz, m_spin, this );
207   } else if ( ((m_side == 1 && theBall.GetStatus() == 3) ||
208 	       (m_side ==-1 && theBall.GetStatus() == 1)) &&
209 	      fabs(m_x-theBall.GetX()) < 0.6 &&
210 	      ((GetSwingSide() && (m_x-theBall.GetX())*m_side < 0 ) ||
211 	       (!GetSwingSide() && (m_x-theBall.GetX())*m_side > 0 )) &&
212 	      (m_y-theBall.GetY())*m_side < 0.3 &&
213 	      (m_y-theBall.GetY())*m_side > -0.6 ) {
214 
215     double targetX, targetY;
216 
217     GetModifiedTarget( targetX, targetY );
218 
219     double maxVy;
220     CalcLevel( &theBall, diff, level, maxVy );
221 
222     theBall.TargetToV( targetX, targetY, level, m_spin, vx, vy, vz,
223 		       0.1, maxVy );
224 
225     // If your target is near the edge of the table, much more status point
226     // is necessary.
227 
228     if ((m_status-StatusBorder())*3/2000.0 <
229 	fabs(fabs(m_x-theBall.GetX())-0.3))
230       AddError( vx, vy, vz );
231 
232     // Reduce status
233     m_afterSwing = (long)
234       (hypot( theBall.GetVX()*0.8-vx, theBall.GetVY()*0.8+vy )
235        * (1.0+diff*10.0) + fabs(m_spin)*5.0 + fabs(theBall.GetSpin())*4.0);
236 
237     if ( ForeOrBack() || m_swingType == SWING_POKE )
238       AddStatus( -m_afterSwing*2 );
239     else
240       AddStatus( -m_afterSwing*3 );
241 
242     if ( m_status == 1 )
243       m_afterSwing *= 3;
244 
245     theBall.Hit( vx, vy, vz, m_spin, this );
246   } else {
247     m_swingError = SWING_MISS;
248     if ( Control::TheControl()->GetThePlayer() == this &&
249 	 mode == MODE_MULTIPLAY )
250       Event::TheEvent()->SendBall();
251   }
252 
253   return true;
254 }
255 
256 bool
SwingType(Ball * ball,long spin)257 PenAttack::SwingType( Ball *ball, long spin ) {
258   if ( (ball->GetStatus() == 3 && m_side == 1) ||
259        (ball->GetStatus() == 1 && m_side == -1) ) {
260     if ( fabs(ball->GetX()) < TABLEWIDTH/2 &&
261 	 fabs(ball->GetY()) < TABLELENGTH/2 &&
262 	 (ball->GetZ()-TABLEHEIGHT-NETHEIGHT)/fabs(ball->GetY()) <
263 	 NETHEIGHT/(TABLELENGTH/2)*0.5 ){	// low ball on the table
264       if ( ball->GetSpin() < 0 ){
265 	m_swingType = SWING_POKE;
266 	//m_spin = -spin*0.2-0.4;
267 	m_spin = -0.8;
268       } else{
269 	m_swingType = SWING_NORMAL;
270 	//m_spin = spin*0.2;
271 	m_spin = 0.4;
272       }
273     } else if ( ball->GetZ() < TABLEHEIGHT+NETHEIGHT ) { // under the net
274       if ( ForeOrBack() ) {
275 	m_swingType = SWING_DRIVE;
276 	//m_spin = spin*0.2+0.4;
277 	m_spin = 0.8;
278       } else {
279 	if ( ball->GetSpin() < 0 ) {
280 	  m_swingType = SWING_POKE;
281 	  //m_spin = -spin*0.2-0.4;
282 	  m_spin = -0.8;
283 	} else {
284 	  m_swingType = SWING_NORMAL;
285 	  //m_spin = spin*0.2;
286 	  m_spin = 0.4;
287 	}
288       }
289     } else if ( fabs(ball->GetY()) < TABLELENGTH/2+1.0 &&
290 		ball->GetZ() > TABLEHEIGHT+NETHEIGHT ){
291       m_swingType = SWING_SMASH;
292       m_spin = 0.2;
293     } else {
294       m_swingType = SWING_NORMAL;
295       //m_spin = spin*0.2;
296       m_spin = 0.4;
297     }
298   } else{
299     m_swingType = SWING_NORMAL;
300     //m_spin = spin*0.2;
301     m_spin = 0.4;
302   }
303 
304   return true;
305 }
306 
307 // Target will be modified by the spin
308 // (now invalid)
309 #if 0
310 bool
311 PenAttack::GetModifiedTarget( double &targetX, double &targetY ) {
312   targetX = m_targetX;
313   targetY = m_targetY + theBall.GetSpin()*m_side*0.5;
314 
315   return true;
316 }
317 #else
318 bool
GetModifiedTarget(double & targetX,double & targetY)319 PenAttack::GetModifiedTarget( double &targetX, double &targetY ) {
320   targetX = m_targetX;
321   targetY = m_targetY;
322 
323   return true;
324 }
325 #endif
326 
327 void
CalcLevel(Ball * ball,double & diff,double & level,double & maxVy)328 PenAttack::CalcLevel(Ball *ball, double &diff, double &level, double &maxVy) {
329   double targetX, targetY;
330 
331   GetModifiedTarget( targetX, targetY );
332 
333   if ( (m_y-ball->GetY())*m_side < 0 )
334     diff = fabs( m_y-ball->GetY() )*0.1;
335   else
336     diff = fabs( m_y-ball->GetY() )*0.15;
337 
338   diff *= fabs(ball->GetSpin())+1;
339 
340   SwingError();
341 
342   level = 1 - fabs(targetY)/(TABLELENGTH/16)/40 -
343     diff*fabs(targetY)/(TABLELENGTH/16);
344 
345   level *= (double)m_pow/20.0 + 0.5;
346 
347   if ( ForeOrBack() ) {
348     switch ( m_swingType ) {
349     case SWING_CUT:
350     case SWING_POKE:
351     case SWING_NORMAL:
352     case SWING_DRIVE:
353       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 15.0 -
354 	(fabs(m_spin)+fabs(ball->GetSpin()))*3.0;
355       break;
356     case SWING_SMASH:
357       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 25.0 -
358 	(fabs(m_spin)+fabs(ball->GetSpin()))*3.0;
359       break;
360     default:
361       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 15.0 -
362 	(fabs(m_spin)+fabs(ball->GetSpin()))*3.0;
363     }
364   } else {
365     switch ( m_swingType ) {
366     case SWING_CUT:
367     case SWING_POKE:
368     case SWING_NORMAL:
369     case SWING_DRIVE:
370       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 12.0 -
371 	(fabs(m_spin)+fabs(ball->GetSpin()))*4.0;
372       break;
373     case SWING_SMASH:
374       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 18.0 -
375 	(fabs(m_spin)+fabs(ball->GetSpin()))*4.0;
376       break;
377     default:
378       maxVy = hypot(ball->GetVX(), ball->GetVY())*0.6 + 12.0 -
379 	(fabs(m_spin)+fabs(ball->GetSpin()))*4.0;
380     }
381   }
382 
383   if ( level > 1.0 )
384     level = 1.0;
385 }
386