1 #ifndef _global_h
2 # include "global.h"
3 #endif
4
5 #ifndef _pball_h
6 # include "pball.h"
7 #endif
8 #ifndef _ball_h
9 # include "ball.h"
10 #endif
11 #ifndef _graph_h
12 # include "graph.h"
13 #endif
14 #ifndef _game_h
15 # include "game.h"
16 #endif
17
18 #define SHOW_MODE 1
19 #ifndef STATISTICS
20 # if (SHOW_MODE)
21 # undef SHOW_MODE
22 # define SHOW_MODE 0
23 # endif
24 #endif
25 #define NEW_FACTOR 2.0
26
27 static const int OLD = 1;
28 static const int NEW = 0;
29
30 PBallTop *PBallTop::pball_queue=0l;
31 int PBallTop::id_count = 0;
32
PBallTop(PBallType type)33 PBallTop::PBallTop(PBallType type):
34 aim_hint(1), hint_valid(0)
35 {
36 PBallTop::id = PBallTop::id_count++;
37 next = pball_queue;
38 pball_queue = this;
39
40 pball_type = type; /* 0-Queue 1-DiscSlider 2-DiscThrower */
41 target_time = NO_TARGET;
42
43 cue = 0l;
44 mode = Unlocked;
45 default_cue = new Ball( 0.0, 0.0, 0.0, 0.0, 0.1 );
46
47 tool_is_visible = 0;
48 valid_queue_position = 0;
49 }
50
51
~PBallTop()52 PBallTop::~PBallTop() {
53 PBallTop *last;
54 //
55 // Pointer aus Listen entfernen
56 //
57 if (pball_queue==this) pball_queue = next;
58 else {
59 last = pball_queue;
60 while ( last->next && last->next != this ) last = last->next;
61 last->next = next;
62 }
63 //
64 // eigenes aufr�umen
65 //
66 if (default_cue) delete default_cue;
67 }
68
69
CueMoved()70 void PBallTop::CueMoved() {
71 if (mode&UsingTool) MoveAimingTool();
72 }
73
CueWasHit()74 void PBallTop::CueWasHit() {
75 //
76 // Der cueball war an einer Kollision beteiligt und ver�nderte seine Richtung
77 // Im PullMoving-Mode bedeutet dies, das die Maus an eine andere Stelle
78 // gewarpt werden mu�, damit der cueball immer noch kontinuierlich in
79 // Mausrichtung gezogen wird.
80 //
81 if (mode&PullMoving) {
82 #ifndef __TURBOC__
83 if (target_time!=NO_TARGET) {
84 dest = cue->P()+cue->V()*(target_time-current_time);
85 Warp( dest ); // internen X-Pointer verschieben
86 SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
87 // Darstellung verschieben
88 }
89 #endif
90 }
91 }
92
93
WarpRecalc()94 void PBallTop::WarpRecalc() {
95 if (dest!=cue->P()) {
96 target_time = GetCurrentTime() + WarpTime; // Offset bis zum Zielpunkt
97 cue->ChgV(((dest-cue->P())/(target_time-current_time)));
98
99 #ifdef DEBUG
100 if (debug&PointerMove) {
101 printf( "PointerMove: P()=(%g,%g), V()=(%5.1f,%5.1f), dest=(%g, %g) (in %g secs)\n",
102 (double)cue->PX(), (double)cue->PY(),
103 (double)cue->VX(), (double)cue->VY(),
104 (double)dest.X(), (double)dest.Y(),
105 (double)(target_time-current_time) );
106 }
107 #endif
108 }
109 else {
110 target_time = NO_TARGET;
111 #ifdef DEBUG
112 if (debug&PointerMove) {
113 printf( "PointerMove: P()=(%g,%g) - destination reached\n",
114 (double)cue->PX(), (double)cue->PY() );
115 }
116 #endif
117 }
118 }
119
PointerMoveTo(const Vec2 & pointer_position)120 void PBallTop::PointerMoveTo( const Vec2 &pointer_position ) {
121 dest = pointer_position;
122 SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
123 if (mode&UsingTool) MoveAimingTool();
124 else if (mode&Moving) cue->SetP(dest);
125 else if (mode&PullMoving) {
126 WarpRecalc();
127 }
128 }
129
130
ForAll(PBallFun fun)131 void PBallTop::ForAll( PBallFun fun ) {
132 for (PBallTop *pball=pball_queue; pball; pball=pball->next) (pball->*fun)();
133 }
134
135
Press(int button)136 void PBallTop::Press(int button) {
137 switch(button) {
138 case 1:
139 switch(pball_type) {
140 case BillardQueue:
141 switch(mode) {
142 case Aiming: StartCharging(OLD); break;
143 case Charging: StartShooting(NEW); break;
144 case Unlocked: StartAiming(); break;
145 default: break;
146 }
147 break;
148 case DiscSlider:
149 case DiscThrower:
150 if (mode!=PullMoving) StartPullMoving();
151 break;
152 }
153 break;
154
155 #if (0)
156 case 2: // Middle Button
157 if (PBallTop::id==0) {
158 CloseGraphic();
159 exit(0);
160 }
161 break;
162 #endif
163
164 case 3: // Right Button
165 switch(mode) {
166 case Unlocked:
167 #if (1)
168 StartPullMoving();
169 #else
170 // PullMoving is not yet debugging in the DOS-version
171 StartMoving();
172 #endif
173 break;
174 case Aiming: StartCharging(NEW); break;
175 default:
176 break;
177 }
178 break; // case 3 - Right Button
179 }
180 }
181
182
Release(int button)183 void PBallTop::Release(int button) {
184 switch(button) {
185 case 1: // Left Button
186 switch(mode) {
187 case LockedQueue: GoUnlocked(); break;
188 case OldCharging: StartShooting(OLD); break;
189 case Shooting: RestartCharging(); break;
190 case PullMoving: if (pball_type==DiscThrower) StopMoving();
191 break;
192 default: break;
193 }
194 break;
195
196 case 3: // Right Button
197 switch(mode) {
198 case PullMoving: if (pball_type==DiscSlider) break;
199 case Moving: StopMoving(); break;
200 case Charging:
201 case OldCharging:
202 case Shooting:
203 case OldShooting:
204 GoUnlocked(); break;
205 default: break;
206 }
207 break;
208 }
209 }
210
211
Update()212 void PBallTop::Update() {
213 switch(mode) {
214 case OldCharging:
215 case Charging:
216 case OldShooting:
217 case Shooting:
218 charge += charge_increment*(current_time-last_chargestep);
219 last_chargestep=current_time;
220 next_chargestep=last_chargestep+g->GetChargeGranularity();
221
222 #ifndef __TURBOC__
223 #ifdef STATISTICS
224 { char buffer[30];
225 sprintf( buffer, "Charge: %5.1f%%", (double)(charge/g->GetMaxCharge()*100.0) );
226 showinfo(PointerInfo,buffer);
227 }
228 #endif
229 #endif
230 MoveAimingTool(); // Ausgabe des Ladezustands
231 if (mode==OldCharging) {
232 if (charge>g->GetMaxCharge()) {
233 charge = charge / 6.0; // �berlauf -> Kraft reduzieren
234 MoveAimingTool(); // Ausgabe des Ladezustands
235 StartShooting(OLD);
236 }
237 }
238 else {
239 if (charge<RealZero) {
240 Shoot();
241 }
242 else if (charge>g->GetMaxCharge()) charge=charge/2.0;
243 }
244 break;
245
246 case PullMoving:
247 if (target_time<current_time) {
248 WarpRecalc();
249 }
250 break;
251
252 case LockedQueue:
253 if (lock_time<current_time) {
254 GoUnlocked();
255 }
256 break;
257 default:
258 break;
259 }
260 }
261
Redraw()262 void PBallTop::Redraw() {
263 tool_is_visible = 0;
264 valid_queue_position = 0;
265 if (mode&UsingTool) DrawAimingTool();
266 RedrawPointer();
267 }
268
269
270 #if (0)
SetIndirect()271 void PBallTop::SetIndirect() {
272 mode=Indirect;
273 #if (0)
274 Draw();
275 m = cue->R()*R()*R()/8;
276 Draw();
277 #endif
278 #if (SHOW_MODE)
279 showinfo(PointerInfo,"Indirect ");
280 #endif
281 }
282
SetDirect()283 void PBallTop::SetDirect() {
284 mode=Direct;
285 target_time=NO_TARGET;
286 Draw();
287 m = 1;
288 Draw();
289 PointerMoveTo( dest );
290 Warp( dest );
291 #if (SHOW_MODE)
292 showinfo(PointerInfo,"Direct ");
293 #endif
294 }
295 #endif
296
FindCueBall()297 Ball *PBallTop::FindCueBall() {
298 Ball *best;
299 Real min_dist=Ball::FindClosest(cue,dest,&best);
300
301 if (min_dist<2.*best->R()) {
302 if (g->IsSelectable(best)) return best;
303 }
304 return 0;
305 }
306
307
StartAiming(Ball * cueball)308 void PBallTop::StartAiming( Ball *cueball ) {
309 if (cueball) cue = cueball;
310 else cue = FindCueBall(); // naechste Kugel suche
311 if (cue&&cue->Lock(this)) cue=0; // Lock erlaubt
312 if (!cue) return;
313
314 mode = Aiming;
315 aim_hint = 1;
316 charge = 0.0;
317 StartAimingTool();
318 #if (SHOW_MODE)
319 showinfo(PointerInfo,"Aiming ");
320 #endif
321 }
322
StartCharging(int old)323 void PBallTop::StartCharging( int old ) {
324 mode = (old)?OldCharging:Charging;
325 charge = 0.0;
326 if (old) charge_increment = g->GetChargeSpeed();
327 else charge_increment = g->GetChargeSpeed()/NEW_FACTOR;
328 last_chargestep = current_time;
329 next_chargestep = last_chargestep + g->GetChargeGranularity();
330 #if (SHOW_MODE)
331 showinfo(PointerInfo,"Charging ");
332 #endif
333 }
334
RestartCharging()335 void PBallTop::RestartCharging() {
336 mode = Charging;
337 charge_increment = g->GetChargeSpeed();
338 }
339
StartShooting(int old)340 void PBallTop::StartShooting( int old ) {
341 save_charge = charge;
342 if (old) charge_increment = -(charge/g->GetShootTime());
343 else charge_increment = -(charge/g->GetShootTime()/NEW_FACTOR);
344 mode = (old)?OldShooting:Shooting;
345 }
346
Shoot()347 void PBallTop::Shoot() {
348 Vec2 dir = dest-cue->P();
349
350 if (!dir.IsZero()) {
351 g->ShootBall( cue ); // neuen Schu� anzeigen
352 cue->TellPressed(); // Pressed-Kugel dem Game-Objekt mitteilen
353 cue->ChgV(dir.Norm1()*save_charge);
354 }
355
356 charge = RealZero; // Damit der Queue sichtbar
357 EndAimingTool(); // L�schen (inkl. Vorausberechnung)
358
359 default_cue->m = cue->m*4.;
360 ReleaseBall(); // Verbindung zur Kugel abbrechen
361 cue = default_cue;
362 cue->idle = 0;
363 cue->SetP( q_end ); // Hilfsball an Queue-Spitze
364 cue->ChgV( Vec2Zero ); // ... ohne Bewegung
365 cue->Lock(this); // ... Verfolgung einschalten
366
367 aim_hint = 0;
368 mode = LockedQueue;
369 StartAimingTool();
370
371 lock_time = current_time + LockedQueueDelay; // nachgelocken
372
373 #if (SHOW_MODE)
374 showinfo(PointerInfo, "LockedQueue " );
375 #endif
376 }
377
378
StartPullMoving(Ball * cueball)379 void PBallTop::StartPullMoving( Ball *cueball ) {
380 if (cueball) cue = cueball;
381 else cue = FindCueBall();
382 if (cue&&cue->Lock(this)) cue=0; // Lock erlaubt
383 if (!cue) return;
384
385 mode = PullMoving;
386 #if (SHOW_MODE)
387 showinfo(PointerInfo,"PullMoving ");
388 #endif
389 }
390
391
StartMoving(Ball * cueball)392 void PBallTop::StartMoving( Ball *cueball ) {
393 if (cueball) cue = cueball;
394 else cue = FindCueBall();
395 if (!cue) return;
396
397 mode = Moving;
398 cue->idle = 1; // Ball vom Tisch
399 cue->SetP(dest);
400 cue->ChgV(Vec2Zero);
401 #if (SHOW_MODE)
402 showinfo(PointerInfo,"Moving ");
403 #endif
404 }
405
406
StopMoving()407 void PBallTop::StopMoving() {
408 cue->idle = 0; // Ball wieder auf dem Tisch
409 if (pball_type!=DiscThrower)
410 cue->ChgV(Vec2Zero); // Ball gegebenenfalls stoppen
411 GoUnlocked();
412 }
413
ReleaseBall()414 void PBallTop::ReleaseBall() {
415 if (cue) {
416 cue->Unlock(this);
417 if (cue==default_cue) {
418 default_cue->idle = 1;
419 default_cue->SetP( Vec2Zero );
420 default_cue->ChgV( Vec2Zero );
421 }
422 cue=0l;
423 }
424 }
425
GoUnlocked()426 void PBallTop::GoUnlocked() {
427 if (mode&UsingTool) {
428 EndAimingTool(); // L�schen des Aiming-Tools
429 ReleaseBall();
430 }
431 else if (mode==Moving) {
432 Ball *nextb;
433 Real dist = Ball::FindClosest( cue, dest, &nextb );
434
435 if (nextb&&dist<nextb->R()+cue->R()) {
436 Vec2 dir = (nextb->P()-cue->P()).Norm1();
437 nextb->ChgV( dir*(nextb->R()+cue->R()-dist)*4.0 );
438 cue->ChgV( -dir*(nextb->R()+cue->R()-dist)*4.0 );
439 }
440 cue=0l;
441 }
442 else if (mode==PullMoving) {
443 target_time = NO_TARGET;
444 ReleaseBall();
445 }
446
447 mode = Unlocked;
448 #if (SHOW_MODE)
449 showinfo(PointerInfo,"Unlocked ");
450 #endif
451 }
452
453
PreCalc()454 void PBallTop::PreCalc() {
455 Vec2 dir = dest-cue->P();
456
457 ncalc_pos=0;
458 balls_dir=Vec2Zero;
459
460 if (!dir.IsZero()) {
461 Vec2 p_save = cue->P();
462 Vec2 v_save = cue->v;
463 Real slowstep_save = cue->next_slowstep;
464 Real next_time;
465 Real min_time;
466 Object *hit_object;
467
468 cue->v = dir.Norm1();
469 Object *obj;
470
471 for (ncalc_pos=0;ncalc_pos<MAX_PREPOS;) {
472 min_time = MAX_TIME;
473 hit_object = 0;
474 for (obj=Object::stat_queue; obj; obj=obj->Object::next ) {
475 if (obj!=cue) {
476 next_time = obj->HitFromBall(cue);
477 if ( next_time < min_time ) {
478 min_time = next_time;
479 hit_object = obj;
480 }
481 }
482 }
483 if (min_time<MAX_TIME) {
484 cue->p = cue->P() + min_time*cue->V();
485 }
486 else {
487 break;
488 }
489
490 calc_pos[ncalc_pos++] = cue->P();
491 if (hit_object->dyn_id!=-1) {
492 if (hit_object->dyn_id>=0) {
493 Vec2 e = cue->V().Norm1();
494 e.Split(((Ball*)hit_object)->P()-cue->P(),&balls_dir,&my_dir);
495 }
496 break;
497 }
498 cue->next_slowstep = SUPPRESS_SLOWSTEP;
499 hit_object->CollideWithBall(cue);
500 }
501
502 cue->next_slowstep = slowstep_save;
503 cue->v = v_save;
504 cue->p = p_save;
505 }
506 }
507
508
509
510
511 #define CHARGE_LEN 20.0
512 #define QUEUE_LEN 50.0
513
DrawQueue()514 void PBallTop::DrawQueue() {
515 if (valid_queue_position) {
516 #ifdef __TURBOC__
517 DrawLine(q_s1_s,q_end_s);
518 DrawLine(q_s2_s,q_end_s);
519 #else
520 FillPoly( 3, &q_end_s, &q_s1_s, &q_s2_s );
521 #endif
522 }
523 }
524
StartQueue(const Vec2 & end,const Vec2 & s1,const Vec2 & s2)525 void PBallTop::StartQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
526 q_end_s = end;
527 q_s1_s = s1;
528 q_s2_s = s2;
529 valid_queue_position = 1;
530 DrawQueue();
531 }
532
MoveQueue(const Vec2 & end,const Vec2 & s1,const Vec2 & s2)533 void PBallTop::MoveQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
534 DrawQueue();
535 q_end_s = end;
536 q_s1_s = s1;
537 q_s2_s = s2;
538 valid_queue_position = 1;
539 DrawQueue();
540 }
541
EndQueue()542 void PBallTop::EndQueue() {
543 DrawQueue();
544 valid_queue_position = 0;
545 }
546
DrawArrow(const Vec2 & from,const Vec2 & dist)547 static void DrawArrow( const Vec2 &from, const Vec2 &dist ) {
548 Vec2 to = from+8.0*dist*4.0;
549 Vec2 pa = dist.TurnAngleDeg( 150.)*4.0;
550 Vec2 pb = dist.TurnAngleDeg(-150.)*4.0;
551
552 DrawLine(from,to);
553 DrawLine(to,to+pa);
554 DrawLine(to,to+pb);
555 }
556
557
DrawAimingTool(int charge_only)558 void PBallTop::DrawAimingTool(int charge_only) {
559 #ifdef __TURBOC__
560 #else
561 gc_current = gc_cursor;
562 #endif
563 Vec2 dir= old_dest - aim_p;
564
565 if (!dir.IsZero()) {
566 Real len = dir.Norm();
567 Vec2 dir_norm = dir / len; // <=> Norm1()
568 // Vec2 mid = aim_p+dir/2.0;
569 Vec2 off = dir_norm*QUEUE_LEN/50.0;
570 Vec2 off1=off.TurnLeft();
571 Vec2 off2=off.TurnRight();
572
573 if (len<6.*aim_r) hint_valid=0;
574
575 if ((!aim_hint||!hint_valid)&&(aim_mode!=LockedQueue)) {
576 DrawLine(aim_p,old_dest);
577 }
578
579 if (aim_mode==LockedQueue||((aim_mode&ChargeOrShoot)&&(aim_charge>=0.0))) {
580 q_end = aim_p
581 - dir_norm*(aim_r+aim_charge/g->GetMaxCharge()*CHARGE_LEN);
582 Vec2 q_start = q_end - QUEUE_LEN*dir_norm;
583 Vec2 qs1 = q_start+off1;
584 Vec2 qs2 = q_start+off2;
585 if (!tool_is_visible) {
586 if (valid_queue_position) MoveQueue(q_end,qs1,qs2);
587 else StartQueue(q_end,qs1,qs2);
588 }
589 }
590
591 if (!charge_only && hint_valid) {
592 int i;
593 #ifndef __TURBOC__
594 XSetLineAttributes(dpy,gc_cursor,0,LineOnOffDash,CapRound,JoinRound);
595 #else
596 setlinestyle(DASHED_LINE,0,1);
597 #endif
598 DrawLine(aim_p,calc_pos[0]);
599 for (i=1;i<ncalc_pos;i++) {
600 DrawLine(calc_pos[i-1],calc_pos[i]);
601 }
602 #ifndef __TURBOC__
603 DrawCircle( calc_pos[i-1], aim_r );
604 XSetLineAttributes(dpy,gc_cursor,0,LineSolid,CapRound,JoinRound);
605 if ((!nohint_flag)&&(ncalc_pos<2)&&!balls_dir.IsZero()) {
606 #else
607 setlinestyle(SOLID_LINE,0,1);
608 if ((!nohint_flag)&&(ncalc_pos<3)&&!balls_dir.IsZero()) {
609 #endif
610 DrawArrow( calc_pos[ncalc_pos-1], balls_dir );
611 DrawArrow( calc_pos[ncalc_pos-1], my_dir );
612 }
613 }
614 }
615 tool_is_visible ^= 1; // toggle state
616 }
617
618
619 void PBallTop::SaveAimingState() {
620 aim_p = cue->P(); // Werte retten
621 aim_r = cue->R();
622 old_dest = dest;
623 aim_charge = charge;
624 int_charge = (int)(charge/2.);
625 aim_mode = mode;
626 }
627
628
629 void PBallTop::StartAimingTool() {
630 if (aim_hint) {
631 PreCalc();
632 hint_valid=1;
633 }
634 else {
635 hint_valid=0;
636 }
637 SaveAimingState();
638 DrawAimingTool();
639 }
640
641 void PBallTop::MoveAimingTool() {
642 int same_pos = (mode==aim_mode)&&((aim_p==cue->P())&&(dest==old_dest));
643
644 if (same_pos&&((int)(charge/2.)==int_charge))
645 return; // keine �nderung
646
647 DrawAimingTool(same_pos); // L�schen der alten Zeichnung
648 if (aim_hint) {
649 if (!same_pos||!hint_valid) PreCalc();
650 hint_valid=1;
651 }
652 else hint_valid=0;
653 SaveAimingState();
654 DrawAimingTool(same_pos); // Neuzeichnen
655 }
656
657 void PBallTop::EndAimingTool() {
658 DrawAimingTool();
659 EndQueue();
660 }
661
662
663 #ifndef __TURBOC__
664 # include "xpball.C"
665 #else
666 # include "dospball.C"
667 #endif
668