1 /*
2 * PLEASE DO NOT EDIT THIS FILE
3 * see documentation for more information
4 *
5 *
6 * System Independent Trojka Core
7 * Copyright (c) 1989-1996 Maarten Los
8 * --------
9 */
10 static char *_TROJKA_CORE_TAG_ = "System Independent Trojka Core 1.1";
11
12 #include "tr_core.h"
13
14 #include <stdlib.h>
15 #include <time.h>
16
17 /*
18 * variables
19 */
20 tt_char tv_field[tc_v_xsize][tc_v_ysize]; /* the playing matrix */
21 tt_char tv_shadow[tc_v_xsize][tc_v_ysize]; /* shadow of it for scan */
22 tt_int tv_shape; /* the current block */
23 tt_long tv_block_count[tc_blocks]; /* blocks/type */
24 tt_long tv_blocks; /* total blocks */
25 tt_long tv_last_blocks; /* total blocks */
26 tt_bool tv_blocks_reset; /* true if tv_blocks > MAXLONG */
27 tt_int tv_x; /* x position in field */
28 tt_int tv_y; /* y position in field */
29 tt_int tv_speed; /* current speed (0-9) */
30 tt_int tv_ticks; /* number of ticks in milliseconds for delay */
31 tt_int tv_stacklevel; /* height of the block stack */
32 tt_long tv_score; /* the score */
33 tt_long tv_last_score; /* the last score */
34 tt_bool tv_score_reset; /* true if tv_score > MAXLONG */
35 tt_long tv_trojkas; /* number of trojka's */
36 tt_long tv_last_trojkas; /* the last number of trojkas */
37 tt_bool tv_trojkas_reset; /* true if tv_trojkas > MAXLONG */
38 tt_long tv_wipes; /* number of wipes */
39 tt_long tv_last_wipes; /* number of wipes */
40 tt_long tv_wipes_reset; /* true if tv_wipes > MAXLONG */
41 tt_long tv_speed_switch; /* score for spead increase */
42 tt_long tv_wizard_switch; /* score for next layout */
43 tt_bool tv_is_wizard; /* are we in wizard mode? */
44 tt_bool tv_is_gaps; /* are there gaps in the field pattern? */
45 tt_int tv_lran_q_ptr; /* layout random pointer */
46 tt_int tv_lran_q[tc_layouts];
47 tt_int tv_layout[tc_layouts][5] =
48 { { 011331, 024413, 040000, 0, 0 },
49 { 010101, 040303, 010101, 040303, 010101 },
50 { 001210, 040004, 030003, 030003, 002120 },
51 { 010101, 000003, 030303, 000000, 040404 },
52 { 020202, 005050, 040404, 003030, 010101 },
53 { 035353, 041014, 040204, 0, 0 },
54 { 054245, 050005, 050005, 002020, 000200 },
55 { 010001, 031013, 011011, 031013, 010001 },
56 { 030003, 001130, 003110, 001130, 030003 },
57 { 011511, 040004, 020202, 040004, 011511 },
58 { 025341, 050000, 030350, 040010, 014320 },
59 { 010201, 021212, 003003, 001010, 010001 },
60 { 000300, 000300, 000300, 000300, 054345 },
61 { 0, 005050, 033133, 005050, 0 },
62 { 000400, 000300, 000200, 000300, 000400 },
63 { 000300, 001010, 040004, 001010, 000300 },
64 { 013531, 002520, 000400, 002520, 013531 },
65 { 000300, 000400, 010501, 005050, 000500 },
66 { 000200, 000400, 033533, 000400, 000200 },
67 { 030303, 001010, 000400, 000200, 000200 },
68 { 010001, 003030, 000100, 003030, 010001 },
69 { 004240, 030003, 0, 004040, 0 },
70 { 000100, 004040, 030303, 020202, 050505 },
71 { 050505, 053535, 003030, 023232, 020202 },
72 { 055355, 005000, 000300, 000050, 055355 },
73 { 053535, 030203, 052525, 030203, 053535 },
74 { 052505, 030303, 010101, 030303, 050525 },
75 { 0, 0, 0, 0, 055455 },
76 { 004253, 0, 031450, 0, 002143 },
77 { 053035, 002120, 053035, 002120, 053035 },
78 { 012121, 004340, 000300, 004340, 012121 },
79 { 005250, 020502, 012021, 020502, 005250 },
80 { 011022, 011022, 0, 033055, 033055 },
81 { 010001, 053035, 012421, 053035, 010001 },
82 { 043035, 020003, 020003, 020003, 043035 },
83 { 000055, 003300, 020000, 001100, 000044 },
84 { 033033, 000500, 005050, 002020, 004040 },
85 { 011000, 001100, 000440, 000044, 0 },
86 { 010001, 000300, 003530, 000300, 010001 },
87 { 000550, 000550, 033000, 033000, 0 },
88 { 055055, 044044, 033033, 022022, 011011 },
89 { 055110, 033550, 022330, 044220, 011440 }
90 };
91
92 /*
93 * function prototypes
94 */
95 int trojka_api(tt_command*);
96 void tf_init(int, int);
97 int tf_block_down(void);
98 void tf_block_left(void);
99 void tf_block_right(void);
100 void tf_drop_block(void);
101 void tf_check_speed(void);
102 void tf_inc_speed(void);
103 void tf_set_speed_var(void);
104 void tf_check_wizard(void);
105 void tf_set_wizard_switch(void);
106 void tf_inc_score(int, tt_long);
107 void tf_scan(void);
108 int tf_field_scan(void);
109 void tf_check_spider(void);
110 tt_bool tf_has_gaps(void);
111 void tf_shift_blocks(void);
112 void tf_shift_down(int, int);
113 void tf_explode(int, int);
114 int tf_get_stack_level(void);
115 tt_bool tf_empty_row(int);
116 void tf_reset_shadow(void);
117 void tf_reset_block(void);
118 void tf_build_layout(void);
119 void tf_init_layout_random_q(void);
120 void tf_draw_field(void);
121 int tf_get_layout_random(void);
122 void tf_constrain_speed(tt_int);
123
124
125 /********************************************************************
126 * MAIN FUNCTIONS *
127 ********************************************************************/
128
129
trojka_api(cmd)130 int trojka_api(cmd)
131 tt_command *cmd;
132 {
133 /*
134 * this is the main api to the core. The function can
135 * be given multiple commands.
136 * The return value and the parameters passed depend
137 * on the command. See documentation.
138 */
139 int res; /* call-result */
140
141 res = tc_res_normal;
142
143
144 switch(cmd->command) {
145 case tc_c_init:
146 tf_init((tt_int)cmd->param1, (tt_bool)cmd->param2);
147 break;
148
149 case tc_c_drawfield:
150 tf_draw_field();
151 break;
152
153 case tc_c_blockdown:
154 res = tf_block_down();
155 break;
156
157 case tc_c_blockleft:
158 tf_block_left();
159 break;
160
161 case tc_c_blockright:
162 tf_block_right();
163 break;
164
165 case tc_c_dropblock:
166 tf_drop_block();
167 break;
168
169 case tc_c_speedup:
170 tf_inc_speed();
171 break;
172
173 case tc_c_setspeed:
174 tf_constrain_speed((tt_int)cmd->param1);
175 tf_set_speed_var();
176 break;
177
178 case tc_c_setwizard:
179 tv_is_wizard = 1;
180 break;
181
182 }
183
184 return res;
185 }
186
187
188 /*********************************************************************
189 * basic functions, called by the api.
190 *********************************************************************/
191
192
tf_init(speed,wizard)193 void tf_init(speed, wizard)
194 tt_int speed;
195 tt_bool wizard;
196 /*
197 * initialize the game
198 */
199 {
200 int x,y;
201
202 /*
203 * reset the variables
204 */
205 tv_stacklevel = 0;
206 tv_blocks = tv_last_blocks = (tt_long)0;
207 tv_trojkas = tv_last_trojkas = (tt_long)0;
208 tv_wipes = tv_last_wipes = (tt_long)0;
209 tv_score = tv_last_score = (tt_long)0;
210 tv_is_wizard = wizard;
211
212 tf_set_wizard_switch();
213
214 tv_blocks_reset = 0;
215 tv_trojkas_reset = 0;
216 tv_wipes_reset = 0;
217 tv_score_reset = 0;
218
219 tf_constrain_speed(speed);
220
221 tf_set_speed_var();
222 tf_reset_block();
223
224 for(x = 0; x < tc_blocks; x++)
225 tv_block_count[x] = (tt_long)0;
226
227
228 /*
229 * clear the playfield
230 */
231 for(x = 0; x < tc_v_xsize; x++) /* clear virt. field */
232 for(y = 1; y < tc_v_ysize; y++)
233 tv_field[x][y] = tc_clear;
234 for(x = 0;x < tc_v_xsize; x++) /* fill bottom-line */
235 tv_field[x][0] = tc_tagged;
236
237 /*
238 * init the layout queue
239 */
240 tf_init_layout_random_q();
241
242 if(tv_is_wizard)
243 tf_build_layout();
244 }
245
246
247
tf_draw_field(void)248 void tf_draw_field(void)
249 /*
250 * draws the field, and puts a layout on it, if in
251 * wizard mode
252 */
253 {
254 int x,y;
255
256 /*
257 * draw all the blocks in the entire field
258 */
259 for(y = tc_pm_top; y >= tc_pm_bottom; y--) {
260 for(x = 0; x < tc_v_xsize; x++)
261 trojka_make_block_callback(x,y,tv_field[x][y]);
262 }
263
264 if(tv_y <= tc_pm_top + 1)
265 trojka_make_block_callback(tv_x, tv_y, tv_shape);
266 }
267
268
269
tf_block_down(void)270 int tf_block_down(void)
271 /*
272 * move the block one down
273 *
274 * returns one of the following:
275 * tc_res_touchdown a block hit the ground
276 * tc_res_gameover the game is over
277 * tc_res_normal continue normal play
278 *
279 */
280 {
281 tv_y--;
282 if(tv_field[tv_x][tv_y] != tc_clear) {
283
284 /*
285 * the block has hit something
286 */
287
288 tv_field[tv_x][tv_y + 1] = tv_shape;
289
290
291 if(tv_blocks_reset)
292 tv_blocks_reset = 0;
293 if(tv_blocks < tv_last_blocks) {
294 tv_blocks = (tt_long)0;
295 tv_blocks_reset = 1;
296 }
297 tv_last_blocks = tv_blocks;
298 tv_blocks++;
299
300
301 tv_block_count[tv_shape-1]++;
302
303 tf_check_speed();
304
305 tf_reset_block();
306
307 tf_inc_score(tc_i_touchdown,0);
308
309 tf_scan();
310
311 if(tv_stacklevel == tc_pm_top)
312 return tc_res_gameover;
313
314 tf_check_wizard();
315
316 return tc_res_touchdown;
317
318 } else {
319 trojka_wipe_block_callback(tv_x, tv_y+1);
320 trojka_make_block_callback(tv_x, tv_y, tv_shape);
321
322 return tc_res_normal;
323 }
324 }
325
326
327
tf_block_left(void)328 void tf_block_left(void)
329 /*
330 * this function move the block one position to the left,
331 * if possible
332 */
333 {
334 if((tv_x > 0) && (tv_field[tv_x-1][tv_y] == tc_clear)) {
335
336 trojka_wipe_block_callback(tv_x, tv_y);
337 tv_x--;
338 trojka_make_block_callback(tv_x, tv_y, tv_shape);
339 }
340 }
341
342
tf_block_right(void)343 void tf_block_right(void)
344 /*
345 * this function move the block one position to the right,
346 * if possible
347 */
348 {
349 if((tv_x < tc_v_xsize-1) && (tv_field[tv_x+1][tv_y] == tc_clear)) {
350 trojka_wipe_block_callback(tv_x, tv_y);
351 tv_x++;
352 trojka_make_block_callback(tv_x, tv_y, tv_shape);
353 }
354 }
355
356
tf_drop_block(void)357 void tf_drop_block(void)
358 /*
359 * drop a block until the bottom is reached
360 */
361 {
362 tf_inc_score(tc_i_dropblock, 0);
363
364 trojka_wipe_block_callback(tv_x, tv_y);
365 while(tv_field[tv_x][tv_y] == tc_clear)
366 tv_y--;
367 tv_y++;
368 trojka_make_block_callback(tv_x, tv_y, tv_shape);
369 }
370
371
372 /**********************************************************************
373 * support functions
374 **********************************************************************/
375
376
tf_check_speed(void)377 void tf_check_speed(void)
378 /*
379 * Increase the game speed if possible
380 */
381 {
382 if(tv_blocks > tv_speed_switch)
383 tf_inc_speed();
384 }
385
386
tf_inc_speed(void)387 void tf_inc_speed(void)
388 /*
389 * increase the speed. This function is called forcedly
390 * on a manual speed increase, and implicitely by
391 * 'tf_check_speed()'
392 */
393 {
394 if(tv_speed < tc_max_speed) {
395 tv_speed++;
396 tf_set_speed_var();
397 trojka_speedup_callback(tv_speed);
398 }
399 }
400
401
tf_set_speed_var(void)402 void tf_set_speed_var(void)
403 /*
404 * this function sets the parameters depending on speed
405 */
406 {
407 tv_ticks = 200 - ((tv_speed)*15);
408 tv_speed_switch = tc_speed_switch * tv_speed;
409 }
410
411
tf_check_wizard(void)412 void tf_check_wizard(void)
413 /*
414 * See if wizard needs a layout. If so, put a new layout
415 * in the playfield.
416 * Returns 1 on wizard change, 0 otherwise
417 * Only call after a tf_scanfield() ! ! ! ! !
418 */
419 {
420 if(!tv_is_wizard)
421 return;
422
423 if((!tv_is_gaps) && (tv_score > tv_wizard_switch)
424 && (tv_stacklevel <= 9)) {
425 tf_build_layout();
426 tf_draw_field();
427 tf_set_wizard_switch();
428 }
429 }
430
tf_set_wizard_switch()431 void tf_set_wizard_switch()
432 /*
433 * set the wizard switch. (it is called from 'check_wizard' and
434 * 'init_game()'
435 */
436 {
437 tv_wizard_switch = tv_score + (tt_long)(2+((rand()%10))*500)+2000;
438 }
439
tf_inc_score(type,amount)440 void tf_inc_score(type, amount)
441 int type;
442 tt_long amount;
443 /*
444 * increase the score depending on the type in the call:
445 * tc_i_touchdown: the block has just normally hit another block
446 * or the ground
447 * tc_i_dropblock: the drop is forcedly dropped
448 * tc_i_force: add the amount 'amount' to the score.
449 */
450 {
451 tt_long add;
452
453 switch(type) {
454
455 case tc_i_touchdown:
456 add = (tt_long)((((tv_shape*tv_speed)+(tv_speed-1))/3)+1);
457 break;
458
459 case tc_i_dropblock:
460 add = (tt_long)((tv_y * tv_speed) / 5);
461 break;
462
463 case tc_i_force:
464 add = amount;
465 break;
466 }
467
468 if(tv_is_wizard)
469 add += 2 + (rand() % tc_blocks);
470
471 if(tv_score_reset)
472 tv_score_reset = 0;
473
474 if(tv_last_score > tv_score) {
475 tv_score_reset = 1;
476 tv_score = (tt_long)0;
477 }
478 tv_last_score = tv_score;
479 tv_score += add;
480
481 }
482
483
484
tf_scan(void)485 void tf_scan(void)
486 {
487 /*
488 * NOTE: this is a very inefficient algorithm!
489 * Return values:
490 * height of blockstack
491 *
492 */
493 tt_long trojka_score;
494
495 int wipe_bonus, wipe_count, wiped;
496
497 tv_stacklevel = tf_get_stack_level();
498 wiped = 0;
499
500
501 trojka_score = (tt_long)tc_trojkabonus;
502 wipe_bonus = 1;
503 wipe_count = 0;
504 tf_reset_shadow();
505
506 while((wiped = tf_field_scan()) > 0) {
507
508 tf_shift_blocks();
509
510 wipe_bonus *= 2;
511 tf_inc_score(tc_i_force, (tt_long)(wipe_bonus*wiped*10));
512
513 wipe_count++;
514 if(((int)wipe_count % tc_trojka) == 0) {
515
516 tf_inc_score(tc_i_force, trojka_score);
517 trojka_trojka_callback(trojka_score);
518
519 trojka_score *= 2;
520
521 if(tv_trojkas_reset)
522 tv_trojkas_reset = 0;
523 if(tv_trojkas < tv_last_trojkas) {
524 tv_trojkas = (tt_long)0;
525 tv_trojkas_reset = 1;
526 }
527 tv_last_trojkas = tv_trojkas;
528 tv_trojkas++;
529
530 }
531
532 if(tv_wipes_reset)
533 tv_wipes_reset = 0;
534 if(tv_wipes < tv_last_wipes) {
535 tv_wipes = (tt_long)0;
536 tv_wipes_reset = 1;
537 }
538 tv_last_wipes = tv_wipes;
539 tv_wipes++;
540 }
541
542 if((tv_stacklevel <= tc_pm_bottom) && (tv_wipes > 0)
543 && (tf_empty_row(tc_pm_bottom)))
544 {
545 tf_inc_score(tc_i_force, tc_bottombonus);
546 trojka_bottom_callback(tc_bottombonus);
547 }
548
549 tv_is_gaps = tf_has_gaps();
550
551 }
552
553
554
tf_field_scan(void)555 int tf_field_scan(void)
556 /*
557 * do an actual scan of the playing matrix
558 * returns the number of wipes times 3.
559 */
560 {
561 tt_int x,y, wiped;
562 tt_char cur;
563
564 tv_stacklevel = tf_get_stack_level();
565
566
567 wiped = 0;
568
569 tf_check_spider();
570
571 for(x = 1; x < tc_v_xsize - 1; x++) {
572 for(y = tv_stacklevel; y >= tc_pm_bottom; y--) {
573 if((cur = tv_field[x][y]) > 0) {
574 /* "\" */ if ((tv_field[x-1][y-1] == cur)
575 && (tv_field[x+1][y+1] == cur)) {
576 tv_shadow[x-1][y-1]
577 = tv_shadow[x+1][y+1]
578 = tv_shadow[x][y] = tc_tagged;
579 wiped++;
580 }
581 /* "/" */ if ((tv_field[x-1][y+1] == cur)
582 && (tv_field[x+1][y-1] == cur)) {
583 tv_shadow[x-1][y+1]
584 = tv_shadow[x][y]
585 = tv_shadow[x+1][y-1] = tc_tagged;
586 wiped++;
587 }
588 /* "--" */ if ((tv_field[x-1][y] == cur)
589 && (tv_field[x+1][y] == cur)) {
590 tv_shadow[x-1][y]
591 = tv_shadow[x+1][y]
592 = tv_shadow[x][y] = tc_tagged;
593 wiped++;
594 }
595 }
596 }
597 }
598 return wiped * 3;
599 }
600
601
tf_check_spider(void)602 void tf_check_spider(void)
603 {
604 tt_long spider_score;
605 tt_char cur;
606 tt_int y;
607
608 for(y = tv_stacklevel; y >= (tc_pm_bottom + 2); y--) {
609 /*
610 * ok, what follows is not your typical fine art of programming,
611 * but it should do it.
612 */
613 spider_score = (tt_long)0;
614 if((cur = tv_field[tc_virt_middle][y]) <= 0)
615 cur = -9;
616
617 if((cur == tv_field[tc_virt_middle-1][y+1])
618 && (cur == tv_field[tc_virt_middle-2][y+2])
619 && (cur == tv_field[tc_virt_middle+1][y+1])
620 && (cur == tv_field[tc_virt_middle+2][y+2])
621 && (cur == tv_field[tc_virt_middle+1][y-1])
622 && (cur == tv_field[tc_virt_middle+2][y-2])
623 && (cur == tv_field[tc_virt_middle-1][y-1])
624 && (cur == tv_field[tc_virt_middle-2][y-2])) {
625
626 spider_score = tc_spiderbonus;
627
628 if((cur == tv_field[tc_virt_middle-1][y])
629 && (cur == tv_field[tc_virt_middle-2][y])
630 && (cur == tv_field[tc_virt_middle+1][y])
631 && (cur == tv_field[tc_virt_middle+2][y])) {
632
633 spider_score = tc_bigspiderbonus;
634
635 }
636
637 tf_inc_score(tc_i_force, spider_score);
638 trojka_spider_callback(spider_score);
639
640 }
641 }
642 }
643
644
tf_has_gaps(void)645 tt_bool tf_has_gaps(void)
646 {
647 int x,y;
648 tt_bool gap_found;
649
650 for(x = 0; x < tc_v_xsize; x++) {
651 gap_found = 0;
652 for(y = tc_pm_bottom; y < tv_stacklevel; y++) {
653 if((tv_field[x][y] > 0) && (gap_found))
654 return 1;
655 else if(tv_field[x][y] == 0)
656 gap_found = 1;
657 }
658 }
659 return 0;
660 }
661
662
tf_shift_blocks(void)663 void tf_shift_blocks(void)
664 /*
665 * shift all the blocks in the playing field,
666 * after a collapse.
667 */
668 {
669 int x, y;
670
671 for(x = 0; x < tc_v_xsize; x++) {
672 for(y = tv_stacklevel; y > 0; y--) {
673 if(tv_shadow[x][y] == tc_tagged) {
674 tf_explode(x, y);
675 tf_shift_down(x, y);
676 tv_shadow[x][y] = tc_clear;
677 }
678 }
679 }
680 }
681
682
683
tf_shift_down(x,y)684 void tf_shift_down(x, y)
685 int x, y;
686 /*
687 * shift down a row of blocks, both on-screen and in the matrix
688 */
689 {
690 while(tv_field[x][y] != tc_clear) {
691 tv_field[x][y] = tv_field[x][y+1];
692 trojka_make_block_callback(x, y, tv_field[x][y]);
693 y++;
694 }
695 }
696
697
tf_explode(x,y)698 void tf_explode(x, y)
699 int x, y;
700 {
701 trojka_explode_callback(x,y);
702 trojka_wipe_block_callback(x,y);
703 }
704
705
tf_get_stack_level(void)706 int tf_get_stack_level(void)
707 /*
708 * returns the last empty line in the virtual field
709 */
710 {
711 int y = tc_pm_top;
712
713 while(tf_empty_row(y))
714 y--;
715
716 return y ;
717 }
718
719
tf_empty_row(y)720 tt_bool tf_empty_row(y)
721 int y;
722 {
723 int x;
724
725 for(x = 0; x < tc_v_xsize; x++)
726 if(tv_field[x][y] != tc_clear)
727 return 0;
728 return 1;
729 }
730
731
tf_reset_shadow(void)732 void tf_reset_shadow(void)
733 {
734 int x,y;
735
736 for(x = 0; x < tc_v_xsize;x++)
737 for(y = 0; y < tc_v_ysize;y++)
738 tv_shadow[x][y] = tc_clear;
739 }
740
741
tf_reset_block(void)742 void tf_reset_block(void)
743 /*
744 * reset the falling block to the top of the playing field
745 *
746 */
747 {
748 tv_y = tc_pm_top + 1;
749 tv_x = tc_virt_middle;
750 tv_shape = (rand() % tc_blocks) + 1;
751
752 }
753
754 /**********************************************************************
755 * layout functions
756 **********************************************************************/
757
758
tf_build_layout(void)759 void tf_build_layout(void)
760 /*
761 * put a layout patter in the playing field (but don't draw it)
762 */
763 {
764 int x,y;
765 int pattern;
766 tt_char block;
767 int layout = tf_get_layout_random();
768
769 for(y = tc_pm_bottom; y < (tc_pm_bottom + 5); y++) {
770 for(x = 0; x < tc_blocks; x++) {
771 pattern = tv_layout[layout][y-1];
772 block = (pattern >> (x*3)) & tc_layout_mask;
773 tv_field[x][y] = block;
774 }
775 }
776 }
777
778
779
tf_init_layout_random_q(void)780 void tf_init_layout_random_q(void)
781 /*
782 * initialize the random queue
783 */
784 {
785 int i;
786 srand(time(0));
787
788 tv_lran_q_ptr = tc_layouts;
789 for(i = 0; i < tv_lran_q_ptr; i++)
790 tv_lran_q[i] = i;
791 }
792
793
tf_get_layout_random(void)794 int tf_get_layout_random(void)
795 /*
796 * get a random queue element, and swap it with one already
797 * used
798 */
799 {
800 int rnd;
801
802 rnd = tv_lran_q[rand() % tv_lran_q_ptr];
803
804 tv_lran_q[rnd] = tv_lran_q[--tv_lran_q_ptr];
805 tv_lran_q[tv_lran_q_ptr] = rnd;
806
807 if(tv_lran_q_ptr == 0)
808 tf_init_layout_random_q();
809
810 return rnd;
811 }
812
813
tf_constrain_speed(speed)814 void tf_constrain_speed(speed)
815 int speed;
816 /*
817 * constrain the speed between a value of 1 and 9
818 */
819 {
820 tv_speed = (speed > tc_max_speed) ? tc_max_speed :
821 (speed < tc_min_speed) ? 1 : speed;
822 }
823