A wall can look like anything you like. Your movement code still needs a clear answer to one question: what is in that cell? These routines give the level its own data, so the answer does not depend on what happens to be drawn on screen.
The complete caller prints a small level, reads the hazard at column 3, row 2, changes it into a goal and prints the changed level. It is a short demonstration of the routines’ interfaces. The shared entry explains the layout, properties and alternatives.
Start with the data
The example keeps its map eight columns wide so the output fits the VIC-20’s text display. Numeric-array storage matters on a small machine: 48 numeric cells cost more than 48 bytes. Keep the short caller, or select an appropriate RAM expansion before growing the level.
Copy the routines into free line numbers in your program. Keep their numbers, or update every GOSUB when moving them. Initialise W (columns), H (rows), S (world units per square tile) and L (highest tile ID), then allocate and fill the map/property arrays. W, H and S must be positive integers, L a non-negative integer. Allocate the arrays to match them before calling anything.
Allocate and fill the caller-owned map and property table
10 W=8:H=6:S=8:L=3
20 DIM M(W*H-1),P(L)
30 FOR I=0 TO 47:M(I)=0:NEXT I
40 FOR I=0 TO 7:M(I)=1:M(40+I)=1:NEXT I
50 FOR I=0 TO 5:M(I*8)=1:M(I*8+7)=1:NEXT I
60 M(19)=2:M(28)=3
70 P(0)=0:P(1)=1:P(2)=2:P(3)=4
80 Q=0:U=0:A=8:B=6The sample uses IDs 0–3: floor, wall, hazard and goal. The property table gives them values 0, 1, 2 and 4. Those values describe gameplay; they are not screen character codes. Change the table to suit your rules, and enlarge it when adding more tile IDs.
Call a routine
| Entry | Set before calling | Read afterwards |
|---|---|---|
GOSUB 1000 |
C column, R row | E=1 and T tile ID; E=0 and T=-1 if invalid |
GOSUB 1100 |
C, R, V new tile ID | E=1 and T=V after writing; E=0 leaves the map unchanged |
GOSUB 1200 |
T tile ID | E=1 and F property value; invalid IDs give E=0, F=-1 |
GOSUB 1300 |
X, Y world position, S tile size | C/R cell coordinates, then the same lookup result as 1000 |
GOSUB 1400 |
Q/U source cell, A/B region width/height | Prints the complete text region and returns E=1, or prints nothing and returns E=0 |
Check E immediately. A later call replaces it. For a position query, set X/Y, call 1300 and check E; then call 1200 to classify the returned tile, and check E again. Decide in your game whether a position beyond the map should block movement, wrap or lead to another room.
Reusable Commodore BASIC map routines
1000 E=0:T=-1
1010 IF C<0 THEN RETURN
1020 IF R<0 THEN RETURN
1030 IF C>=W THEN RETURN
1040 IF R>=H THEN RETURN
1050 IF C<>INT(C) THEN RETURN
1060 IF R<>INT(R) THEN RETURN
1070 D=R*W+C:T=M(D):E=1
1080 RETURN
1100 E=0
1110 IF V<0 THEN RETURN
1120 IF V>L THEN RETURN
1130 IF V<>INT(V) THEN RETURN
1140 GOSUB 1000
1150 IF E=0 THEN RETURN
1160 M(D)=V:T=V
1170 RETURN
1200 E=0:F=-1
1210 IF T<0 THEN RETURN
1220 IF T>L THEN RETURN
1230 IF T<>INT(T) THEN RETURN
1240 F=P(T):E=1
1250 RETURN
1300 E=0:C=-1:R=-1:T=-1
1310 IF S<=0 THEN RETURN
1320 IF X<0 THEN RETURN
1330 IF Y<0 THEN RETURN
1340 C=INT(X/S):R=INT(Y/S)
1350 GOSUB 1000
1360 RETURN
1400 E=0
1410 IF Q<0 THEN RETURN
1420 IF U<0 THEN RETURN
1430 IF A<1 THEN RETURN
1440 IF B<1 THEN RETURN
1450 IF Q+A>W THEN RETURN
1460 IF U+B>H THEN RETURN
1470 IF Q<>INT(Q) THEN RETURN
1480 IF U<>INT(U) THEN RETURN
1490 IF A<>INT(A) THEN RETURN
1500 IF B<>INT(B) THEN RETURN
1510 FOR K=U TO U+B-1
1520 FOR J=Q TO Q+A-1
1530 C=J:R=K:GOSUB 1000
1540 PRINT CHR$(48+T);
1550 NEXT J
1560 PRINT
1570 NEXT K
1580 E=1:RETURNNegative world positions and a non-positive S return C=R=T=-1 and E=0. Non-negative positions beyond the map retain their calculated C/R but return T=-1, E=0. Fractional tile coordinates and IDs are rejected. Positive fractional world positions are allowed and round down to a cell.
Own the state
Reserve the map/property arrays M()/P(), W/H/S/L and the inputs/outputs in the table. D is lookup scratch. Region drawing also consumes C/R/T/D/J/K, so do not call it inside a loop that needs J or K preserved. The routines use global BASIC state and are not re-entrant.
The region must fit completely inside the map and have positive integer dimensions. It starts at the current text cursor, prints character codes 48+T for the sample’s IDs 0–3, and ends each row with a newline. It does not position a window on screen, draw custom artwork or manage colour. Replace that small display adapter when choosing another tile set.
Changing the map does not redraw it. Draw the affected cell or region through your display adapter. Keep moving actors separately when the underlying floor must survive their movement.
Take the code
Numeric-array elements take this interpreter’s numeric storage, not one byte each. This version favours readable, editable data. Byte maps, packed IDs, ROM maps with a small change list and room-at-a-time decoding are useful alternatives when memory becomes tight. They need their own implementations and measurements. No complete-game frame rate follows from this text caller.
For the display itself, use native region drawing. It keeps map IDs separate from native character codes and colour memory.