|
3 | 3 | ' |
4 | 4 | ' Demonstrates SCREEN 4 (640x400 RGBA) for a desktop-style UI: |
5 | 5 | ' toolbar buttons, tile picker, map grid, mouse paint, plus |
6 | | -' binary file SAVE / LOAD of the edited map. |
| 6 | +' JSON file SAVE / LOAD against the canonical map format |
| 7 | +' (docs/map-format.md v1). |
7 | 8 | ' |
8 | 9 | ' Tile graphics come from walls.png grid of |
9 | 10 | ' 32x32 cells -> TILEMAP DRAW; 0 = blank). |
10 | 11 | ' |
11 | 12 | ' Render model: full-frame redraw. CLS each tick wipes both the |
12 | 13 | ' bitmap plane and the per-frame TILEMAP cell list (cap 4096), |
13 | 14 | ' so continuous paint can never overflow it. |
| 15 | +' |
| 16 | +' Save format: writes a v1 map.json file via PRINT# — a 10x10 |
| 17 | +' bg tile layer, no objects, no camera. Load uses the runtime's |
| 18 | +' MAPLOAD statement, which validates schema and fills MAP_BG(). |
14 | 19 | ' ============================================================ |
15 | 20 |
|
16 | 21 | SCREEN 4 |
@@ -43,7 +48,23 @@ BTN_QUIT_X0 = 592 : BTN_QUIT_Y0 = 4 : BTN_QUIT_X1 = 632 : BTN_QUIT_Y1 = 24 |
43 | 48 |
|
44 | 49 | DIM PICKER(PICK_COUNT) |
45 | 50 | DIM MAP(MAP_COUNT) |
| 51 | +' MAPLOAD targets — pre-DIMmed at MAP_COUNT so a JSON read fills |
| 52 | +' MAP_BG and we copy back into MAP(). Other MAP_* arrays exist so |
| 53 | +' MAPLOAD's writes don't crash on missing globals. |
| 54 | +DIM MAP_BG(MAP_COUNT - 1) |
| 55 | +DIM MAP_FG(MAP_COUNT - 1) |
| 56 | +DIM MAP_COLL(15) |
| 57 | +DIM MAP_OBJ_TYPE$(15) |
| 58 | +DIM MAP_OBJ_KIND$(15) |
| 59 | +DIM MAP_OBJ_X(15) |
| 60 | +DIM MAP_OBJ_Y(15) |
| 61 | +DIM MAP_OBJ_W(15) |
| 62 | +DIM MAP_OBJ_H(15) |
| 63 | +DIM MAP_OBJ_ID(15) |
| 64 | +DIM MAP_TILESET_ID$(7) |
| 65 | +DIM MAP_TILESET_SRC$(7) |
46 | 66 | SELECTED = 1 |
| 67 | +Q$ = CHR$(34) |
47 | 68 |
|
48 | 69 | ' Status line state — re-stamped every frame after CLS. |
49 | 70 | STATUS$ = "LEFT CLICK A TILE, THEN LEFT CLICK + DRAG ON THE MAP. RIGHT CLICK CLEARS." |
|
78 | 99 | IF MX >= BTN_QUIT_X0 AND MX <= BTN_QUIT_X1 AND MY >= BTN_QUIT_Y0 AND MY <= BTN_QUIT_Y1 THEN EXIT |
79 | 100 |
|
80 | 101 | IF MX >= BTN_SAVE_X0 AND MX <= BTN_SAVE_X1 AND MY >= BTN_SAVE_Y0 AND MY <= BTN_SAVE_Y1 THEN |
81 | | - OPEN 1, 1, 1, "map.bin" |
82 | | - FOR I = 0 TO MAP_COUNT - 1 |
83 | | - PUTBYTE #1, MAP(I) |
84 | | - NEXT I |
85 | | - CLOSE #1 |
86 | | - STATUS$ = "SAVED map.bin" |
| 102 | + SaveMapJson() |
| 103 | + STATUS$ = "SAVED map.json" |
87 | 104 | STATUS_R = 200 : STATUS_G = 255 : STATUS_B = 200 |
88 | 105 | END IF |
89 | 106 |
|
90 | 107 | IF MX >= BTN_LOAD_X0 AND MX <= BTN_LOAD_X1 AND MY >= BTN_LOAD_Y0 AND MY <= BTN_LOAD_Y1 THEN |
91 | | - OPEN 1, 1, 0, "map.bin" |
92 | | - IF ST = 1 THEN |
93 | | - STATUS$ = "NO map.bin YET - SAVE FIRST" |
94 | | - STATUS_R = 255 : STATUS_G = 200 : STATUS_B = 200 |
95 | | - ELSE |
96 | | - FOR I = 0 TO MAP_COUNT - 1 |
97 | | - GETBYTE #1, B |
98 | | - IF B >= 0 AND B <= PICK_COUNT THEN MAP(I) = B ELSE MAP(I) = 0 |
99 | | - NEXT I |
100 | | - CLOSE #1 |
101 | | - STATUS$ = "LOADED map.bin" |
102 | | - STATUS_R = 200 : STATUS_G = 200 : STATUS_B = 255 |
103 | | - END IF |
| 108 | + LoadMapJson() |
104 | 109 | END IF |
105 | 110 |
|
106 | 111 | IF MX >= PICKER_X AND MX < PICKER_X + PCOLS * TILE_SIZE AND MY >= PICKER_Y AND MY < PICKER_Y + PROWS * TILE_SIZE THEN |
@@ -207,3 +212,60 @@ FUNCTION DrawStatus() |
207 | 212 | COLORRGB STATUS_R, STATUS_G, STATUS_B |
208 | 213 | DRAWTEXT 8, 384, STATUS$ |
209 | 214 | END FUNCTION |
| 215 | + |
| 216 | +' ------------------------------------------------------------ |
| 217 | +' JSON save / load — canonical map format v1 |
| 218 | +' ------------------------------------------------------------ |
| 219 | + |
| 220 | +FUNCTION SaveMapJson() |
| 221 | + ' Writes a v1 map.json: format/size/tileSize/tilesets/layers[bg]. |
| 222 | + ' No object layer, no camera — this editor only paints a tile grid. |
| 223 | + OPEN 1, 1, 1, "map.json" |
| 224 | + PRINT#1, "{" |
| 225 | + PRINT#1, " " + Q$ + "format" + Q$ + ": 1," |
| 226 | + PRINT#1, " " + Q$ + "id" + Q$ + ": " + Q$ + "editor-map" + Q$ + "," |
| 227 | + PRINT#1, " " + Q$ + "size" + Q$ + ": { " + Q$ + "cols" + Q$ + ": " + STR$(MCOLS) + ", " + Q$ + "rows" + Q$ + ": " + STR$(MROWS) + " }," |
| 228 | + PRINT#1, " " + Q$ + "tileSize" + Q$ + ": { " + Q$ + "w" + Q$ + ": 32, " + Q$ + "h" + Q$ + ": 32 }," |
| 229 | + PRINT#1, " " + Q$ + "tilesets" + Q$ + ": [" |
| 230 | + PRINT#1, " { " + Q$ + "id" + Q$ + ": " + Q$ + "walls" + Q$ + ", " + Q$ + "src" + Q$ + ": " + Q$ + "walls.png" + Q$ + ", " + Q$ + "cellW" + Q$ + ": 32, " + Q$ + "cellH" + Q$ + ": 32 }" |
| 231 | + PRINT#1, " ]," |
| 232 | + PRINT#1, " " + Q$ + "layers" + Q$ + ": [" |
| 233 | + PRINT#1, " { " + Q$ + "name" + Q$ + ": " + Q$ + "bg" + Q$ + ", " + Q$ + "type" + Q$ + ": " + Q$ + "tiles" + Q$ + ", " + Q$ + "tilesetId" + Q$ + ": " + Q$ + "walls" + Q$ + "," |
| 234 | + PRINT#1, " " + Q$ + "data" + Q$ + ": [" |
| 235 | + FOR SI = 0 TO MAP_COUNT - 1 |
| 236 | + IF SI < MAP_COUNT - 1 THEN |
| 237 | + PRINT#1, " " + STR$(MAP(SI)) + "," |
| 238 | + ELSE |
| 239 | + PRINT#1, " " + STR$(MAP(SI)) |
| 240 | + END IF |
| 241 | + NEXT SI |
| 242 | + PRINT#1, " ]" |
| 243 | + PRINT#1, " }" |
| 244 | + PRINT#1, " ]" |
| 245 | + PRINT#1, "}" |
| 246 | + CLOSE #1 |
| 247 | +END FUNCTION |
| 248 | + |
| 249 | +FUNCTION LoadMapJson() |
| 250 | + ' Probe for the file first — MAPLOAD raises a runtime error on a |
| 251 | + ' missing path, which would kill the editor. ST=1 after OPEN signals |
| 252 | + ' "not found"; bail out with a friendly status instead. |
| 253 | + OPEN 1, 1, 0, "map.json" |
| 254 | + IF ST = 1 THEN |
| 255 | + STATUS$ = "NO map.json YET - SAVE FIRST" |
| 256 | + STATUS_R = 255 : STATUS_G = 200 : STATUS_B = 200 |
| 257 | + RETURN 0 |
| 258 | + END IF |
| 259 | + CLOSE #1 |
| 260 | + MAPLOAD "map.json" |
| 261 | + IF MAP_W = MCOLS AND MAP_H = MROWS THEN |
| 262 | + FOR LI = 0 TO MAP_COUNT - 1 |
| 263 | + MAP(LI) = MAP_BG(LI) |
| 264 | + NEXT LI |
| 265 | + STATUS$ = "LOADED map.json" |
| 266 | + STATUS_R = 200 : STATUS_G = 200 : STATUS_B = 255 |
| 267 | + ELSE |
| 268 | + STATUS$ = "MAP SIZE MISMATCH - EXPECTED " + STR$(MCOLS) + "x" + STR$(MROWS) |
| 269 | + STATUS_R = 255 : STATUS_G = 200 : STATUS_B = 200 |
| 270 | + END IF |
| 271 | +END FUNCTION |
0 commit comments