#include #include "menus.h" #include "data.h" #include "blocks.h" #include "options.h" #include "gameloop.h" static int menu_optionsMain(SDL_Renderer *, Inputs *); /* === GAME STATES === */ /* state_title * Presents a title screen with basic options. Is capable of changing the game * state. */ int state_title(SDL_Renderer *renderer, Inputs *inputs, int *gameState) { inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); white(renderer); drawBig( renderer, "M4KC", BUFFER_HALF_W, 16); #ifdef small shadowStr(renderer, "version 0.8.0 kolibri", 1, BUFFER_H - 9); #else shadowStr(renderer, "version 0.8.0 kolibri (dev build)", 1, BUFFER_H - 9); #endif if (button(renderer, "Singleplayer", BUFFER_HALF_W - 64, 42, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { if (data_refreshWorldList()) { gameLoop_error("Cannot refresh world list"); } else { *gameState = STATE_SELECT_WORLD; } } if (button(renderer, "Options", BUFFER_HALF_W - 64, 64, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gameState = STATE_OPTIONS; } if (button(renderer, "Quit Game", BUFFER_HALF_W - 64, 86, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { return 1; } return 0; } void state_selectWorld( SDL_Renderer *renderer, Inputs *inputs, int *gameState, World *world) { static int scroll = 0; int needRefresh = 0; if (inputs->mouse.wheel != 0) { scroll -= inputs->mouse.wheel; inputs->mouse.wheel = 0; } if (scroll < 0) { scroll = 0; } if (scroll > data_worldListLength - 1) { scroll = data_worldListLength - 1; } SDL_Rect listBackground; listBackground.x = 0; listBackground.y = 0; listBackground.w = BUFFER_W; listBackground.h = BUFFER_H - 28; inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); tblack(renderer); SDL_RenderFillRect(renderer, &listBackground); SDL_RenderDrawLine(renderer, 0, BUFFER_H - 29, BUFFER_W, BUFFER_H - 29); int index = 0; int y = 6; int yLimit = BUFFER_H - 44; data_WorldListItem *item = data_worldList; while (item != NULL) { if (y > yLimit) { break; } if (index < scroll) { goto nextItem; } int hover = drawWorldListItem(renderer, item, BUFFER_HALF_W - 64, y, inputs->mouse.x, inputs->mouse.y); y += 21; if (!inputs->mouse.left) { goto nextItem; } switch (hover) { case 1: if (World_load(world, item->name)) { gameLoop_error("Could not load world"); } else { *gameState = STATE_LOADING; } return; case 2:; char deletePath[PATH_MAX]; if (data_getWorldPath(deletePath, item->name)) { gameLoop_error("Could not delete world"); return; } data_removeDirectory(deletePath); needRefresh = 1; // TODO Fix call and recursion here // data_refreshWorldList(); // state_selectWorld(renderer, inputs, gameState, world); break; } nextItem: index++; item = item->next; } if (6 + index * 22 > yLimit) { scrollbar( renderer, BUFFER_HALF_W + 70, 0, BUFFER_H - 29, inputs->mouse.x, inputs->mouse.y, inputs->mouse.left, &scroll, data_worldListLength); } if (index == 0) { shadowCenterStr(renderer, "No worlds", BUFFER_HALF_W, BUFFER_HALF_H - 15); } if (button(renderer, "Cancel", BUFFER_HALF_W - 64, BUFFER_H - 22, 61, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gameState = STATE_TITLE; scroll = 0; } if (button(renderer, "New", BUFFER_HALF_W + 3, BUFFER_H - 22, 61, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gameState = STATE_NEW_WORLD; scroll = 0; } if (needRefresh) { data_refreshWorldList(); } } const char *terrainNames[16] = { "Classic Terrain", "Natural Terrain", "Flat Stone", "Flat Grass", "Water World"}; const char *dayNightModes[16] = { "Day and Night", "Always Day", "Always Night", }; /* state_newWorld * Shows a menu with editable parameters for creating a new world. Capable of * editing world prarameters and changing the game state. */ void state_newWorld( SDL_Renderer *renderer, Inputs *inputs, int *gameState, World *world) { static int badName = 0; static int whichInput = 0; static int typeSelect = 1; static int dayNightSelect = 0; static char seedBuffer[16]; static InputBuffer seedInput = { .buffer = seedBuffer, .len = 16, .cursor = 0}; static char nameBuffer[16]; static InputBuffer nameInput = { .buffer = nameBuffer, .len = 16, .cursor = 0}; inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); if (whichInput == 0) { manageInputBuffer(&nameInput, inputs); } if (input(renderer, "Name", nameInput.buffer, BUFFER_HALF_W - 64, 8, 128, inputs->mouse.x, inputs->mouse.y, whichInput == 0) && inputs->mouse.left) { whichInput = 0; } if (badName) { SDL_SetRenderDrawColor(renderer, 255, 128, 128, 255); drawChar(renderer, '!', BUFFER_HALF_W + 70, 12); } if (whichInput == 1) { manageInputBuffer(&seedInput, inputs); } if (input(renderer, "Seed", seedInput.buffer, BUFFER_HALF_W - 64, 30, 128, inputs->mouse.x, inputs->mouse.y, whichInput == 1) && inputs->mouse.left) { whichInput = 1; } if (button(renderer, terrainNames[typeSelect], BUFFER_HALF_W - 64, 52, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { typeSelect = (typeSelect + 1) % 5; } if (button(renderer, dayNightModes[dayNightSelect], BUFFER_HALF_W - 64, 74, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { dayNightSelect = (dayNightSelect + 1) % 3; } if (button(renderer, "Cancel", BUFFER_HALF_W - 64, 96, 61, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gameState = STATE_SELECT_WORLD; } if (button(renderer, "Generate", BUFFER_HALF_W + 3, 96, 61, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { // Reject empty names and names with slashes if (nameInput.buffer[0] == 0) { goto cantMakeWorld; } for (int index = 0; nameInput.buffer[index]; index++) { if (nameInput.buffer[index] == '/') { goto cantMakeWorld; } } if (data_getWorldPath(world->path, nameInput.buffer)) { goto cantMakeWorld; } if (data_directoryExists(world->path)) { goto cantMakeWorld; } world->time = 2048; world->type = typeSelect; world->dayNightMode = dayNightSelect; // Get numeric seed world->seed = 0; for (int index = 0; seedInput.buffer[index]; index++) { world->seed *= 10; world->seed += seedInput.buffer[index] - '0'; } // "Randomize" seed if it was not set if (world->seed == 0) { world->seed = time(0) % 999999999999999; } // Secret world for testing nonsense. Type "dev" if (world->seed == 5800) { world->type = -1; } whichInput = 0; seedInput.buffer[0] = 0; seedInput.cursor = 0; nameInput.buffer[0] = 0; nameInput.cursor = 0; badName = 0; *gameState = STATE_LOADING; } return; cantMakeWorld: nameInput.buffer[0] = 0; nameInput.cursor = 0; badName = 1; } /* state_loading * Shows a loading screen and progressively loads in chunks. Returns 1 when * finished. */ int state_loading( SDL_Renderer *renderer, World *world, unsigned int seed, Coords center) { IntCoords chunkLoadCoords; static int chunkLoadNum = 0; if (chunkLoadNum < CHUNKARR_SIZE) { chunkLoadCoords.x = ((chunkLoadNum % CHUNKARR_DIAM) - CHUNKARR_RAD) * 64; chunkLoadCoords.y = (((chunkLoadNum / CHUNKARR_DIAM) % CHUNKARR_DIAM) - CHUNKARR_RAD) * 64; chunkLoadCoords.z = ((chunkLoadNum / (CHUNKARR_DIAM * CHUNKARR_DIAM)) - CHUNKARR_RAD) * 64; genChunk( world, seed, chunkLoadCoords.x, chunkLoadCoords.y, chunkLoadCoords.z, world->type, 1, center); loadScreen( renderer, "Generating world...", chunkLoadNum, CHUNKARR_SIZE); chunkLoadNum++; return 0; } else { chunkLoadNum = 0; return 1; } } /* state_options * Shows an options screen. Capable of changing settings and the game state. */ void state_options(SDL_Renderer *renderer, Inputs *inputs, int *gameState) { inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); if (menu_optionsMain(renderer, inputs)) { *gameState = 0; } } /* state_egg * This lacks description. Capable of changing the game state. */ void state_egg(SDL_Renderer *renderer, Inputs *inputs, int *gameState) { inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); white(renderer); centerStr( renderer, "Go away, this is my house.", BUFFER_HALF_W, BUFFER_HALF_H - 16); if (button(renderer, "Ok", BUFFER_HALF_W - 64, BUFFER_HALF_H, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gameState = STATE_TITLE; } } /* state_err * Shows an error message on screen. Returns 1 when the "Ok" button is pressed. */ int state_err(SDL_Renderer *renderer, Inputs *inputs, char *message) { inputs->mouse.x /= BUFFER_SCALE; inputs->mouse.y /= BUFFER_SCALE; dirtBg(renderer); SDL_SetRenderDrawColor(renderer, 255, 128, 128, 255); centerStr( renderer, "Error:", BUFFER_HALF_W, BUFFER_HALF_H - 20); white(renderer); centerStr( renderer, message, BUFFER_HALF_W, BUFFER_HALF_H - 4); if (button(renderer, "Ok", BUFFER_HALF_W - 64, BUFFER_HALF_H + 16, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { return 1; } return 0; } /* === INGAME POPUPS === */ /* popup_hud * Draws the heads up display, including the hotbar, offhand, crosshair, health, * hunger, chat history, and the debug menu if activated. */ void popup_hud( SDL_Renderer *renderer, Inputs *inputs, World *world, int *debugOn, uint32_t *fps_now, Player *player) { int i; static SDL_Rect hotbarRect; hotbarRect.x = BUFFER_HALF_W - 77; hotbarRect.y = BUFFER_H - 18; hotbarRect.w = 154; hotbarRect.h = 18; static SDL_Rect hotbarSelectRect; hotbarSelectRect.x = 0; hotbarSelectRect.y = hotbarRect.y; hotbarSelectRect.w = 18; hotbarSelectRect.h = 18; static SDL_Rect offhandRect; offhandRect.x = 0; offhandRect.y = BUFFER_H - 18; offhandRect.w = 18; offhandRect.h = 18; // Debug screen if (*debugOn) { static char debugText[][32] = { "M4KC 0.7", "Seed: ", "X: ", "Y: ", "Z: ", "FPS: ", "ChunkX: ", "ChunkY: ", "ChunkZ: "}; // Seed strnum(debugText[1], 6, world->seed); // Coordinates strnum(debugText[2], 3, (int)player->pos.x); strnum(debugText[3], 3, (int)player->pos.y); strnum(debugText[4], 3, (int)player->pos.z); // FPS strnum(debugText[5], 5, *fps_now); // Chunk coordinates strnum(debugText[6], 8, ((int)player->pos.x) >> 6); strnum(debugText[7], 8, ((int)player->pos.y) >> 6); strnum(debugText[8], 8, ((int)player->pos.z) >> 6); // Text for (i = 0; i < 9; i++) { drawBGStr(renderer, debugText[i], 0, i * 9); } // Chunk monitor #ifndef small #define CHUNKMONW 10 #define CHUNKMONCOL 9 SDL_Rect chunkMonitorRect = { .x = 0, .y = 1 - CHUNKMONW, .w = CHUNKMONW, .h = CHUNKMONW}; for (i = 0; i < CHUNKARR_SIZE; i++) { if (i % CHUNKMONCOL == 0) { chunkMonitorRect.x = BUFFER_W - ((CHUNKMONW * (CHUNKMONCOL - 1)) + 2); chunkMonitorRect.y += CHUNKMONW - 1; } else { chunkMonitorRect.x += CHUNKMONW - 1; } int stamp = world->chunk[i].loaded; SDL_SetRenderDrawColor( renderer, (stamp & 0x03) * 64, (stamp & 0x0C) * 16, (stamp & 0x30) * 4, 0xFF); SDL_RenderFillRect(renderer, &chunkMonitorRect); white(renderer); SDL_RenderDrawRect(renderer, &chunkMonitorRect); } #undef CHUNKMONW #undef CHUNKMONCOL #endif } // Hotbar tblack(renderer); SDL_RenderFillRect(renderer, &hotbarRect); hotbarSelectRect.x = BUFFER_HALF_W - 77 + player->inventory.hotbarSelect * 17; white(renderer); SDL_RenderDrawRect(renderer, &hotbarSelectRect); for (i = 0; i < 9; i++) { drawSlot( renderer, &player->inventory.hotbar[i], BUFFER_HALF_W - 76 + i * 17, BUFFER_H - 17, inputs->mouse.x, inputs->mouse.y); } // Offhand if (player->inventory.offhand.blockid != 0) { tblack(renderer); SDL_RenderDrawRect(renderer, &offhandRect); drawSlot( renderer, &player->inventory.offhand, 1, BUFFER_H - 17, inputs->mouse.x, inputs->mouse.y); } // Chat int chatDrawIndex = chatHistoryIndex; for (i = 0; i < 11; i++) { chatDrawIndex = nmod(chatDrawIndex - 1, 11); if (chatHistoryFade[chatDrawIndex] > 0) { chatHistoryFade[chatDrawIndex]--; drawBGStr( renderer, chatHistory[chatDrawIndex], 0, BUFFER_H - 32 - i * 9); } } } /* manageInvSlot * Draws and performs the input logic of a single inventory slot. Capable of * changing which slot is currently being dragged. */ void manageInvSlot( SDL_Renderer *renderer, Inputs *inputs, int x, int y, InvSlot *current, InvSlot *selected, int *dragging) { if (drawSlot( renderer, current, x, y, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { inputs->mouse.left = 0; if (*dragging) { // Place down item if (current->blockid == 0) { *current = *selected; *selected = (const InvSlot){0}; *dragging = 0; } else if (current->blockid == selected->blockid) { InvSlot_transfer(current, selected); } else { InvSlot_swap(current, selected); } } else if (current->blockid != 0) { // Pick up item *selected = *current; *current = (const InvSlot){0}; *dragging = 1; } } } /* popup_inventory * Allows the user to manage their inventory, rearranging the items inside of * it. Capable of closing itself. */ void popup_inventory( SDL_Renderer *renderer, Inputs *inputs, Player *player, int *gamePopup) { SDL_Rect inventoryRect; inventoryRect.x = BUFFER_HALF_W - 77; inventoryRect.y = (BUFFER_H - 18) / 2 - 26; inventoryRect.w = 154; inventoryRect.h = 52; SDL_Rect hotbarRect; hotbarRect.x = BUFFER_HALF_W - 77; hotbarRect.y = BUFFER_H - 18; hotbarRect.w = 154; hotbarRect.h = 18; SDL_Rect offhandRect; offhandRect.x = 0; offhandRect.y = BUFFER_H - 18; offhandRect.w = 18; offhandRect.h = 18; static InvSlot selected = {0}; static int dragging = 0; // Inventory background tblack(renderer); SDL_RenderFillRect(renderer, &inventoryRect); SDL_RenderFillRect(renderer, &hotbarRect); SDL_RenderFillRect(renderer, &offhandRect); // Hotbar items for (int i = 0; i < HOTBAR_SIZE; i++) { manageInvSlot( renderer, inputs, BUFFER_HALF_W - 76 + i * 17, BUFFER_H - 17, &player->inventory.hotbar[i], &selected, &dragging); } // Inventory items for (int i = 0; i < INVENTORY_SIZE; i++) { manageInvSlot( renderer, inputs, BUFFER_HALF_W - 76 + (i % HOTBAR_SIZE) * 17, inventoryRect.y + 1 + (i / HOTBAR_SIZE) * 17, &player->inventory.slots[i], &selected, &dragging); } // Offhand manageInvSlot( renderer, inputs, 1, BUFFER_H - 17, &player->inventory.offhand, &selected, &dragging); if (dragging) { drawSlot( renderer, &selected, inputs->mouse.x - 8, inputs->mouse.y - 8, 0, 0); } // Exit inventory if (inputs->keyboard.e) { inputs->keyboard.e = 0; *gamePopup = POPUP_HUD; } } /* popup_chat * Allows the user to type in chat, and view farther back in the message * history. Capable of closing itself. */ void popup_chat(SDL_Renderer *renderer, Inputs *inputs, uint64_t gameTime) { static char buffer[64] = {0}; static InputBuffer chatBox = { .buffer = buffer, .len = 64, .cursor = 0}; static SDL_Rect chatBoxRect = {0, 0, 0, 9}; chatBoxRect.y = BUFFER_H - 9; chatBoxRect.w = BUFFER_W; int chatDrawIndex = chatHistoryIndex; for (int i = 0; i < 11; i++) { chatDrawIndex = nmod(chatDrawIndex - 1, 11); drawBGStr( renderer, chatHistory[chatDrawIndex], 0, BUFFER_H - 32 - i * 9); } // Get keyboard input if (manageInputBuffer(&chatBox, inputs)) { // 63: max chat box length // 7: max username length // 2: ": " chars // 1: null static char chatNameConcat[63 + 7 + 2 + 1]; snprintf(chatNameConcat, 63 + 7 + 2, "%s: %s", options.username.buffer, chatBox.buffer); // Add input to chat chatAdd(chatNameConcat); // Clear input box chatBox.cursor = 0; chatBox.buffer[0] = 0; } // Chat input box // If char limit is reached, give some visual // feedback. if (chatBox.cursor == 63) { SDL_SetRenderDrawColor(renderer, 128, 0, 0, 128); } else { tblack(renderer); } SDL_RenderFillRect(renderer, &chatBoxRect); white(renderer); drawChar( renderer, 95 + 32 * ((gameTime >> 6) % 2), drawStr( renderer, chatBox.buffer, 0, BUFFER_H - 8), BUFFER_H - 8); } /* popup_pause * Displays a pause menu. Capable of activating submenus or changing the game * state. */ void popup_pause( SDL_Renderer *renderer, Inputs *inputs, int *gamePopup, int *gameState, World *world) { if (button(renderer, "Back to Game", BUFFER_HALF_W - 64, 20, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_HUD; } if (button(renderer, "Options...", BUFFER_HALF_W - 64, 42, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_OPTIONS; } if (button(renderer, "Save and Quit", BUFFER_HALF_W - 64, 64, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { int err = World_save(world); if (err) { gameLoop_error("Could not save world"); return; } World_wipe(world); *gameState = STATE_TITLE; } } /* popup_options * Shows an options screen. Capable of changing settings and closing itself. */ void popup_options(SDL_Renderer *renderer, Inputs *inputs, int *gamePopup) { if (menu_optionsMain(renderer, inputs)) { *gamePopup = 1; } } #ifndef small /* popup_debugTools * Shows a menu listing advanced debug tools. These are only included in debug * builds and are not included in compressed executables. */ void popup_debugTools(SDL_Renderer *renderer, Inputs *inputs, int *gamePopup) { if (button(renderer, "Chunk Peek", BUFFER_HALF_W - 64, 20, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_CHUNK_PEEK; } if (button(renderer, "All Chunks", BUFFER_HALF_W - 64, 42, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_ROLL_CALL; } if (button(renderer, "World overview", BUFFER_HALF_W - 64, 64, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_OVERVIEW; } if (button(renderer, "Done", BUFFER_HALF_W - 64, 86, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_HUD; } } /* popup_chunkPeek * Shows a 3D map of the current chunk, with the ability to view a cross-section * of it. This feature is only included in debug builds. */ void popup_chunkPeek( SDL_Renderer *renderer, Inputs *inputs, World *world, int *gamePopup, Player *player) { static int chunkPeekRYMax = 0; int chunkPeekRX, chunkPeekRY, chunkPeekRZ, chunkPeekColor; Chunk *debugChunk; char chunkPeekText[][32] = { "coordHash: ", "loaded: "}; debugChunk = chunkLookup( world, (int)player->pos.x, (int)player->pos.y, (int)player->pos.z); white(renderer); if (debugChunk != NULL) { // There is a chunk to display info about. Process // strings. strnum(chunkPeekText[0], 11, debugChunk->coordHash); strnum(chunkPeekText[1], 8, debugChunk->loaded); // Draw the strings for (int i = 0; i < 2; i++) { drawStr(renderer, chunkPeekText[i], 0, i << 3); } // Scroll wheel for changing chunk map xray if (inputs->mouse.wheel != 0) { chunkPeekRYMax -= inputs->mouse.wheel; chunkPeekRYMax = nmod(chunkPeekRYMax, 64); inputs->mouse.wheel = 0; } // Mouse for changing chunk map xray if ( inputs->mouse.x > 128 && inputs->mouse.y < 64 && inputs->mouse.left) chunkPeekRYMax = inputs->mouse.y; // Up/Down buttons for changing chunk map xray if (button(renderer, "UP", 4, 56, 64, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { chunkPeekRYMax = nmod(chunkPeekRYMax - 1, 64); } if (button(renderer, "DOWN", 4, 78, 64, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { chunkPeekRYMax = nmod(chunkPeekRYMax + 1, 64); } // Draw chunk map white(renderer); SDL_RenderDrawLine( renderer, 128, chunkPeekRYMax, 191, chunkPeekRYMax); for ( chunkPeekRY = 64; chunkPeekRY >= chunkPeekRYMax; chunkPeekRY--) for ( chunkPeekRX = 0; chunkPeekRX < 64; chunkPeekRX++) for ( chunkPeekRZ = 0; chunkPeekRZ < 63; chunkPeekRZ++) { Block currentBlock = debugChunk->blocks[chunkPeekRX + (chunkPeekRY << 6) + (chunkPeekRZ << 12)]; chunkPeekColor = textures[currentBlock * 256 * 3 + 6 * 16]; if (chunkPeekColor) { int alpha = 255; if (currentBlock == BLOCK_WATER) { alpha = 64; } SDL_SetRenderDrawColor( renderer, (chunkPeekColor >> 16 & 0xFF), (chunkPeekColor >> 8 & 0xFF), (chunkPeekColor & 0xFF), alpha); SDL_RenderDrawPoint( renderer, chunkPeekRX + 128, chunkPeekRY + chunkPeekRZ); // A little shadow for depth SDL_SetRenderDrawColor( renderer, 0, 0, 0, 64); SDL_RenderDrawPoint( renderer, chunkPeekRX + 128, chunkPeekRY + chunkPeekRZ + 1); } } } else { drawStr(renderer, "Chunk not found", 0, 0); } if (button(renderer, "Done", 4, 100, 64, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_ADVANCED_DEBUG; } } void popup_rollCall( SDL_Renderer *renderer, Inputs *inputs, World *world, int *gamePopup) { static int scroll = 0; if (inputs->mouse.wheel != 0) { scroll += inputs->mouse.wheel; inputs->mouse.wheel = 0; } if (scroll > 0) { scroll = 0; } if (scroll < 1 - CHUNKARR_SIZE) { scroll = 1 - CHUNKARR_SIZE; } white(renderer); drawStr(renderer, "x y z stmp hash", 8, 10); for (int index = 0; index < CHUNKARR_SIZE; index++) { Chunk *chunk = &world->chunk[index]; char chunkDescription[32]; white(renderer); int topMargin = 28; int y = (index + scroll) * 8 + topMargin; if (y < topMargin || y >= BUFFER_H) { continue; } snprintf(chunkDescription, 32, "%i", chunk->center.x - 32); drawStr(renderer, chunkDescription, 0, y); snprintf(chunkDescription, 32, "%i", chunk->center.y - 32); drawStr(renderer, chunkDescription, 24, y); snprintf(chunkDescription, 32, "%i", chunk->center.z - 32); drawStr(renderer, chunkDescription, 48, y); snprintf(chunkDescription, 32, "#%i", chunk->loaded); drawStr(renderer, chunkDescription, 72, y); snprintf(chunkDescription, 32, "%016x", chunk->coordHash); drawStr(renderer, chunkDescription, 96, y); } if (button(renderer, "Done", BUFFER_W - 6 - 32, 6, 32, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_ADVANCED_DEBUG; } } void popup_overview( SDL_Renderer *renderer, Inputs *inputs, World *world, int *gamePopup) { (void)(world); int worldEndingBound = CHUNK_SIZE * (CHUNKARR_RAD + 1); int worldStartingBound = CHUNK_SIZE * CHUNKARR_RAD * -1; for (int y = worldEndingBound; y > worldStartingBound; y -= 4) for (int x = worldStartingBound; x < worldEndingBound; x += 4) for (int z = worldStartingBound; z < worldEndingBound; z += 4) { int projectX = (x - z) / 4; int projectY = ((x + z) / 2 + y) / 4; Block currentBlock = World_getBlock(world, x, y, z); int color; int alpha = 255; if (currentBlock < NUMBER_OF_BLOCKS) { color = textures[currentBlock * 256 * 3 + 6 * 16]; } else { color = 0xFF0000; alpha = 0; } if (color != 0) { if (currentBlock == BLOCK_WATER) { alpha = 64; } SDL_SetRenderDrawColor( renderer, (color >> 16 & 0xFF), (color >> 8 & 0xFF), (color & 0xFF), alpha); SDL_RenderDrawPoint( renderer, projectX + BUFFER_HALF_W, projectY + 32); } } if (button(renderer, "Done", BUFFER_W - 6 - 32, 6, 32, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { *gamePopup = POPUP_ADVANCED_DEBUG; } } #endif /* menu_optionsMain * This function presents the options menu. It's purpose is to be included in * other functions that draw a suitable background and then call this one. * Returns 1 when the user pressed the "Done" button. Capable of changing * settings. */ static int menu_optionsMain(SDL_Renderer *renderer, Inputs *inputs) { static int page = 0; switch (page) { case 0: manageInputBuffer(&options.username, inputs); input(renderer, "Username", options.username.buffer, BUFFER_HALF_W - 64, 20, 128, inputs->mouse.x, inputs->mouse.y, 1); static char *trapMouseTexts[] = { "Capture Mouse: OFF", "Capture Mouse: ON"}; if (button(renderer, trapMouseTexts[options.trapMouse], BUFFER_HALF_W - 64, 42, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { options.trapMouse = !options.trapMouse; } break; case 1: ;static char drawDistanceText[20] = {0}; if (!drawDistanceText[0]) { snprintf( drawDistanceText, 20, "Draw distance: %i", options.drawDistance); } if (button(renderer, drawDistanceText, BUFFER_HALF_W - 64, 20, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { switch (options.drawDistance) { case 20: options.drawDistance = 32; break; case 32: options.drawDistance = 64; break; case 64: options.drawDistance = 96; break; case 96: options.drawDistance = 128; break; default: options.drawDistance = 20; break; } strnum(drawDistanceText, 15, options.drawDistance); } static char *fovTexts[] = { "FOV: Low", "FOV: Medium", "FOV: High", "FOV: ?"}; char *fovText = NULL; switch ((int)options.fov) { default: fovText = fovTexts[3]; break; case 60: fovText = fovTexts[2]; break; case 90: fovText = fovTexts[1]; break; case 140: fovText = fovTexts[0]; break; } if (button(renderer, fovText, BUFFER_HALF_W - 64, 42, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { switch ((int)options.fov) { case 60: options.fov = 140; break; case 90: options.fov = 60; break; default: options.fov = 90; break; } } static char *fogTexts[] = { "Fog: Gradual", "Fog: Sharp"}; if (button(renderer, fogTexts[options.fogType], BUFFER_HALF_W - 64, 64, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { options.fogType = !options.fogType; } break; } if (button(renderer, "<", BUFFER_HALF_W - 86, 20, 16, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { page--; page = nmod(page, 2); } if (button(renderer, ">", BUFFER_HALF_W + 70, 20, 16, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { page++; page = nmod(page, 2); } if (button(renderer, "Done", BUFFER_HALF_W - 64, 86, 128, inputs->mouse.x, inputs->mouse.y) && inputs->mouse.left) { int err = options_save(); if (err) { gameLoop_error("Could not save options"); } page = 0; return 1; } return 0; }