├── LICENSE ├── game.c ├── game.h ├── platform_win32.c ├── platform_x11.c ├── readme.md ├── spritesheet.h └── tools ├── spritegen.c ├── spritesheet.png └── stb_image.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Noel Berry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /game.c: -------------------------------------------------------------------------------- 1 | #include "game.h" 2 | #include "spritesheet.h" 3 | 4 | /* Platform Implementations */ 5 | #include "platform_win32.c" 6 | #include "platform_x11.c" 7 | 8 | /* Global Variables */ 9 | Game nb_game; 10 | const Entity nb_zero_entity; 11 | const EntityType nb_entity_types[] = { 12 | nb_player, nb_coin, nb_pop, NB_NULL 13 | }; 14 | 15 | /* Begin Game API */ 16 | 17 | void nb_init() 18 | { 19 | NB_INT i, j; 20 | 21 | /* Zero out entities */ 22 | for (i = 0; i < NB_ENTITIES; i ++) 23 | nb_game.entities[i] = nb_zero_entity; 24 | 25 | /* Zero out tiles with solids along room edges */ 26 | for (i = 0; i < NB_ROOM_COLS; i ++) 27 | for (j = 0; j < NB_ROOM_ROWS; j ++) 28 | nb_game.tiles[i][j] = (i == 0 || j == 0 || i == NB_ROOM_COLS - 1 || j == NB_ROOM_ROWS - 1); 29 | 30 | /* Default Palette */ 31 | nb_game.palette[NB_COL_0] = 0x30436a; 32 | nb_game.palette[NB_COL_1] = 0xff8d78; 33 | nb_game.palette[NB_COL_2] = 0xe8c562; 34 | nb_game.palette[NB_COL_3] = 0xd4cdff; 35 | 36 | /* Create some entities */ 37 | nb_en_create(nb_player, NB_ROOM_WIDTH / 2, NB_ROOM_HEIGHT / 2); 38 | for (i = 0; i < 7; i ++) 39 | { 40 | nb_en_create(nb_coin, 32 + i * 32, 32); 41 | nb_en_create(nb_coin, 32 + i * 32, NB_ROOM_HEIGHT - 32); 42 | } 43 | } 44 | 45 | void nb_step() 46 | { 47 | NB_INT i, j, nx, ny; 48 | EntityEvent ev; 49 | 50 | /* run entities */ 51 | ev.type = NB_ENTITY_UPDATE; 52 | for (i = 0; i < NB_ENTITIES; i ++) 53 | { 54 | ev.self = nb_game.entities + i; 55 | 56 | if (ev.self->type == NB_NULL) 57 | continue; 58 | 59 | ev.self->timer += 1.0f / 60.0f; 60 | 61 | /* get move amount */ 62 | nx = ev.self->sx + ev.self->rx; 63 | ny = ev.self->sy + ev.self->ry; 64 | 65 | /* try to move */ 66 | if ((ev.self->flags & NB_ENTITY_HITS_SOLIDS) != 0) 67 | { 68 | j = nx; 69 | while (j != 0) 70 | { 71 | ev.self->x += nb_sign(j); 72 | if (nb_en_solids(ev.self)) 73 | { 74 | ev.self->sx = ev.self->rx = nx = 0; 75 | ev.self->x -= nb_sign(j); 76 | break; 77 | } 78 | j -= nb_sign(j); 79 | } 80 | 81 | j = ny; 82 | while (j != 0) 83 | { 84 | ev.self->y += nb_sign(j); 85 | if (nb_en_solids(ev.self)) 86 | { 87 | ev.self->sy = ev.self->ry = ny = 0; 88 | ev.self->y -= nb_sign(j); 89 | break; 90 | } 91 | j -= nb_sign(j); 92 | } 93 | } 94 | else 95 | { 96 | ev.self->x += nx; 97 | ev.self->y += ny; 98 | } 99 | 100 | /* update move remainder */ 101 | ev.self->rx = ev.self->sx + ev.self->rx - nx; 102 | ev.self->ry = ev.self->sy + ev.self->ry - ny; 103 | 104 | /* call their update event */ 105 | ev.self->type(&ev); 106 | } 107 | 108 | /* do overlap tests */ 109 | ev.type = NB_ENTITY_OVERLAP; 110 | for (i = 0; i < NB_ENTITIES; i ++) 111 | { 112 | ev.self = nb_game.entities + i; 113 | 114 | if (ev.self->type == NB_NULL || ev.self->bw < 0 || ev.self->bh <= 0 || (ev.self->flags & NB_ENTITY_OVERLAP_CHECKS) == 0) 115 | continue; 116 | 117 | for (j = 0; j < NB_ENTITIES; j ++) 118 | { 119 | ev.other = nb_game.entities + j; 120 | if (i == j || ev.other->type == NB_NULL || ev.other->bw < 0 || ev.other->bh <= 0 || (ev.other->flags & NB_ENTITY_OVERLAP_CHECKS) == 0) 121 | continue; 122 | if (nb_en_overlaps(ev.self, ev.other)) 123 | ev.self->type(&ev); 124 | } 125 | } 126 | 127 | /* make sure camera is inside the room bounds */ 128 | nb_game.cam_x = nb_max(0, nb_min(NB_ROOM_WIDTH - NB_WIDTH, nb_game.cam_x)); 129 | nb_game.cam_y = nb_max(0, nb_min(NB_ROOM_HEIGHT - NB_HEIGHT, nb_game.cam_y)); 130 | 131 | /* draw bg */ 132 | nb_clear(NB_COL_0); 133 | for (i = nb_game.cam_x / 32; i <= (nb_game.cam_x + NB_WIDTH) / 32; i ++) 134 | nb_rect(i * 32, nb_game.cam_y, 1, NB_HEIGHT, NB_COL_1); 135 | for (i = nb_game.cam_y / 32; i <= (nb_game.cam_y + NB_HEIGHT) / 32; i ++) 136 | nb_rect(nb_game.cam_x, i * 32, NB_WIDTH, 1, NB_COL_1); 137 | 138 | /* draw solids */ 139 | for (i = 0; i < NB_ROOM_COLS; i ++) 140 | for (j = 0; j < NB_ROOM_ROWS; j ++) 141 | if (nb_game.tiles[i][j]) 142 | nb_rect(i * NB_GRID_SIZE, j * NB_GRID_SIZE, NB_GRID_SIZE, NB_GRID_SIZE, NB_COL_3); 143 | 144 | /* draw entities */ 145 | ev.type = NB_ENTITY_DRAW; 146 | for (i = 0; i < NB_ENTITIES; i ++) 147 | { 148 | ev.self = nb_game.entities + i; 149 | if (ev.self->type != NB_NULL) 150 | ev.self->type(&ev); 151 | } 152 | 153 | /* store previous button state */ 154 | for (i = 0; i < NB_BUTTON_COUNT; i ++) 155 | nb_game.btn_prev[i] = nb_game.btn[i]; 156 | } 157 | 158 | Entity* nb_en_create(EntityType type, NB_INT x, NB_INT y) 159 | { 160 | NB_UINT id = 0; 161 | EntityEvent ev; 162 | 163 | for (id = 0; id < NB_ENTITIES; id ++) 164 | if (nb_game.entities[id].type == NB_NULL) 165 | { 166 | ev.type = NB_ENTITY_INIT; 167 | ev.self = nb_game.entities + id; 168 | *ev.self = nb_zero_entity; 169 | ev.self->type = type; 170 | ev.self->x = x; 171 | ev.self->y = y; 172 | ev.self->timer = 0.0f; 173 | ev.self->type(&ev); 174 | break; 175 | } 176 | 177 | return (id >= 0 && id < NB_ENTITIES ? nb_game.entities + id : NB_NULL); 178 | } 179 | 180 | NB_BOOL nb_en_overlaps(Entity* a, Entity* b) 181 | { 182 | if (a->x + a->bx < b->x + b->bx + b->bw && 183 | a->y + a->by < b->y + b->by + b->bh && 184 | a->x + a->bx + a->bw > b->x + b->bx && 185 | a->y + a->by + a->bh > b->y + b->by) 186 | return NB_TRUE; 187 | return NB_FALSE; 188 | } 189 | 190 | NB_BOOL nb_en_solids(Entity* a) 191 | { 192 | NB_INT l, r, t, b, x, y; 193 | l = nb_max(0, (a->x + a->bx) / NB_GRID_SIZE); 194 | r = nb_min(NB_ROOM_COLS - 1, (a->x + a->bx + a->bw - 1) / NB_GRID_SIZE); 195 | t = nb_max(0, (a->y + a->by) / NB_GRID_SIZE); 196 | b = nb_min(NB_ROOM_ROWS - 1, (a->y + a->by + a->bh - 1) / NB_GRID_SIZE); 197 | 198 | for (x = l; x <= r; x ++) 199 | for (y = t; y <= b; y ++) 200 | if (nb_game.tiles[x][y]) return NB_TRUE; 201 | return NB_FALSE; 202 | } 203 | 204 | void nb_en_destroy(Entity* entity) 205 | { 206 | EntityEvent ev; 207 | NB_INT id = entity - nb_game.entities; 208 | if (id >= 0 && id < NB_ENTITIES && nb_game.entities[id].type != NB_NULL) 209 | { 210 | ev.type = NB_ENTITY_DESTROY; 211 | ev.self = nb_game.entities + id; 212 | ev.self->type(&ev); 213 | ev.self->type = NB_NULL; 214 | } 215 | } 216 | 217 | void nb_clear(NB_COL color) 218 | { 219 | NB_COL* end = nb_game.screen + NB_WIDTH * NB_HEIGHT; 220 | NB_COL* at = nb_game.screen; 221 | 222 | while (at < end) 223 | { 224 | *at = color; 225 | at++; 226 | } 227 | } 228 | 229 | void nb_rect(NB_INT x, NB_INT y, NB_INT w, NB_INT h, NB_COL color) 230 | { 231 | NB_INT px, py, l, t, r, b; 232 | 233 | x -= nb_game.cam_x; y -= nb_game.cam_y; 234 | l = nb_max(0, x); 235 | t = nb_max(0, y); 236 | r = nb_min(NB_WIDTH, x + w); 237 | b = nb_min(NB_HEIGHT, y + h); 238 | 239 | for (py = t; py < b; py ++) 240 | for (px = l; px < r; px ++) 241 | nb_game.screen[px + py * NB_WIDTH] = color; 242 | } 243 | 244 | void nb_spr(NB_INT x, NB_INT y, NB_INT spr_x, NB_INT spr_y, NB_INT spr_w, NB_INT spr_h) 245 | { 246 | nb_spr_ext(x, y, spr_x, spr_y, spr_w, spr_h, NB_FALSE, NB_FALSE); 247 | } 248 | 249 | void nb_spr_ext(NB_INT x, NB_INT y, NB_INT spr_x, NB_INT spr_y, NB_INT spr_w, NB_INT spr_h, NB_BOOL flip_x, NB_BOOL flip_y) 250 | { 251 | NB_INT px, py, dst_x, dst_y, src; 252 | 253 | x -= nb_game.cam_x; 254 | y -= nb_game.cam_y; 255 | spr_x = nb_max(0, spr_x * NB_GRID_SIZE); 256 | spr_y = nb_max(0, spr_y * NB_GRID_SIZE); 257 | spr_w = nb_min(NB_SPRITESHEET_WIDTH - spr_x, spr_w * NB_GRID_SIZE); 258 | spr_h = nb_min(NB_SPRITESHEET_HEIGHT - spr_y, spr_h * NB_GRID_SIZE); 259 | flip_x = (flip_x ? 1 : 0); 260 | flip_y = (flip_y ? 1 : 0); 261 | 262 | /* todo: clamp this to screen bounds before we 263 | * begin iterating, to avoid the inner if statement 264 | */ 265 | for (px = 0; px < spr_w; px++) 266 | for (py = 0; py < spr_h; py++) 267 | { 268 | dst_x = x + px; 269 | dst_y = y + py; 270 | src = (spr_x + px + (spr_w - 2 * px) * flip_x); 271 | src += (spr_y + py + (spr_h - 2 * py) * flip_y) * NB_SPRITESHEET_WIDTH; 272 | if (dst_x >= 0 && dst_y >= 0 && dst_x < NB_WIDTH && dst_y < NB_HEIGHT && nb_spritesheet[src] < NB_PALETTE) 273 | nb_game.screen[dst_x + dst_y * NB_WIDTH] = nb_spritesheet[src]; 274 | } 275 | } 276 | 277 | NB_BOOL nb_down(Buttons btn) { return nb_game.btn[btn]; } 278 | NB_BOOL nb_pressed(Buttons btn) { return !nb_game.btn_prev[btn] && nb_game.btn[btn]; } 279 | NB_BOOL nb_released(Buttons btn) { return nb_game.btn_prev[btn] && !nb_game.btn[btn]; } 280 | 281 | /* Begin Entity Types */ 282 | 283 | void nb_player(EntityEvent* ev) 284 | { 285 | const NB_FLT max_speed = 3; 286 | const NB_FLT accel = 20; 287 | const NB_FLT friction = 15; 288 | 289 | Entity* self = ev->self; 290 | NB_INT frame = 0; 291 | 292 | if (ev->type == NB_ENTITY_INIT) 293 | { 294 | self->bx = -4; 295 | self->by = -8; 296 | self->bw = 8; 297 | self->bh = 8; 298 | self->flags = NB_ENTITY_OVERLAP_CHECKS | NB_ENTITY_HITS_SOLIDS; 299 | } 300 | else if (ev->type == NB_ENTITY_UPDATE) 301 | { 302 | if (nb_down(NB_LEFT)) 303 | { 304 | nb_appr(self->sx, -max_speed, accel * NB_DELTA); 305 | self->flags |= NB_ENTITY_FACING_LEFT; 306 | } 307 | else if (nb_down(NB_RIGHT)) 308 | { 309 | nb_appr(self->sx, max_speed, accel * NB_DELTA); 310 | self->flags &= ~NB_ENTITY_FACING_LEFT; 311 | } 312 | else 313 | nb_appr(self->sx, 0, friction * NB_DELTA); 314 | 315 | if (nb_down(NB_UP)) 316 | nb_appr(self->sy, -max_speed, accel * NB_DELTA); 317 | else if (nb_down(NB_DOWN)) 318 | nb_appr(self->sy, max_speed, accel * NB_DELTA); 319 | else 320 | nb_appr(self->sy, 0, friction * NB_DELTA); 321 | 322 | /* make camera approach player */ 323 | nb_game.cam_x += ((self->x + self->bx + self->bw / 2 - NB_WIDTH / 2) - nb_game.cam_x) / 10; 324 | nb_game.cam_y += ((self->y + self->by + self->bh / 2 - NB_HEIGHT / 2) - nb_game.cam_y) / 10; 325 | } 326 | else if (ev->type == NB_ENTITY_OVERLAP) 327 | { 328 | if (ev->other->type == nb_coin) 329 | nb_en_destroy(ev->other); 330 | } 331 | else if (ev->type == NB_ENTITY_DRAW) 332 | { 333 | frame = ((NB_INT)(self->timer * 8) % 2); 334 | if (self->sx == 0 && self->sy == 0) frame = 0; 335 | nb_spr_ext(self->x - 8, self->y - 16, frame * 2, 0, 2, 2, self->flags & NB_ENTITY_FACING_LEFT ? 1 : 0, NB_FALSE); 336 | } 337 | } 338 | 339 | void nb_coin(EntityEvent* ev) 340 | { 341 | Entity* self = ev->self; 342 | Entity* other; 343 | NB_INT frame; 344 | 345 | if (ev->type == NB_ENTITY_INIT) 346 | { 347 | self->bx = -2; 348 | self->by = -2; 349 | self->bw = 4; 350 | self->bh = 4; 351 | self->flags = NB_ENTITY_OVERLAP_CHECKS; 352 | } 353 | else if (ev->type == NB_ENTITY_DRAW) 354 | { 355 | frame = ((NB_INT)(self->timer * 8) % 4); 356 | nb_spr_ext(self->x - 4, self->y - 4, 4 + frame % 2, 0, 1, 1, frame > 1, 0); 357 | } 358 | else if (ev->type == NB_ENTITY_DESTROY) 359 | { 360 | other = nb_en_create(nb_pop, self->x, self->y); 361 | other->sx = 4; 362 | other->sy = 4; 363 | other = nb_en_create(nb_pop, self->x, self->y); 364 | other->sx = -4; 365 | other->sy = 4; 366 | other = nb_en_create(nb_pop, self->x, self->y); 367 | other->sx = 4; 368 | other->sy = -4; 369 | other = nb_en_create(nb_pop, self->x, self->y); 370 | other->sx = -4; 371 | other->sy = -4; 372 | } 373 | } 374 | 375 | void nb_pop(EntityEvent* ev) 376 | { 377 | Entity* self = ev->self; 378 | 379 | if (ev->type == NB_ENTITY_INIT) 380 | { 381 | self->bw = 0; 382 | self->bh = 0; 383 | } 384 | else if (ev->type == NB_ENTITY_UPDATE) 385 | { 386 | nb_appr(self->sx, 0, 32 * NB_DELTA); 387 | nb_appr(self->sy, 0, 32 * NB_DELTA); 388 | if (self->timer > 0.20f) 389 | nb_en_destroy(self); 390 | } 391 | else if (ev->type == NB_ENTITY_DRAW) 392 | { 393 | nb_rect(self->x - 1, self->y - 1, 2, 2, NB_COL_2); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /game.h: -------------------------------------------------------------------------------- 1 | #ifndef NB_GAME_H 2 | #define NB_GAME_H 3 | 4 | /* Types */ 5 | #define NB_FLT float 6 | #define NB_INT int 7 | #define NB_UINT unsigned int 8 | #define NB_COL unsigned char 9 | #define NB_RGB unsigned int 10 | #define NB_BOOL int 11 | #define NB_TRUE 1 12 | #define NB_FALSE 0 13 | #define NB_STR const char* 14 | #define NB_NULL 0 15 | 16 | /* Constants */ 17 | #define NB_TITLE "Game" 18 | #define NB_WIDTH 240 19 | #define NB_HEIGHT 135 20 | #define NB_FRAMERATE 60 21 | #define NB_ENTITIES 64 22 | #define NB_ROOM_WIDTH 256 23 | #define NB_ROOM_HEIGHT 256 24 | #define NB_GRID_SIZE 8 25 | #define NB_PALETTE 4 26 | #define NB_ROOM_COLS (NB_ROOM_WIDTH / NB_GRID_SIZE) 27 | #define NB_ROOM_ROWS (NB_ROOM_HEIGHT / NB_GRID_SIZE) 28 | #define NB_DELTA (1.0f / NB_FRAMERATE) 29 | 30 | #define NB_COL_0 0 31 | #define NB_COL_1 1 32 | #define NB_COL_2 2 33 | #define NB_COL_3 3 34 | #define NB_TRANSPARENT 4 35 | 36 | /* Controls */ 37 | typedef enum Buttons 38 | { 39 | NB_LEFT, 40 | NB_RIGHT, 41 | NB_UP, 42 | NB_DOWN, 43 | NB_A, 44 | NB_B, 45 | NB_BUTTON_COUNT 46 | } Buttons; 47 | 48 | /* Entity */ 49 | struct Entity; 50 | struct EntityEvent; 51 | typedef void (*EntityType)(struct EntityEvent*); /* entity type is just a pointer to a behavior function */ 52 | 53 | typedef struct Entity 54 | { 55 | EntityType type; /* entity type; a pointer to a behavior function */ 56 | NB_INT x, y; /* position */ 57 | NB_FLT sx, sy; /* speed */ 58 | NB_FLT rx, ry; /* movement remainder */ 59 | NB_INT bx, by, bw, bh; /* bounding box */ 60 | NB_FLT timer; /* general timer, starts at 0 on create */ 61 | NB_UINT flags; /* general entity flags */ 62 | } Entity; 63 | 64 | typedef enum EntityFlags 65 | { 66 | NB_ENTITY_HITS_SOLIDS = 1 << 0, 67 | NB_ENTITY_OVERLAP_CHECKS = 1 << 1, 68 | NB_ENTITY_FACING_LEFT = 1 << 2 69 | } EntityFlags; 70 | 71 | /* Entity Event */ 72 | typedef enum EntityEventType 73 | { 74 | NB_ENTITY_INIT, 75 | NB_ENTITY_UPDATE, 76 | NB_ENTITY_DRAW, 77 | NB_ENTITY_DESTROY, 78 | NB_ENTITY_OVERLAP 79 | } EntityEventType; 80 | 81 | typedef struct EntityEvent 82 | { 83 | EntityEventType type; 84 | Entity* self; 85 | Entity* other; 86 | } EntityEvent; 87 | 88 | typedef struct Game 89 | { 90 | NB_FLT cam_x, cam_y; 91 | NB_BOOL btn[NB_BUTTON_COUNT]; 92 | NB_BOOL btn_prev[NB_BUTTON_COUNT]; 93 | NB_BOOL tiles[NB_ROOM_COLS][NB_ROOM_ROWS]; 94 | NB_COL screen[NB_WIDTH * NB_HEIGHT]; 95 | NB_RGB palette[NB_PALETTE]; 96 | Entity entities[NB_ENTITIES]; 97 | } Game; 98 | 99 | /* Globals */ 100 | extern Game nb_game; 101 | extern const Entity nb_zero_entity; 102 | extern const EntityType nb_entity_types[]; 103 | 104 | /* Game API */ 105 | void nb_init (); 106 | void nb_step (); 107 | Entity* nb_en_create (EntityType type, NB_INT x, NB_INT y); 108 | NB_BOOL nb_en_overlaps (Entity* a, Entity* b); 109 | NB_BOOL nb_en_solids (Entity* entity); 110 | void nb_en_destroy (Entity* entity); 111 | void nb_clear (NB_COL color); 112 | void nb_rect (NB_INT x, NB_INT y, NB_INT w, NB_INT h, NB_COL color); 113 | void nb_spr (NB_INT x, NB_INT y, NB_INT spr_x, NB_INT spr_y, NB_INT spr_w, NB_INT spr_h); 114 | void nb_spr_ext (NB_INT x, NB_INT y, NB_INT spr_x, NB_INT spr_y, NB_INT spr_w, NB_INT spr_h, NB_BOOL flip_x, NB_BOOL flip_y); 115 | NB_BOOL nb_down (Buttons btn); 116 | NB_BOOL nb_pressed (Buttons btn); 117 | NB_BOOL nb_released (Buttons btn); 118 | 119 | /* Math Functions */ 120 | #define nb_min(x, y) ((x) < (y) ? (x) : (y)) 121 | #define nb_max(x, y) ((x) > (y) ? (x) : (y)) 122 | #define nb_appr(val, tar, delta) val = (val > (tar) ? nb_max(tar, val - delta) : nb_min(tar, val + delta)) 123 | #define nb_sign(x) ((x) > 0 ? 1 : ((x) < 0 ? -1 : 0)) 124 | 125 | /* Entity Types */ 126 | void nb_player(EntityEvent*); 127 | void nb_coin(EntityEvent*); 128 | void nb_pop(EntityEvent*); 129 | 130 | #endif -------------------------------------------------------------------------------- /platform_win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Note: Do not compile this file! 3 | * It is included in game.c 4 | * game.c should be the only file you compile 5 | */ 6 | 7 | #ifdef _WIN32 8 | 9 | #include "game.h" 10 | #include 11 | 12 | struct Platform 13 | { 14 | NB_FLT framecounter; 15 | NB_BOOL closing; 16 | HWND window; 17 | NB_RGB buffer[NB_WIDTH * NB_HEIGHT]; 18 | HDC buffer_memory; 19 | HBITMAP buffer_bitmap; 20 | HBRUSH bg_brush; 21 | } platform; 22 | 23 | LRESULT CALLBACK nb_win32_poll(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 24 | 25 | void nb_win32_run() 26 | { 27 | INT x, y; 28 | WNDCLASS wc; 29 | HDC dc; 30 | MSG msg; 31 | BITMAPINFO bminfo; 32 | 33 | /* Background Color is black */ 34 | platform.bg_brush = CreateSolidBrush(RGB(0, 0, 0)); 35 | platform.closing = NB_FALSE; 36 | platform.framecounter = 0; 37 | 38 | /* Create Window Class */ 39 | wc.lpfnWndProc = DefWindowProc; 40 | wc.lpszClassName = "NB_WINDOW"; 41 | wc.lpfnWndProc = nb_win32_poll; 42 | RegisterClass(&wc); 43 | 44 | /* Open Window */ 45 | platform.window = CreateWindow("NB_WINDOW", NB_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, NB_WIDTH * 4, NB_HEIGHT * 4, NULL, NULL, NULL, NULL); 46 | ShowWindow(platform.window, SW_SHOW); 47 | 48 | /* create screen buffer */ 49 | dc = GetDC(platform.window); 50 | platform.buffer_memory = CreateCompatibleDC(dc); 51 | platform.buffer_bitmap = CreateCompatibleBitmap(dc, NB_WIDTH, NB_HEIGHT); 52 | SelectObject(platform.buffer_memory, platform.buffer_bitmap); 53 | ReleaseDC(platform.window, dc); 54 | 55 | /* run the game */ 56 | nb_init(); 57 | while (!platform.closing) 58 | { 59 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 60 | { 61 | TranslateMessage(&msg); 62 | DispatchMessage(&msg); 63 | } 64 | 65 | nb_step(); 66 | 67 | /* Copy Palette Screen data to our RGB buffer */ 68 | for (x = 0; x < NB_WIDTH; x ++) 69 | for (y = 0; y < NB_HEIGHT; y ++) 70 | platform.buffer[x + y * NB_WIDTH] = nb_game.palette[nb_game.screen[x + y * NB_WIDTH]]; 71 | 72 | /* Copy to buffer */ 73 | bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader); 74 | bminfo.bmiHeader.biWidth = NB_WIDTH; 75 | bminfo.bmiHeader.biHeight = -(NB_INT)NB_HEIGHT; 76 | bminfo.bmiHeader.biPlanes = 1; 77 | bminfo.bmiHeader.biBitCount = 32; 78 | bminfo.bmiHeader.biCompression = BI_RGB; 79 | bminfo.bmiHeader.biXPelsPerMeter = 1; 80 | bminfo.bmiHeader.biYPelsPerMeter = 1; 81 | SetDIBits(platform.buffer_memory, platform.buffer_bitmap, 0, NB_HEIGHT, platform.buffer, &bminfo, 0); 82 | 83 | /* Tell WM_PAINT to run */ 84 | InvalidateRect(platform.window, NULL, FALSE); 85 | 86 | /* Faking V-Sync */ 87 | platform.framecounter += (1000.0 / NB_FRAMERATE); 88 | Sleep((INT)platform.framecounter); 89 | platform.framecounter -= (INT)platform.framecounter; 90 | } 91 | 92 | /* shutdown */ 93 | DeleteObject(platform.bg_brush); 94 | DeleteDC(platform.buffer_memory); 95 | DeleteObject(platform.buffer_bitmap); 96 | DestroyWindow(platform.window); 97 | } 98 | 99 | LRESULT CALLBACK nb_win32_poll(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 100 | { 101 | PAINTSTRUCT ps; 102 | HDC dc; 103 | FLOAT s; 104 | RECT size, fill, client; 105 | 106 | switch (msg) 107 | { 108 | case WM_CLOSE: 109 | platform.closing = NB_TRUE; 110 | return 0; 111 | case WM_DESTROY: 112 | PostQuitMessage(0); 113 | return 0; 114 | case WM_KEYDOWN: 115 | if (wParam == VK_RIGHT) nb_game.btn[NB_RIGHT] = NB_TRUE; 116 | if (wParam == VK_LEFT) nb_game.btn[NB_LEFT] = NB_TRUE; 117 | if (wParam == VK_UP) nb_game.btn[NB_UP] = NB_TRUE; 118 | if (wParam == VK_DOWN) nb_game.btn[NB_DOWN] = NB_TRUE; 119 | return 0; 120 | case WM_KEYUP: 121 | if (wParam == VK_RIGHT) nb_game.btn[NB_RIGHT] = NB_FALSE; 122 | if (wParam == VK_LEFT) nb_game.btn[NB_LEFT] = NB_FALSE; 123 | if (wParam == VK_UP) nb_game.btn[NB_UP] = NB_FALSE; 124 | if (wParam == VK_DOWN) nb_game.btn[NB_DOWN] = NB_FALSE; 125 | return 0; 126 | case WM_PAINT: 127 | dc = BeginPaint(hwnd, &ps); 128 | 129 | /* Get Screen Size */ 130 | GetClientRect(platform.window, &client); 131 | s = (INT)min((client.right - client.left) / (FLOAT)NB_WIDTH, (client.bottom - client.top) / (FLOAT)NB_HEIGHT); 132 | size.left = ((client.right - client.left) - NB_WIDTH * s) / 2; 133 | size.top = ((client.bottom - client.top) - NB_HEIGHT * s) / 2; 134 | size.right = size.left + NB_WIDTH * s; 135 | size.bottom = size.top + NB_HEIGHT * s; 136 | 137 | /* Fill outside with black */ 138 | SetRect(&fill, 0, 0, size.left, client.bottom); FillRect(dc, &fill, platform.bg_brush); 139 | SetRect(&fill, size.right, 0, client.right, client.bottom); FillRect(dc, &fill, platform.bg_brush); 140 | SetRect(&fill, size.left, 0, size.right, size.top); FillRect(dc, &fill, platform.bg_brush); 141 | SetRect(&fill, size.left, size.bottom, size.right, client.bottom); FillRect(dc, &fill, platform.bg_brush); 142 | 143 | /* Display screen on window */ 144 | StretchBlt(dc, size.left, size.top, size.right - size.left, size.bottom - size.top, 145 | platform.buffer_memory, 0, 0, NB_WIDTH, NB_HEIGHT, SRCCOPY); 146 | 147 | EndPaint(hwnd, &ps); 148 | return 0; 149 | } 150 | 151 | return DefWindowProc(hwnd, msg, wParam, lParam); 152 | } 153 | 154 | /* without libc wrapping it, _start() is the correct entry point */ 155 | void _start(void) 156 | { 157 | nb_win32_run(); 158 | } 159 | 160 | #endif /* _WIN32 */ -------------------------------------------------------------------------------- /platform_x11.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NoelFB/c89_nolib/398f1ae6b9a3ad267b05ed44c03a780bc507d5f6/platform_x11.c -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | a very simple experiment to see if i can make a game in C89 without the standard library. 2 | 3 | Windows example building with GCC: 4 | - only compile `game.c`, it includes everything else. 5 | - use the equivalent of `-nostdlib` and `-std=c89` 6 | ``` 7 | gcc game.c -nostdlib -std=c89 -e _start C:\Windows\System32\user32.dll C:\Windows\System32\Gdi32.dll C:\Windows\System32\kernel32.dll 8 | ``` 9 | 10 | For non-Windows, a different Platform implementation must to be defined, which in turn should call `nb_init` and `nb_step`. -------------------------------------------------------------------------------- /spritesheet.h: -------------------------------------------------------------------------------- 1 | #ifndef NB_SPRITESHEET_H 2 | #define NB_SPRITESHEET_H 3 | 4 | /* This file is generated automatically via `spritegen.c` in tools */ 5 | 6 | #define NB_SPRITESHEET_WIDTH 64 7 | #define NB_SPRITESHEET_HEIGHT 64 8 | const unsigned char nb_spritesheet[] = { 9 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 10 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 2, 2, 0, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 2, 0, 2, 2, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 4, 4, 4, 4, 4, 0, 2, 0, 2, 2, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 13 | 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 3, 3, 0, 4, 4, 4, 4, 0, 2, 0, 2, 2, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 14 | 4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 3, 3, 0, 4, 4, 4, 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 0, 2, 0, 2, 2, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 15 | 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 0, 0, 2, 2, 0, 0, 4, 4, 4, 0, 2, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16 | 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 4, 4, 0, 3, 3, 0, 3, 3, 3, 0, 3, 0, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17 | 4, 4, 4, 0, 3, 3, 0, 3, 3, 3, 0, 3, 0, 4, 4, 4, 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 18 | 4, 4, 4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 19 | 4, 4, 4, 0, 3, 3, 3, 3, 0, 0, 0, 3, 3, 0, 4, 4, 4, 4, 4, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 20 | 4, 4, 4, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 4, 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 21 | 4, 4, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 22 | 4, 4, 0, 3, 0, 3, 3, 3, 3, 3, 0, 3, 0, 4, 4, 4, 4, 4, 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 23 | 4, 4, 4, 0, 0, 3, 0, 0, 0, 3, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24 | 4, 4, 4, 0, 3, 3, 0, 4, 0, 3, 3, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 27 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 28 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 29 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 30 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 31 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 32 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 33 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 34 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 35 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 36 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 37 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 38 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 39 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 40 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 41 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 42 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 43 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 44 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 45 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 46 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 47 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 48 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 49 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 50 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 51 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 52 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 53 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 54 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 55 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 56 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 57 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 58 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 59 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 60 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 61 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 62 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 63 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 64 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 65 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 66 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 67 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 68 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 69 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 70 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 71 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 72 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 73 | }; 74 | #endif 75 | -------------------------------------------------------------------------------- /tools/spritegen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a tool to convert PNG files into a 3 | * simple Palette-based C array. It uses the standard 4 | * library for simplicity. 5 | */ 6 | 7 | #define STB_IMAGE_IMPLEMENTATION 8 | #include "stb_image.h" 9 | #include 10 | 11 | int main() 12 | { 13 | int x, y, w, h, c; 14 | stbi_uc* image; 15 | FILE* file; 16 | 17 | image = stbi_load("tools/spritesheet.png", &w, &h, &c, 4); 18 | file = fopen("spritesheet.h", "w"); 19 | 20 | fprintf(file, "#ifndef NB_SPRITESHEET_H\n"); 21 | fprintf(file, "#define NB_SPRITESHEET_H\n\n"); 22 | fprintf(file, "/* This file is generated automatically via `spritegen.c` in tools */\n\n"); 23 | fprintf(file, "#define NB_SPRITESHEET_WIDTH %i\n", w); 24 | fprintf(file, "#define NB_SPRITESHEET_HEIGHT %i\n", h); 25 | fprintf(file, "const unsigned char nb_spritesheet[] = {\n"); 26 | 27 | for (y = 0; y < h; y ++) 28 | { 29 | fprintf(file, " "); 30 | for (x = 0; x < w; x ++) 31 | { 32 | c = (x + y * w) * 4; 33 | 34 | if (image[c + 3] == 0) 35 | fprintf(file, "4, "); 36 | else 37 | fprintf(file, "%i, ", (int)((image[c] / 256.0f) * 4)); 38 | } 39 | fprintf(file, "\n"); 40 | } 41 | fprintf(file, "};\n"); 42 | fprintf(file, "#endif\n"); 43 | fclose(file); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /tools/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NoelFB/c89_nolib/398f1ae6b9a3ad267b05ed44c03a780bc507d5f6/tools/spritesheet.png --------------------------------------------------------------------------------