#include "editor.h" #include #include #include #include #include "logger.h" #include "nfd.h" #include "raylib.h" #define RAYGUI_IMPLEMENTATION #include "raygui.h" #include "xd.h" const char *EDITOR_MAIN_TABS[] = {"Build"}; const int EDITOR_MAIN_TABS_SIZE = sizeof(EDITOR_MAIN_TABS) / sizeof(EDITOR_MAIN_TABS[0]); void removeNullTiles(Editor *editor) { int i, j = 0; EditorTile **tiles = editor->state.cache.tiles; int *count = &editor->state.cache.tilesCount; for (i = 0; i < *count; i++) { if (tiles[i] != NULL) { tiles[j++] = tiles[i]; } } *count = j; for (i = j; i < *count; i++) { tiles[i] = NULL; } } void SE_DrawEditor(Editor *editor, Camera2D *camera) { Vector2 mousePos = GetScreenToWorld2D(GetMousePosition(), *camera); float zoom = camera->zoom; XdLevel *level = editor->data->levels[editor->state.currentLevel]; XdFloor *floor = level->floors[editor->state.currentLayer]; // rendering placed tiles for (int i = 0; i < editor->state.cache.tilesCount; i++) { EditorTile *t = editor->state.cache.tiles[i]; if (t == NULL) continue; Rectangle s = {0, 0, 16, 16}; Rectangle d = {t->position.x * zoom, t->position.y * zoom, zoom, zoom}; DrawTexturePro(t->tile->texture, s, d, (Vector2){0, 0}, 0.0f, WHITE); } for (int x = 0; x < floor->width; x++) { for (int y = 0; y < floor->height; y++) { float rx = x * zoom, ry = y * zoom; DrawRectangleLines(rx, ry, zoom, zoom, BLACK); if ((rx < mousePos.x && mousePos.x < rx + zoom) && (ry < mousePos.y && mousePos.y < ry + zoom) && GetMousePosition().x < EDITOR_TOOLKIT_X) { // rendering selected tile if (editor->state.selectedTile != NULL) { EditorTileData *t = editor->state.selectedTile; Rectangle s = {0, 0, 16, 16}; Rectangle d = {rx, ry, zoom, zoom}; DrawTexturePro(t->texture, s, d, (Vector2){0, 0}, 0.0f, WHITE); // placing tiles if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { EditorTile *t = NULL; // checking for existing tiles on the position for (int i = 0; i < editor->state.cache.tilesCount; i++) { EditorTile *tt = editor->state.cache.tiles[i]; if (tt == NULL) continue; if (tt->position.x == x && tt->position.y == y) { t = tt; break; } } if (t == NULL) { t = malloc(sizeof(EditorTile)); t->position = (Vector2){x, y}; editor->state.cache.tiles[editor->state.cache.tilesCount] = t; editor->state.cache.tilesCount++; } t->tile = editor->state.selectedTile; } // removing tiles if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) { for (int i = 0; i < editor->state.cache.tilesCount; i++) { EditorTile *tt = editor->state.cache.tiles[i]; if (tt == NULL) continue; if (tt->position.x == x && tt->position.y == y) { free(tt); editor->state.cache.tiles[i] = NULL; break; } } } } } } } removeNullTiles(editor); } void drawBuildTab(Editor *editor, int x, int y, int width, int height, int padding) { GuiPanel((Rectangle){x, y + 24.0f, width, height}, NULL); // Rendering Rectangle controlBounds = {x + padding, y + padding + 20.0f, 200, 200}; Rectangle contentSize = {x + padding, y + padding, 10 * TEXTURE_WIDTH, 1000}; GuiScrollPanel(controlBounds, NULL, contentSize, &editor->state.panelScroll, &editor->state.panelView); BeginScissorMode(editor->state.panelView.x, editor->state.panelView.y, editor->state.panelView.width, editor->state.panelView.height); Vector2 mousePos = GetMousePosition(); // rendering floor tiles in grid int row = 0, column = 0, texturesPerRow = 10; for (int i = 0; i < editor->state.cache.tileDataSize; i++) { EditorTileData *tile = editor->state.cache.tileData[i]; if (tile == NULL) continue; row = i / texturesPerRow; column = i % texturesPerRow; int x = controlBounds.x + 5 + TEXTURE_WIDTH * column; int y = controlBounds.y + editor->state.panelScroll.y + TEXTURE_HEIGHT * row; DrawTexture(tile->texture, x, y, WHITE); bool isHovered = (x <= mousePos.x && mousePos.x <= x + TEXTURE_WIDTH) && (y <= mousePos.y && mousePos.y <= y + TEXTURE_HEIGHT); bool sameTile = editor->state.selectedTile != NULL && editor->state.selectedTile->data.id == tile->data.id; // hover event if (isHovered || sameTile) { DrawRectangleLines(x, y, TEXTURE_WIDTH, TEXTURE_HEIGHT, YELLOW); } // select tile if (isHovered && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { editor->state.selectedTile = tile; } } EndScissorMode(); // create tile button if (GuiButton( (Rectangle){x + padding, height - padding - 12.0f, 24.0f, 24.0f}, "#08#") && editor->state.createBlockState == NULL) { SE_LOG_INFO("Creating a new block"); EditorCreateBlockState *state = malloc(sizeof(EditorCreateBlockState)); editor->state.createBlockState = state; } } void drawTextures(EditorCreateBlockState *state, int wx, int wy, int ww, int wh, int pad) { if (state->isFloor) { GuiLabel((Rectangle){wx + pad, wy + pad + 24.0f * 2.0f, ww, 24.0f}, "UP"); DrawTexture(state->upTexture, wx + pad, wy + pad + 24.0f * 3.0f, WHITE); } else { GuiLabel((Rectangle){wx + pad, wy + pad + 24.0f * 2.0f, ww, 24.0f}, "UP (2D)"); DrawTexture(state->upTexture, wx + pad, wy + pad + 24.0f * 3.0f, WHITE); GuiLabel((Rectangle){wx + pad + 24.0f * 3.0f, wy + pad + 24.0f * 2.0f, ww, 24.0f}, "CORNER (2D)"); DrawTexture(state->cornerTexture, wx + pad + 24.0f * 3.0f, wy + pad + 24.0f * 3.0f, WHITE); GuiLabel((Rectangle){wx + pad + 24.0f * 7.0f, wy + pad + 24.0f * 2.0f, ww, 24.0f}, "SIDE (3D)"); DrawTexture(state->sideTexture, wx + pad + 24.0f * 7.0f, wy + pad + 24.0f * 3.0f, WHITE); } } void drawPreview(EditorCreateBlockState *state, int wx, int wy, int ww, int wh, int pad) { GuiLabel((Rectangle){wx + pad, wy + pad + 24.0f * 4.0f, ww, 24.0f}, "2D PREVIEW"); for (int x = 0; x < 10; x++) { for (int y = 0; y < 5; y++) { if (state->isFloor) { DrawTexture(state->upTexture, wx + pad + x * TEXTURE_WIDTH, wy + pad + 24.0f * 5.0f + y * TEXTURE_HEIGHT, WHITE); } else { Vector2 position = {x * TEXTURE_WIDTH, y * TEXTURE_HEIGHT}; position.x += wx + pad; position.y += wy + pad + 24.0f * 5.0f; // --- CORNERS --- // top left if (x == 0 && y == 0) { DrawTextureEx(state->cornerTexture, position, 0.0f, 1.0f, WHITE); } // top right else if (x == 9 && y == 0) { DrawTextureEx(state->cornerTexture, position, 90.0f, 1.0f, WHITE); } // bottom left else if (x == 0 && y == 4) { DrawTextureEx(state->cornerTexture, position, 270.0f, 1.0f, WHITE); } // bottom right else if (x == 9 && y == 4) { DrawTextureEx(state->cornerTexture, position, 180.0f, 1.0f, WHITE); } // --- SIDES --- // top else if (y == 0) { DrawTextureEx(state->upTexture, position, 0.0f, 1.0f, WHITE); } // left else if (x == 0 && y > 1) { DrawTextureEx(state->upTexture, position, 270.0f, 1.0f, WHITE); } // right else if (x == 9) { DrawTextureEx(state->upTexture, position, 90.0f, 1.0f, WHITE); } // bottom else if (y == 4 && x > 1) { DrawTextureEx(state->upTexture, position, 180.0f, 1.0f, WHITE); } } } } } void loadTexture(EditorCreateBlockState *state) { nfdchar_t *outPath = NULL; nfdresult_t result = NFD_OpenDialog(NULL, NULL, &outPath); if (result == NFD_OKAY) { if (!IsFileExtension(outPath, ".png")) { SE_LOG_WARN("The editor only supports .png files"); return; } // unloading old texture if (state->upFilePath != NULL) { UnloadTexture(state->upTexture); free(state->upFilePath); } state->upFilePath = outPath; Image image = LoadImage(state->upFilePath); if (image.width != TEXTURE_WIDTH || image.height != TEXTURE_HEIGHT) { ImageResizeNN(&image, TEXTURE_WIDTH, TEXTURE_HEIGHT); } state->upTexture = LoadTextureFromImage(image); UnloadImage(image); } } void drawCreatingNewBlock(Editor *editor) { EditorCreateBlockState *state = editor->state.createBlockState; int ww = 300; int wh = 280; int wx = GetScreenWidth() / 2.0f - ww / 2.0f; int wy = GetScreenHeight() / 2.0f - wh / 2.0f; int pad = 20; int close = GuiWindowBox((Rectangle){wx, wy, ww, wh}, "Creating a new block"); // --- Block type --- GuiLabel((Rectangle){wx + pad, wy + pad, ww - pad * 2, 24.0f}, "Select type"); GuiCheckBox((Rectangle){wx + pad, wy + pad + 24.0f, 24.0f, 24.0f}, "Floor", &state->isFloor); if (state->isFloor) state->isWall = false; GuiCheckBox( (Rectangle){wx + pad * 4.0f + 24.0f, wy + pad + 24.0f, 24.0f, 24.0f}, "Wall", &state->isWall); if (state->isWall) state->isFloor = false; if (!state->isWall && !state->isFloor) state->isFloor = true; // --- Texture loading --- // Loading UP texture if (state->upFilePath == NULL) { if (GuiButton((Rectangle){wx + pad, wy + pad + 24.0f * 3.0f, 64.0f, 24.0f}, "Load")) { loadTexture(state); } } // Changing the texture else { if (GuiButton((Rectangle){wx + pad * 1.5f + TEXTURE_WIDTH, wy + pad + 24.0f * 3.0f - TEXTURE_WIDTH / 2.0f, 24.0f, 24.0f}, "#211#")) { loadTexture(state); } } // --- Texture preview --- // Up texture drawTextures(state, wx, wy, ww, wh, pad); drawPreview(state, wx, wy, ww, wh, pad); int createButton = GuiButton( (Rectangle){wx + pad, wy + wh - pad * 2.0f, ww - pad * 2.0f, 32.0f}, "Create"); if (createButton) { close = true; if (state->isFloor) { int id = editor->state.cache.tileDataSize; EditorTileData *tile = malloc(sizeof(EditorTileData)); editor->state.cache.tileData[id] = tile; tile->data = (XdTileData){}; tile->data.id = id; tile->data.type = TILE_FLOOR; tile->texture = state->upTexture; editor->state.cache.tileDataSize++; } } if (close) { // free(state); editor->state.createBlockState = NULL; } } void SE_DrawEditorToolkit(Editor *editor) { switch (editor->state.activeMainTab) { case 0: { drawBuildTab(editor, EDITOR_TOOLKIT_X, EDITOR_TOOLKIT_Y, EDITOR_TOOLKIT_WIDTH, EDITOR_TOOLKIT_HEIGHT, 20); break; } default: break; } GuiTabBar((Rectangle){EDITOR_TOOLKIT_X, EDITOR_TOOLKIT_Y, 100.0, 24.0f}, EDITOR_MAIN_TABS, EDITOR_MAIN_TABS_SIZE, &editor->state.activeMainTab); // Creating new block if (editor->state.createBlockState != NULL) { drawCreatingNewBlock(editor); } }