Merged with ui

main
Jonathan Hager 3 years ago
commit 9ad56ed6bb
Signed by: JonathanHager
GPG Key ID: 34881E488569708C

@ -1,5 +1,4 @@
{
"C_Cpp.errorSquiggles": "Enabled",
"files.associations": {
"isometricrenderer.h": "c",
"sprite.h": "c",
@ -9,6 +8,12 @@
"textureids.h": "c",
"tile.h": "c",
"raylib.h": "c",
"game.h": "c"
}
"game.h": "c",
"buttons.h": "c",
"string.h": "c",
"uicontainer.h": "c",
"button.h": "c",
"cstdio": "c"
},
"C_Cpp.errorSquiggles": "disabled"
}

@ -17,6 +17,7 @@ Bucket * BucketInit(Sprite *sprite, Tile *tile){
bucket->type = 1;
bucket->sprite = 0;
bucket->tile = tile;
// TODO: Aktuell wird die Depth an der oberen Ecke berechnet, müsste es die untere sein? Oder eher die Mitte? Das scheint mir problematisch zu sein :o
bucket->depth = tile->x * 32 + tile->y * 32 + tile->z;
}

@ -50,6 +50,20 @@ void mouseInput(Game *game){
inputHandler->cursorPos.x = GetMousePosition().x;
inputHandler->cursorPos.y = GetMousePosition().y;
// bissl Kamera Zoom
float maxZoom = 5.0f;
float minZoom = 0.2f;
if(IsKeyPressed(KEY_I)){
if(camera->zoom < maxZoom){
camera->zoom += 0.2f;
}
}
if(IsKeyPressed(KEY_K)){
if(camera->zoom > minZoom){
camera->zoom -= 0.2f;
}
}
// resetting last selected Tile to grass texture
if(inputHandler->selectedLayer != -1){
IsometricMapChangeTextureIdOfTile(layers, (int) inputHandler->cursorWorldTile.x, (int) inputHandler->cursorWorldTile.y, inputHandler->selectedLayer, 0);
@ -64,7 +78,11 @@ void mouseInput(Game *game){
int mouseAdjustmentX = -tileWidthHalf;
int mouseAdjustmentY = -tileHeightQuarter + (tileHeightQuarter * layers[n]->layer);
IsometricMapProject(layers[n], camera, inputHandler->cursorPos.x + mouseAdjustmentX, inputHandler->cursorPos.y + mouseAdjustmentY, &inputHandler->cursorWorldPos);
// Updating inputHandler->cursorWorldPos Vector2D
IsometricMapProject(layers[n], camera,
(inputHandler->cursorPos.x / camera->zoom) + mouseAdjustmentX,
(inputHandler->cursorPos.y / camera->zoom) + mouseAdjustmentY,
&inputHandler->cursorWorldPos);
/*N I C E*/ Tile *selectedTile = IsometricMapGetTileFromWorldCoordinates(layers, inputHandler->cursorWorldPos.x, inputHandler->cursorWorldPos.y, n);

@ -25,7 +25,6 @@ Node * ListCreateNode(Sprite *data);
//Print the list in order
void ListPrintForward(List *list);
void ListInsertFront(List *list, Sprite *data);
void ListInsertBack(List *list, Sprite *data);
List * ListInit();

@ -1,6 +1,6 @@
CC = gcc
FLAGS = -lraylib -lGL -lm -lpthread -ldl -lrt -lX11
OBJS = main.o sprite.o inputHandler.o isometricMap.o list.o game.o textureatlas.o animation.o animationHandler.o bucket.o
OBJS = main.o sprite.o inputHandler.o isometricMap.o list.o game.o textureatlas.o animation.o animationHandler.o bucket.o button.o uiContainer.o debug.o
spiel: $(OBJS)
$(CC) -o spiel $(OBJS) $(FLAGS)
@ -35,5 +35,14 @@ animationHandler.o: Textures/animationHandler.c
bucket.o: DepthSorting/bucket.c
$(CC) -c DepthSorting/bucket.c $(FLAGS)
button.o: Ui/button.c
$(CC) -c Ui/button.c $(FLAGS)
uiContainer.o: Ui/uiContainer.c
$(CC) -c Ui/uiContainer.c $(FLAGS)
debug.o: Ui/debug.c
$(CC) -c Ui/debug.c $(FLAGS)
clean:
rm *.o spiel

@ -17,34 +17,15 @@ Fantasy Welt oder Realistisch?
[Sprite sortierung nach Nähe zur Kamera](https://gamedev.stackexchange.com/questions/8151/how-do-i-sort-isometric-sprites-into-the-correct-order)
[turn off EXIT on ESC Key, for later](https://github.com/raysan5/raylib/issues/520)
## TODO
+ LinkedList erweitern
+ Sprites Animationen etc improven
+ Die Inputs sollten den Kamera Zoom beachten, aktuell geht noch alles kaputt wenn man den zoom umstellt
+ Funktion, um die ganzen Sprites nach ihrer y-Koordinaten sortiert zu drawen
+ Drawable Container machen, die sortiert werden können, dort kommen alle Tiles und Sprites rein, damit sie dann sortiert werden können
+ Das Map Rendering sollte den Zoom auch noch beachten, wenn man rauszoomt wird nicht die ganzen map gedrawed, sondern nur die mit den default values
+ Maps in eigenen Dateien speichern
+ Parser für Map-Dateien
+ MapEditor
* Rendering Reihenfolge: layer 0, Sprites auf layer 0, layer 1, Sprites auf layer 1; Theoretisch müssen die einzelnen Layer Reihenweise gedrawed werden mit den Sprites zwischendrin
+ IsometricMap struct erstellen, das den IsometricMap(+Layer) Array speichert ?
+ Beim rendern müssen die map tiles und die sprites nach ihrer depth (d = x + y + ~0.05*z) sortiert werden, dafür sollten wir ein bucket sorting system implementieren. Buckets sollten erstmal nur tiles und sprites unterstützen.
+ Wir können auch die Sprites in der Liste nach d sortieren, dann geht das gut mit "nur in der Kamera sichtbare Sprites rendern". d ist nicht optimal, vielleicht auch einfach über die ganze Liste gehen und nur die sprites mit $ x y \subset camBounds $ in das Bucket sorting system einfügen. Buckets können auch mit MergeSort oder so sortiert werden
[Vererbung in C, drawable superStruct?](https://de.wikibooks.org/wiki/C%2B%2B-Programmierung:_Vererbung)
+ Mit der Map geht das schon recht einfach, weil wir wissen welche tiles in der Kamera sind. d
+ TODO: Das rendern der IsometricMap wird bei größeren Maps sehr ineffizient;
+ Add offset x and y to each tile to be calculated ONCE, not every frame
+ Alle gehardcodeten screen bounds (450 225 800 400) durch GetScreenWidth() GetScreenHeight() ersetzen
+ Kameraposition abhängiges drawen auch für Sprites implementieren
### WiP
+ Dokumentation aufholen
### Done
+ Movement speed der Sprites an delta time orientieren

@ -0,0 +1,75 @@
#include "button.h"
#include "../game.h"
#include "screenIDs.h"
#include "stdio.h"
#include "stdlib.h"
#include "raylib.h"
#include "string.h"
Button * ButtonInitButton(Texture2D textures[4], Vector2 *position, char *text, int textLEN, int fontSize, int id){
Button *button = malloc(sizeof(Button));
button->textures[0] = textures[0];
button->textures[1] = textures[1];
button->textures[2] = textures[2];
button->textures[3] = textures[3];
button->position = (Vector2){position->x, position->y};
button->centerPosition = (Vector2){position->x + textures[0].width/2, position->y + textures[0].height/2};
strncpy(button->text, text, textLEN);
button->state = BUTTON_STATE_DEFAULT;
button->id = id;
button->fontSize = fontSize;
return button;
}
void ButtonExecuteButton(Button *button, Game *game){
button->state = BUTTON_STATE_DEFAULT;
switch(button->id){
case BUTTON_ID_CONTINUE: // continue game
game->screen = SCREEN_GAME;
break;
case BUTTON_ID_EXIT:
// wahrscheinlich kein guter stil, es muss noch zeug freigegeben werden oder soos... keine Ahnung
game->screen = SCREEN_EXIT;
break;
case BUTTON_ID_START_GAME:
game->screen = SCREEN_GAME;
break;
default:
printf("\n\n\n\n\n\n Unsupported Button ID %d \n\n\n\n\n\n", button->id);
break;
}
}
void ButtonDrawButton(Button * button){
// erst Button Texture, dann Text zentriert drauf
DrawTexture(button->textures[button->state], button->position.x, button->position.y, WHITE);
DrawText(button->text, button->centerPosition.x - MeasureText(button->text, button->fontSize)/2, button->centerPosition.y - button->fontSize/2, button->fontSize, BLACK);
}
int ButtonUpdateButtonState(Button * button){
if(button->state == BUTTON_STATE_RELEASED){
// Wir verlassen den RELEASED State automatisch wenn wir den Code des Buttons ausführen, siehe ButtonExecuteButton
// So lange der Code nicht ausgeführt wurde bleiben wir im state damit er definitiv im nächsten Frame ausgeführt wird
return button->state;
}
else if(GetMouseX() > button->position.x &&
GetMouseX() < button->position.x + button->textures[button->state].width &&
GetMouseY() > button->position.y &&
GetMouseY() < button->position.y + button->textures[button->state].height
){
if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)){
return button->state = BUTTON_STATE_PRESSED;
}
else if(button->state == BUTTON_STATE_PRESSED){
return button->state = BUTTON_STATE_RELEASED;
}
return button->state = BUTTON_STATE_HOVERED;
}
return button->state = BUTTON_STATE_DEFAULT;
}

@ -0,0 +1,36 @@
#ifndef BUTTONS_H_
#define BUTTONS_H_
#include "raylib.h"
#include "../game.h"
#define BUTTON_STATE_DEFAULT 0 // button is just there
#define BUTTON_STATE_HOVERED 1 // mouse is being hovered over the button
#define BUTTON_STATE_PRESSED 2 // left mouse button is down while hovering the button
#define BUTTON_STATE_RELEASED 3 // left mouse button is released while hovering the button, button code will be executed
#define BUTTON_ID_CONTINUE 0 // going to game screen, supposed to be used from pause screen
#define BUTTON_ID_EXIT 1 // closing the game using exit code 0
#define BUTTON_ID_START_GAME 2 // going to game screen, supposed to be used from mainmenu screen
typedef struct Button{
Texture2D textures[4]; // [0]: Normal [1]: Hovered [2]: Pressed [3]: Released
Vector2 position; // Linke obere Ecke des Buttons
Vector2 centerPosition; // Die Mitte des Buttons
char text[20]; // Text, den der Button zentriert anzeigt, aktuell max. 19 Zeichen
int state; // 0: default 1: hovered 2: pressed 3: released
int id; // Durch die ID wird dem Button eine Funktion zugeordnet
int fontSize; // FontSize kann für jeden Button individuell festgelegt werden
} Button;
// Textures: [0]: Normal [1]: Hovered [2]: Pressed [3]: Released
Button * ButtonInitButton(Texture2D textures[4], Vector2 *position, char *text, int textLEN, int fontSize, int id);
// executes the logic of one button of certain id - huge switch?
void ButtonExecuteButton(Button *button, Game * game);
int ButtonUpdateButtonState(Button * button);
void ButtonDrawButton(Button * button);
#endif

@ -0,0 +1,42 @@
#include "debug.h"
#include "../game.h"
#include "uiContainer.h"
#include "string.h"
#include <stdio.h>
#include "../Input/inputHandler.h"
void DebugDraw(Game *game, UiContainer *uiContainer){
char strings[30][41]; // Platz für bis zu 30 Strings der Länge 40
int lineamount = 0; // sollte aktuell gehalten werden, wie viele Lines sind aktuell im Strings Array
int neededWidth = MeasureText("1234567890123456789012345", DEBUG_FONT_SIZE); // sollte an den längsten debug String angepasst werden, damit dieser noch reinpasst
// Hier die Debug Information in den Strings Array einfügen
// im Endeffekt einfach im Array an der Stelle lineamount++ die Elemente einfügen
sprintf(strings[lineamount++], "Screen: %d", game->screen);
sprintf(strings[lineamount++], "MouseScreenX: %d", GetMouseX());
sprintf(strings[lineamount++], "MouseScreenY: %d", GetMouseY());
sprintf(strings[lineamount++], "MouseWorldX: %d", (int)game->inputHandler->cursorWorldPos.x);
sprintf(strings[lineamount++], "MouseWorldY: %d", (int)game->inputHandler->cursorWorldPos.y);
sprintf(strings[lineamount++], "Selected Layer: %d", game->inputHandler->selectedLayer);
sprintf(strings[lineamount++], "DEPTH: %d", (int)(game->inputHandler->cursorWorldPos.x + game->inputHandler->cursorWorldPos.y + game->inputHandler->selectedLayer));
sprintf(strings[lineamount++], "Camera Zoom: %f", game->camera->zoom);
// Hier müssten wir eine bessere Lösung finden, das flackert weil pressed nur für einen Frame gilt. Eine ähnliche Funktion gibt es für CharDown leider nicht, müssten wir selbst programmieren. Ich habe es erstmal nicht auskommentiert. Kann man aber easy machen sollte es stören
int pressed = GetCharPressed();
while(pressed != 0){
sprintf(strings[lineamount++], "Ḱey Pressed: %c", pressed);
pressed = GetCharPressed();
}
// Drawed eine Box für die Debug Info
DrawRectangleLines(0, 0, neededWidth, lineamount * DEBUG_FONT_SIZE + 5, (Color){0, 0, 0, 255});
DrawRectangle(0, 0, neededWidth, lineamount * DEBUG_FONT_SIZE + 5, (Color){0, 0, 0, 150});
// Drawed den Inhalt des "strings" Arrays
int counter = 0;
while(counter < lineamount){
DrawText(strings[counter], 2, counter * DEBUG_FONT_SIZE + 2, DEBUG_FONT_SIZE, (Color){220, 25, 25, 255});
counter++;
}
}

@ -0,0 +1,12 @@
#ifndef DEBUG_H_
#define DEBUG_H_
#include "../game.h"
#include "uiContainer.h"
#define DEBUG_FONT_SIZE 20 // Font size of the Debug window
// Drawed das Debug Fenster in die obere Linke Ecke
void DebugDraw(Game *game, UiContainer *uiContainer);
#endif

@ -0,0 +1,10 @@
#ifndef SCREENIDS_H_
#define SCREENIDS_H_
#define SCREEN_EXIT 0 // Will exit the game using code 0
#define SCREEN_MAINMENU 1
#define SCREEN_OPTIONS 2
#define SCREEN_GAME 3
#define SCREEN_PAUSE 4
#endif

@ -0,0 +1,78 @@
#include "uiContainer.h"
#include "../game.h"
#include "button.h"
#include "raylib.h"
#include "stdlib.h"
#include "stdio.h"
UiContainer * UiContainerInitPauseUiContainer(){
UiContainer *uiContainer = malloc(sizeof(UiContainer));
Texture2D textures[4] = { LoadTexture("assets/button.png"), //DEFAULT
LoadTexture("assets/button_hovered.png"), //HOVERED
LoadTexture("assets/button_pressed.png"), //PRESSED
LoadTexture("assets/button_pressed.png")}; //RELEASED
Vector2 position = (Vector2){GetScreenWidth()/2 - textures[0].width/2, GetScreenHeight()/2 - 50};
int buttonFontSize = 36;
Button *continuebutton = ButtonInitButton(textures, &position, "Continue", 9, buttonFontSize, BUTTON_ID_CONTINUE);
uiContainer->buttons[0] = continuebutton;
uiContainer->buttonCounter = 1;
position.y += 250;
Button *exitButton = ButtonInitButton(textures, &position, "EXIT", 5, buttonFontSize, BUTTON_ID_EXIT);
uiContainer->buttons[1] = exitButton;
uiContainer->buttonCounter = 2;
// Methode funktioniert wieso auch immer auch ohne dieses return. C returned Implizit odder was O_o
return uiContainer;
}
UiContainer * UiContainerInitMainMenuUiContainer(){
UiContainer *uiContainer = malloc(sizeof(UiContainer));
Texture2D textures[4] = { LoadTexture("assets/button.png"), //DEFAULT
LoadTexture("assets/button_hovered.png"), //HOVERED
LoadTexture("assets/button_pressed.png"), //PRESSED
LoadTexture("assets/button_pressed.png")}; //RELEASED
Vector2 position = (Vector2){GetScreenWidth()/2 - textures[0].width/2, GetScreenHeight()/2 - 50};
int buttonFontSize = 36;
Button *continuebutton = ButtonInitButton(textures, &position, "Start Game", 11, buttonFontSize, BUTTON_ID_START_GAME);
uiContainer->buttons[0] = continuebutton;
uiContainer->buttonCounter = 1;
position.y += 250;
Button *exitButton = ButtonInitButton(textures, &position, "EXIT", 5, buttonFontSize, BUTTON_ID_EXIT);
uiContainer->buttons[1] = exitButton;
uiContainer->buttonCounter = 2;
// Methode funktioniert wieso auch immer auch ohne dieses return. C returned Implizit odder was O_o
return uiContainer;
}
UiContainer * UiContainerInitGameUiContainer(){
printf("\n\n\n\n UiContainerInitGameUiContainer not implemented yet!! \n\n\n\n");
return 0;
}
// Updated alle Buttons und führt gegebenenfalls deren Code aus
void UiContainerUpdateUiContainer(UiContainer *uiContainer, Game *game){
int i = 0;
for(i=0 ; i < uiContainer->buttonCounter; i++){
ButtonUpdateButtonState(uiContainer->buttons[i]);
if(uiContainer->buttons[i]->state == BUTTON_STATE_RELEASED){
ButtonExecuteButton(uiContainer->buttons[i], game);
}
}
}
// Drawed alle Buttons
void UiContainerDrawUiContainer(UiContainer *uiContainer){
int i = 0;
for(i=0 ; i < uiContainer->buttonCounter; i++){
ButtonDrawButton(uiContainer->buttons[i]);
}
}

@ -0,0 +1,20 @@
#ifndef UICONTAINER_H_
#define UICONTAINER_H_
#include "raylib.h"
#include "button.h"
#include "../game.h"
typedef struct UiContainer{
Button *buttons[15]; // Platz für max. 15 Buttons
int buttonCounter; // Zeigt wie viele Buttons der UiContainer aktuell schon speichert
} UiContainer;
void UiContainerUpdateUiContainer(UiContainer *uiContainer, Game *game);
void UiContainerDrawUiContainer(UiContainer *uiContainer);
UiContainer * UiContainerInitPauseUiContainer();
UiContainer * UiContainerInitGameUiContainer();
UiContainer * UiContainerInitMainMenuUiContainer();
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -7,6 +7,7 @@
#include "IsometricMap/isometricMap.h"
#include "Textures/textureatlas.h"
#include "stdio.h"
#include "Ui/screenIDs.h"
// returns pointer to new Game instance
Game *GameInit()
@ -30,6 +31,8 @@ Game *GameInit()
game->inputHandler->cursorTextures = game->textures->cursorTextures;
game->inputHandler->cursorSprite = game->cursorSprite;
game->screen = SCREEN_MAINMENU;
game->camera = malloc(sizeof(Camera2D));
game->camera->target.x = 0;
game->camera->target.y = 0;

@ -11,10 +11,7 @@ typedef struct Game{
struct InputHandler *inputHandler;
struct Camera2D *camera;
struct IsometricMap **layers;
// Head of the sorted buckets. Used to draw sprites/tiles correctly on the screen.
// Head is the bucket with the highest depth
struct Bucket *head;
int screen;
} Game;
// returns pointer to new Game instance

117
main.c

@ -1,4 +1,5 @@
#include "raylib.h"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>
#include "sprite.h"
@ -8,46 +9,110 @@
#include "IsometricMap/isometricMap.h"
#include "game.h"
#include "DepthSorting/bucket.h"
#include "Ui/screenIDs.h"
#include "Ui/button.h"
#include "Ui/uiContainer.h"
#include "Ui/debug.h"
int main(){
InitWindow(800, 450, "basic window");
InitWindow(1280, 720, "basic window");
Game *game = GameInit();
// Hides the operating systems own cursor
HideCursor();
// TODO: Screen structs, die zum Beispiel die UiContainer enthalten?
//SetTargetFPS(60);
// GAME MAIN ROUTINE
while(!WindowShouldClose()){
UiContainer *pauseScreenUiContainer = UiContainerInitPauseUiContainer();
UiContainer *mainMenuScreenUiContainer = UiContainerInitMainMenuUiContainer();
ListActAllSprites(game);
// Hides the operating systems own cursor
HideCursor();
ClearBackground(RAYWHITE);
BeginDrawing();
SetTargetFPS(60);
BeginMode2D(*(game->camera));
//IsometricRendererRenderIsometricMap(game);
//ListDrawAllSprites(game->sprites, game->layers, game->camera);
IsometricMapDraw(game);
EndMode2D();
while(!WindowShouldClose()){
// Moving cursor Sprite to Mouse Pos and drawing it
game->cursorSprite->x = game->inputHandler->cursorPos.x;
game->cursorSprite->y = game->inputHandler->cursorPos.y;
// Moving cursor Sprite to Mouse Pos
game->cursorSprite->x = GetMousePosition().x;
game->cursorSprite->y = GetMousePosition().y;
BeginDrawing(); // Drawing ist grundsätzlich immer aktiviert
ClearBackground(RAYWHITE); // Screen wird in jedem Frame gecleared
switch(game->screen){ // Screenspecific Code
case SCREEN_EXIT:
printf("EXIT \n");
return 0;
case SCREEN_MAINMENU:
// MainMenu hat aktuell nur paar Buttons
UiContainerUpdateUiContainer(mainMenuScreenUiContainer, game);
UiContainerDrawUiContainer(mainMenuScreenUiContainer);
break;
case SCREEN_OPTIONS:
printf("OPTIONS \n");
return 0;
case SCREEN_GAME:
// Updating Sprites
ListActAllSprites(game);
// Drawing IsometricMap
BeginMode2D(*(game->camera)); // Sorgt dafür, dass die Kameraposition beachtet wird
IsometricMapDraw(game);
EndMode2D();
// User Input Handling
mouseInput(game);
keyboardInput(game->inputHandler, game->camera);
if(IsKeyPressed(KEY_P)){
game->screen = SCREEN_PAUSE;
}
break;
case SCREEN_PAUSE:
// Still drawing isometric map, which is not updated atm
BeginMode2D(*(game->camera)); // Sorgt dafür, dass die Kameraposition beachtet wird
IsometricMapDraw(game);
EndMode2D();
// darkened background + "Paused" Text
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), (Color){0, 0, 0, 150});
int textWidthHalf = MeasureText("Paused", 28) / 2;
DrawText("Paused", GetScreenWidth()/2 - textWidthHalf, GetScreenHeight()/4 - 14, 28, WHITE);
// Controls lol
DrawText("I: Zoom in", 5, GetScreenHeight()/2, 16, WHITE);
DrawText("K: Zoom out", 5, GetScreenHeight()/2 + 16, 16, WHITE);
DrawText("P: Pause", 5, GetScreenHeight()/2 + 32, 16, WHITE);
DrawText("WASD: Move Camera", 5, GetScreenHeight()/2 + 48, 16, WHITE);
DrawText("ESC: Exit Game", 5, GetScreenHeight()/2 + 64, 16, WHITE);
// Button / UI stuff
UiContainerUpdateUiContainer(pauseScreenUiContainer, game);
UiContainerDrawUiContainer(pauseScreenUiContainer);
// Debug Menu
DebugDraw(game, pauseScreenUiContainer);
if(IsKeyPressed(KEY_P)){
game->screen = SCREEN_GAME;
}
break;
default:
printf("\n\n\n\n\n\n Wir haben ein problematisches Problem! Die Screen-ID [%d] ist nicht definiert. Hmmpf... früher bescheid wisse!\n\n\n\n\n\n", game->screen);
return 1;
break;
}
// Dinge die grundsätzlich immer gedrawed werden sollen
// Debug Menu, FPS anzeige, Cursor
DebugDraw(game, pauseScreenUiContainer);
DrawFPS(GetScreenWidth() - 95, 10);
DrawSpriteToScreen(game->cursorSprite);
EndDrawing();
// User Input Handling
mouseInput(game);
keyboardInput(game->inputHandler, game->camera);
DrawFPS(GetScreenWidth() - 95, 10);
EndDrawing();
}
@ -57,5 +122,3 @@ int main(){
}
Loading…
Cancel
Save