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