--- title: "Combined Track Tab (D&D)" id: 57410 type: "computer_media" slug: "combined-track-tab-dd" url: "http://localhost/computer_media/combined-track-tab-dd/" markdown_url: "http://localhost/computer_media/combined-track-tab-dd.md" published_at: "2024-10-05T12:45:12+00:00" modified_at: "2026-04-03T07:58:32+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/211_CTT.png" excerpt: "A full RPG combat tracker handles hit points, damage, healing, regeneration, and attack tables for multiple creatures at once — all in BASIC." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" indiv: - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" genre: - name: "Dungeons & Dragons" slug: "dungeons-dragons" taxonomy: "genre" url: "http://localhost/type/dungeons-dragons/" media_contents: - id: 56736 title: "Timex Sinclair Public Domain Library Tape 1005" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1005/" media_type: "Program" programmers: - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/211_CTT.png" media_type_tags: "Dungeons & Dragons" --- # Combined Track Tab (D&D) This program is a combat tracking utility for tabletop role-playing games, designed to manage hit points, damage, healing, regeneration, and attack resolution for multiple creatures simultaneously. It allocates five parallel arrays (A through E) indexed by creature number to store current hit points, maximum hit points, regeneration rate, regeneration delay timer, and a “bound” flag respectively. A built-in attack table subroutine (line 2000) calculates the die roll needed to hit a target armour class for Cleric, Thief, Fighter, and Magic-User character classes across multiple level bands, using constants like `NOT PI` (=0), `SGN PI` (=1), `INT PI` (=3), and `LEN STR$ PI` (=9) as compact numeric literals. The regeneration logic (lines 680–870) handles both troll-style full regeneration (3 HP per round after a delay) and per-round fractional regeneration, including bleed-out tracking for creatures at negative hit points. *** ## Program Analysis ### Program Structure The program is organized as a menu-driven dispatcher. After initialization (lines 100–130) and setup (210–350), control falls into a main loop anchored at line 140, which prints a menu and branches via a computed `GOTO` at line 200. Each functional module returns to the status display at line 360 or back to line 140. | Line Range | Module | | --- | --- | | 100–130 | Initialization / entry point | | 140–200 | Main menu and dispatcher | | 210–350 | Setup: creature count, hit points, monster series | | 360–580 | Display status (option 1) | | 590–870 | Damage entry and round regeneration (option 2) | | 880–1130 | Regeneration setup — trolls and others (option 4) | | 1140–1230 | Heal (option 3) | | 1240–1310 | Add/Drop characters (option 6) | | 1320–1370 | Bind creature (option 5) | | 1380–1520 | Random dice roller (option 7) | | 1530–1570 | Screen-clearing subroutine | | 2000–2265 | Attack table calculator (option 8 / F=1000) | | 2750–2755 | Monster attack table display subroutine | | 2800–2900 | Add monsters mid-session | | 2950–3020 | Monster series batch entry | | 9000–9020 | Stop and Save | ### Array Layout Five parallel arrays of dimension `Q` (number of creatures) are declared at lines 220–224: - `A(F)` — current hit points (0 = inactive/dropped) - `B(F)` — maximum (full) hit points - `C(F)` — regeneration amount per round (10 = troll-style) - `D(F)` — round number at which troll regeneration resumes - `E(F)` — bleed/bound flag (1 = bleeding; 10 = bound/dead) ### Computed GOTO Dispatcher Line 200 uses the idiom of summing Boolean-multiplied line numbers: `GOTO (360 AND J=1)+(590 AND J=2)+(1140 AND J=3)+...` In Sinclair BASIC, a true condition evaluates to 1 and false to 0, so exactly one term is non-zero, producing the target line number. This avoids a chain of `IF … GOTO` statements and is a well-known space and speed optimization. ### Regeneration Logic The regeneration pass at lines 680–870 runs at the end of each damage round. Three cases are handled: 1. **Bleed-out** (lines 700–760): If `A(F)` is negative and `E(F)` is 0, set `E(F)=1` to start bleeding. On subsequent rounds while negative and not yet bound (`E(F)≠10`), subtract 1 HP per round. 2. **Troll regeneration** (`C(F)=10`, lines 790–820): A delay of 3 rounds is set in `D(F)` on first use; thereafter 3 HP are restored per round, capped at `B(F)`. 3. **Standard regeneration** (`C(F)<10`, lines 850–860): Add `C(F)` HP per round, capped at `B(F)`. ### Attack Table (Lines 2000–2265) The attack calculator accepts class (as a string), level, and opponent armour class (AC). It first sets a base “to-hit” score `S` from lookup conditions, then iterates from AC 9 down to the target AC, incrementing `S` each step. Fighter THAC0 is computed formulaically (`S = 11 - L`), while other classes use banded level ranges. The iteration loop (lines 2215–2235) also handles the edge case where `S` reaches 20 — the maximum natural roll — by allowing up to 6 additional “natural 20” attacks before capping. ### Notable Techniques and Idioms - **Math constants as compact literals**: `NOT PI` = 0, `SGN PI` = 1, `INT PI` = 3, `LEN STR$ PI` = 9 (length of “3.1415927”), `COS PI` = −1. These are used throughout the attack table to avoid storing numeric constants as tokens. - **`GOSUB VAL "2750"`** (line 2080): Using `VAL "number"` in `GOSUB` is a memory optimization — it stores the number as a short string rather than a full floating-point constant. - **Screen clearing subroutine** (lines 1530–1570): Rather than `CLS`, two rows at the bottom of the screen are overwritten with spaces, preserving the upper display during data entry. - **Escape into dice roller from damage entry**: At line 633, entering `F=2000` during damage input branches directly to the random dice roller, and `FL=1` ensures return to the damage loop afterward (lines 1515, 2262). - **Heal with code 1000** (line 1210): Entering 1000 as the cure amount fully restores a creature to its maximum HP (`B(F)`) rather than adding a literal 1000. - **`FAST` mode** (line 2040): The attack table section switches to FAST mode for quicker computation, consistent with ZX81/TS1000 practice for calculation-heavy routines. ### Bugs and Anomalies - **Line 130 is missing**: Line 350 does `GOTO 130`, but no line 130 exists in the listing. This would cause a “No such line” error at runtime. The intent appears to be a jump to `140` (the main menu). - **Variable `R` reused**: `R` is initialized to 0 at line 100 and set to 1 as a “regeneration active” flag at line 890, but it is also used as a loop variable in the monster series subroutine (lines 3000–3015), which would corrupt the flag. - **Line 345 range check order**: The check `IF MS>2 OR MS<1 THEN GOTO 341` at line 345 is placed after the valid-value checks at 343–344, so it functions correctly, but a non-integer input between 1 and 2 would fall through to line 344’s `IF MS=2` test, fail it, and then be caught by line 345 — a minor robustness gap rather than a true bug. - **Status display column arithmetic** (lines 430–480): The column variable `H` starts at 0 and increments by 4 each creature. The wrap condition is `H<28`; since the screen is 32 columns wide, creatures at columns 28 and beyond would be partially off-screen before the wrap triggers on the *next* iteration. ## Source Code ``` 1 REM %C%O%M%B%I%N%E%D%(%T%R%A%C%K%-%T%A%B%)6/84 TRACK ****D £ D TRACK**** J LANE/T WILLING MODIFIED FOR %T%I%M%E%X/ SINCLAIR BY ANTHONY WILLING (4/1984) WRITE FOR INSTRUCTS TO POB 199, VASHON, WA., 98070 S.A.S.E. 100 LET R=0 110 LET TY=1 120 GOTO 210 140 REM ** CONTROL ** 141 LET FL=0 150 PRINT AT 19,1;"########BEGINNING OF ROUND ";TY;"########" 160 PRINT AT 20,1;"%1=STATUS %2=DAMAGE %3=HEAL %4=REG. %5=BIND %6=DROP %7= RAND %8=ATTACK" 170 INPUT J 180 IF J=0 THEN STOP 190 IF J>8 THEN GOTO 170 200 GOTO (360 AND J=1)+(590 AND J=2)+(1140 AND J=3)+(880 AND J=4)+(1320 AND J=5)+(1240 AND J=6)+(1380 AND J=7)+(2000 AND J=8) 210 REM ** BEGIN ** 215 PRINT AT 0,0;"HOW MANY CREATURES TO TRACK?" 217 INPUT Q 220 DIM A(Q) 221 DIM B(Q) 222 DIM C(Q) 223 DIM D(Q) 224 DIM E(Q) 230 CLS 240 PRINT AT 20,1;"MONSTER NUMBER?" 250 INPUT F 260 IF F=0 THEN GOTO 290 270 LET A(F)=1 280 GOTO 230 290 FOR F=1 TO Q 300 IF A(F)=0 THEN GOTO 340 310 PRINT AT 20,1;"HIT POINTS FOR ";F 320 INPUT H 330 LET A(F)=H 335 LET B(F)=H 340 NEXT F 341 PRINT AT 20,1;"MONSTER SERIES? (Y=1/N=2) " 342 INPUT MS 343 IF MS=1 THEN GOSUB 2950 344 IF MS=2 THEN GOTO 350 345 IF MS>2 OR MS<1 THEN GOTO 341 350 GOTO 130 360 REM ** DISPLAY STATUS ** 370 CLS 380 LET G=1 390 LET H=0 400 FOR F=1 TO Q 410 IF A(F)=0 THEN GOTO 490 420 IF G>21 THEN GOSUB 510 430 PRINT AT G-1,H;F 440 PRINT AT G,H;A(F) 450 LET H=H+4 460 IF H<28 THEN GOTO 490 470 LET H=1 480 LET G=G+3 490 NEXT F 500 GOTO 580 510 PRINT AT 21,1;"CONTINUE PRINTOUT?" 520 INPUT A$ 530 IF A$="N" THEN GOTO 580 540 LET G=3 550 LET H=1 560 CLS 570 RETURN 580 GOTO 140 590 GOSUB 1530 591 REM ** DAMAGE ** 600 LET TY=TY+1 605 PRINT AT 18,0;"% %1%0%0%0%=%A%T%T%A%C%K% %T%A%B%L%E%S% % %2%0%0%0%=%D%I%C%E% % " 610 PRINT AT 21,1;"CHARACTER THAT TOOK DAMAGE? " 620 INPUT F 630 IF F=0 THEN GOTO 680 631 LET FL=1 632 IF F=1000 THEN GOTO 2000 633 IF F=2000 THEN GOTO 1380 640 PRINT AT 21,1;"AMOUNT OF DAMAGE? " 650 INPUT J 660 LET A(F)=A(F)-J 670 GOTO 610 680 REM ** REGENERATE/ROUND ** 690 FOR F=1 TO Q 700 IF A(F)>=0 THEN LET E(F)=0 710 IF A(F)>=0 THEN GOTO 770 720 IF E(F)=10 THEN GOTO 770 730 IF E(F)=0 THEN GOTO 760 740 LET A(F)=A(F)-1 750 GOTO 770 760 LET E(F)=1 770 IF (C(F)=0 OR A(F)=B(F)) THEN GOTO 830 780 IF (C(F)<10) AND (A(F)TY THEN GOTO 830 810 LET A(F)=A(F)+3 820 IF A(F)>B(F) THEN LET A(F)=B(F) 830 NEXT F 840 GOTO 360 850 LET A(F)=A(F)+C(F) 860 IF A(F)>B(F) THEN LET A(F)=B(F) 870 GOTO 830 880 REM ** REGENERATE ** 890 LET R=1 900 FOR F=1 TO Q 902 LET B(F)=A(F) 904 NEXT F 910 REM ** TROLLS ** 920 GOSUB 1530 930 PRINT AT 20,1;"FIRST TROLL NUMBER?" 940 INPUT K 950 IF K=0 THEN GOTO 1040 960 PRINT AT 20,1;"LAST TROLL NUMBER? " 970 INPUT L 980 IF K>L THEN GOTO 1030 990 IF A(K)<1 THEN GOTO 1010 1000 LET C(K)=10 1010 LET K=K+1 1020 GOTO 980 1030 GOTO 930 1040 REM ** OTHER REGENERATE ** 1050 GOSUB 1530 1060 PRINT AT 20,1;"OTHER REGENERATING CREATURE?" 1070 INPUT F 1080 IF F=0 THEN GOTO 1130 1090 PRINT AT 20,1;"REGENERATION PER ROUND? " 1100 INPUT M 1110 LET C(F)=M 1120 GOTO 1060 1130 GOTO 360 1140 REM ** HEAL ** 1150 GOSUB 1530 1160 PRINT AT 20,1;"CHARACTER THAT WAS HEALED?" 1170 INPUT F 1180 IF F=0 THEN GOTO 1230 1190 PRINT AT 20,1;"AMOUNT OF CURE? " 1200 INPUT N 1210 IF N=1000 THEN LET A(F)=B(F) 1212 IF N<>1000 THEN LET A(F)=A(F)+N 1220 GOTO 1160 1230 GOTO 360 1240 REM ** DROP CHARACTERS ** 1250 GOSUB 1530 1260 PRINT AT 20,1;"ADD/DROP? (1/2) " 1261 INPUT AD 1262 IF AD<0 OR AD>2 THEN GOTO 1260 1263 IF AD=1 THEN GOTO 2800 1264 IF AD=0 THEN GOTO 360 1269 PRINT AT 20,1;"CHARACTER TO DROP FROM STATUS?" 1270 INPUT F 1280 IF F=0 THEN GOTO 1310 1290 LET A(F)=0 1295 LET B(F)=0 1300 GOTO 1260 1310 GOTO 360 1320 REM ** BIND ** 1325 GOSUB 1530 1330 PRINT AT 20,1;"CHARACTER NUMBER BOUND?" 1332 INPUT F 1340 IF F=0 THEN GOTO 1370 1350 LET E(F)=10 1360 GOTO 1330 1370 GOTO 360 1380 REM ** RANDOM ROLLS ** 1390 CLS 1400 PRINT "WHAT SIDED DICE?" 1410 INPUT F 1420 IF F=0 THEN GOTO 1510 1430 PRINT "NUMBER OF TIMES?" 1440 INPUT G 1445 CLS 1450 FOR H=1 TO G 1470 LET K=INT (F*RND)+1 1480 PRINT K 1490 NEXT H 1492 PRINT "(PRESS %E%N%T%E%R TO GO ON)" 1495 PAUSE 4E4 1500 GOTO 1390 1510 CLS 1515 IF FL=1 THEN GOTO 610 1520 GOTO 360 1530 PRINT AT 20,1;" " 1540 PRINT AT 21,1;" " 1570 RETURN 2000 REM % % % % % % % % % %A%-%T%A%B% % % % % % % % % 2010 REM %B%Y% %A%N%T%H%O%N%Y% %W%I%L%L%I%N%G% %6%/%8%4 2030 REM 2040 FAST 2050 CLS 2060 PRINT "%A%T%T%A%C%K% %T%A%B%L%E"," ""X"" TO END" 2070 PRINT "INPUT ""%CLERIC"",""%THIEF"",","""%FIGHTER"",""%MAGIC-USER""" 2080 GOSUB VAL "2750" 2090 PRINT 2100 PRINT AT VAL "4",NOT PI;" INPUT CLASS" 2110 INPUT C$ 2115 IF C$(1)="X" THEN GOTO 360 2120 PRINT " INPUT LEVEL" 2130 INPUT L 2140 PRINT " INPUT OPPONENT A.C." 2145 INPUT O 2150 IF C$(1)="F" THEN LET S=VAL "11"-L 2152 IF C$(1)="F" AND L>=17 THEN LET S=VAL "-6" 2155 IF C$(1)="C" AND L>=1 AND L<=3 THEN LET S=VAL "10" 2160 IF C$(1)="C" AND L>=4 AND L<=6 THEN LET S=VAL "8" 2165 IF C$(1)="C" AND L>=7 AND L<=9 OR C$(1)="T" AND L>=9 AND L<=12 OR C$(1)="M" AND L>=11 AND L<=15 THEN LET S=VAL "6" 2170 IF C$(1)="C" AND L>=10 AND L<=12 OR C$(1)="T" AND L>=13 AND L<=16 THEN LET S=VAL "4" 2175 IF C$(1)="C" AND L>=13 AND L<=15 OR C$(1)="T" AND L>=17 AND L<=20 THEN LET S=VAL "2" 2180 IF C$(1)="C" AND L>=16 AND L<=18 OR C$(1)="T" AND L>=21 THEN LET S=NOT PI 2185 IF C$(1)="C" AND L>=19 THEN LET S=COS PI 2190 IF C$(1)="T" AND L>=1 AND L<=4 OR C$(1)="M" AND L>=1 AND L<=5 THEN LET S=VAL "11" 2195 IF C$(1)="T" AND L>=5 AND L<=8 OR C$(1)="M" AND L>=6 AND L<=10 THEN LET S=LEN STR$ PI 2200 IF C$(1)="M" AND L>=16 AND L<=20 THEN LET S=INT PI 2205 IF C$(1)="M" AND L>=21 THEN LET S=SGN PI 2210 LET Z=SGN PI 2215 FOR H=9 TO O STEP -1 2220 IF S=20 THEN LET Z=Z+SGN PI 2225 IF S=20 AND Z<=6 THEN LET S=19 2230 LET S=S+1 2235 NEXT H 2240 PRINT 2245 PRINT 2250 PRINT "NEED TO HIT: ";S 2255 PAUSE 4E4 2260 CLS 2262 IF FL=1 THEN GOTO 610 2265 GOTO 2000 2750 PRINT AT 16,0;"MONSTERS ACT AS FIGHTERS","%<%1%-%1 0 %1%-%1 1 %1 2 %1%+%1 3","%2%-%3%+ 5 %4%-%5%+ 6 %6%-%7%+ 8 %8%-%9%+ 9","%1%0%-%1%1%+ 11 %1%2%-%1%3%+ 12 %1%4%-%1%5%+ 13","%1%6%+ 14" 2755 RETURN 2799 REM ** ADDED MONSTERS ** 2800 CLS 2801 PRINT AT 20,1;"FIRST MONSTER NUMBER ADDED?" 2802 INPUT MX 2805 PRINT AT 20,1;"ADD MONSTER NUMBER? " 2810 INPUT ADD 2820 IF ADD=0 THEN GOTO 2850 2830 LET A(ADD)=1 2840 GOTO 2810 2850 FOR V=MX TO Q 2860 IF A(V)=0 THEN GOTO 2897 2870 PRINT AT 20,1;"HIT POINTS FOR ";V;" " 2880 INPUT AHP 2890 LET A(V)=AHP 2895 LET B(V)=AHP 2897 NEXT V 2900 GOTO 1260 2950 REM ** MONSTER SERIES ** 2955 CLS 2960 PRINT AT 20,1;"FIRST NUMBER OF SERIES?" 2965 INPUT FN 2970 PRINT AT 20,1;"LAST NUMBER OF SERIES? " 2975 INPUT LN 2980 IF FN=0 OR LN=0 THEN GOTO 230 2985 IF FN>LN THEN GOTO 2950 2990 PRINT AT 20,1;"HIT POINTS FOR THESE MONSTERS? " 2995 INPUT MP 3000 FOR R=FN TO LN 3005 LET A(R)=MP 3010 LET B(R)=MP 3015 NEXT R 3017 CLS 3020 RETURN 9000 STOP 9010 SAVE "1021%1" 9020 RUN ```