#include "editor.h" #include #include #include #include #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 = 20; GuiPanel((Rectangle){x, y + 24.0f, width, height}, NULL); if (IsFileDropped() && editor->state.createBlockState == NULL) { EditorCreateBlockState *state = malloc(sizeof(EditorCreateBlockState)); FilePathList droppedFiles = LoadDroppedFiles(); if (!IsFileExtension(droppedFiles.paths[0], ".png")) { free(state); UnloadDroppedFiles(droppedFiles); return; } state->upFilePath = droppedFiles.paths[0]; UnloadDroppedFiles(droppedFiles); // loading textures Image image = LoadImage(state->upFilePath); if (image.width != TEXTURE_WIDTH || image.height != TEXTURE_HEIGHT) { ImageResize(&image, TEXTURE_WIDTH, TEXTURE_HEIGHT); } state->upTexture = LoadTextureFromImage(image); editor->state.createBlockState = state; UnloadImage(image); } // 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(); } 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 loadWallTextures(EditorCreateBlockState *state, int wx, int wy, int ww, int wh, int pad) { if (state->sideFilePath == NULL) { int box = GuiMessageBox((Rectangle){wx - 20, wy + 100, ww + 40, 200}, "Waiting for side texture", "Drop side texture here", "No"); if (box >= 1) { state->isWall = false; state->isFloor = true; } if (IsFileDropped()) { FilePathList droppedFiles = LoadDroppedFiles(); if (!IsFileExtension(droppedFiles.paths[0], ".png")) { UnloadDroppedFiles(droppedFiles); return; } state->sideFilePath = droppedFiles.paths[0]; UnloadDroppedFiles(droppedFiles); Image image = LoadImage(state->sideFilePath); if (image.width != TEXTURE_WIDTH || image.height != TEXTURE_HEIGHT) { ImageResize(&image, TEXTURE_WIDTH, TEXTURE_HEIGHT); } state->sideTexture = LoadTextureFromImage(image); } return; } if (state->cornerFilePath == NULL) { int box = GuiMessageBox((Rectangle){wx - 20, wy + 100, ww + 40, 200}, "Waiting for corner texture", "Drop corner texture here", "No"); if (box >= 1) { UnloadTexture(state->sideTexture); free(state->sideFilePath); state->sideFilePath = NULL; state->isWall = false; state->isFloor = true; } if (IsFileDropped()) { FilePathList droppedFiles = LoadDroppedFiles(); if (!IsFileExtension(droppedFiles.paths[0], ".png")) { UnloadDroppedFiles(droppedFiles); return; } state->cornerFilePath = droppedFiles.paths[0]; UnloadDroppedFiles(droppedFiles); Image image = LoadImage(state->cornerFilePath); if (image.width != TEXTURE_WIDTH || image.height != TEXTURE_HEIGHT) { ImageResize(&image, TEXTURE_WIDTH, TEXTURE_HEIGHT); } state->cornerTexture = LoadTextureFromImage(image); } return; } } 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 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 --- // Up texture drawTextures(state, wx, wy, ww, wh, pad); // Side texture if (state->isWall) { loadWallTextures(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); 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); } }