/* * Copyright (c) Camden Dixie O'Brien * SPDX-License-Identifier: AGPL-3.0-only */ #include #include #include #include #include #include #define SCALE 4 #define TILESIZE 32 #define VIEWWIDTH 8 #define VIEWHEIGHT 6 #define MAX_PATH_LEN 128 #define MAP_ASSET "/map.tmx" #define MAPWIDTH 112 #define MAPHEIGHT 112 #define MAPSHIFTX 48 #define MAPSHIFTY 48 #define TSASSET "/tileset.png" #define TSCOLS 56 #define PIASSET "/player/idle/down.png" #define PIWIDTH 48 #define PIHEIGHT 64 #define PIANIMLEN 8 #define BASEANIMPERIOD 200 static SDL_Window *window; static SDL_Renderer *renderer; static unsigned map[MAPWIDTH][MAPHEIGHT]; static SDL_Texture *tstex, *pitex; int main(int argc, char *argv[]) { char path[MAX_PATH_LEN]; if (2 != argc) { fprintf(stderr, "Usage: %s ASSETS-DIR\n", argv[0]); return 1; } // Set up SDL window int err = SDL_Init(SDL_INIT_VIDEO); assert(0 == err); window = SDL_CreateWindow( "2D Game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCALE * TILESIZE * VIEWWIDTH, SCALE * TILESIZE * VIEWHEIGHT, 0); assert(NULL != window); renderer = SDL_CreateRenderer(window, -1, 0); assert(NULL != renderer); // Find chunk nodes in map XML xmlDocPtr doc; xmlNodePtr node; assert(strlen(argv[1]) + strlen(MAP_ASSET) < MAX_PATH_LEN); strcpy(path, argv[1]); strcat(path, MAP_ASSET); doc = xmlParseFile(path); assert(NULL != doc); node = xmlDocGetRootElement(doc); assert(0 == xmlStrcmp(node->name, (const xmlChar *)"map")); node = node->xmlChildrenNode; while (NULL != node && xmlStrcmp(node->name, (const xmlChar *)"layer") != 0) node = node->next; assert(NULL != node); node = node->xmlChildrenNode; while (NULL != node && xmlStrcmp(node->name, (const xmlChar *)"data") != 0) node = node->next; assert(NULL != node); // Iterate through chunks and populate map array. xmlNodePtr chunk_contents; for (node = node->xmlChildrenNode; NULL != node; node = node->next) { if (0 != xmlStrcmp(node->name, (const xmlChar *)"chunk")) continue; xmlChar *x_attr, *y_attr; x_attr = xmlGetProp(node, (const xmlChar *)"x"); y_attr = xmlGetProp(node, (const xmlChar *)"y"); const int chunk_x = atoi((const char *)x_attr); const int chunk_y = atoi((const char *)y_attr); chunk_contents = node->xmlChildrenNode; assert( 0 == xmlStrcmp(chunk_contents->name, (const xmlChar *)"text")); int x = chunk_x + MAPSHIFTX, y = chunk_y + MAPSHIFTY; xmlChar buf[10]; const xmlChar *p, *q; q = chunk_contents->content; while (isspace((const char)*q)) ++q; p = q; while ('\0' != *q) { switch (*q) { case (xmlChar)',': memset(buf, 0, sizeof(buf)); memcpy(buf, p, sizeof(xmlChar) * (q - p)); assert(x < MAPWIDTH && y < MAPHEIGHT); map[x][y] = (unsigned)atoi((const char *)buf); ++x; p = ++q; break; case (xmlChar)'\n': if (x < MAPWIDTH) { memset(buf, 0, sizeof(buf)); memcpy(buf, p, sizeof(xmlChar) * (q - p)); assert(y < MAPHEIGHT); map[x][y] = (unsigned)atoi((const char *)buf); } x = chunk_x + MAPSHIFTX; ++y; p = ++q; break; default: ++q; break; } } } // Load tileset assert(strlen(argv[1]) + strlen(TSASSET) < MAX_PATH_LEN); strcpy(path, argv[1]); strcat(path, TSASSET); tstex = IMG_LoadTexture(renderer, path); assert(NULL != tstex); // Load player idle spritesheet assert(strlen(argv[1]) + strlen(PIASSET) < MAX_PATH_LEN); strcpy(path, argv[1]); strcat(path, PIASSET); pitex = IMG_LoadTexture(renderer, path); assert(NULL != pitex); int offx = 16, offy = 0; const SDL_Rect pdest = { .x = SCALE * 80, .y = SCALE * 80, .w = SCALE * PIWIDTH, .h = SCALE * PIHEIGHT, }; SDL_Event event; while (1) { // Handle events SDL_PollEvent(&event); switch (event.type) { case SDL_QUIT: goto quit; default: break; } SDL_RenderClear(renderer); // Draw map for (int y = 0; y < VIEWHEIGHT; ++y) { for (int x = 0; x < VIEWWIDTH; ++x) { const unsigned tileid = map[x + offx + MAPSHIFTX][y + offy + MAPSHIFTY]; if (0 == tileid) continue; const SDL_Rect src = { .x = TILESIZE * ((tileid - 1) % TSCOLS), .y = TILESIZE * ((tileid - 1) / TSCOLS), .w = TILESIZE, .h = TILESIZE, }; const SDL_Rect dest = { .x = SCALE * TILESIZE * x, .y = SCALE * TILESIZE * y, .w = SCALE * TILESIZE, .h = SCALE * TILESIZE, }; SDL_RenderCopy(renderer, tstex, &src, &dest); } } // Draw player const unsigned piframe = (SDL_GetTicks64() / BASEANIMPERIOD) % PIANIMLEN; const SDL_Rect psrc = { .x = PIWIDTH * piframe, .y = 0, .w = PIWIDTH, .h = PIHEIGHT, }; SDL_RenderCopy(renderer, pitex, &psrc, &pdest); SDL_RenderPresent(renderer); } quit: SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }