Merge branch 'dev' - Game fully functional
Game runs well without any known bugs
This commit is contained in:
commit
4cf3cf73a4
4
.gitignore
vendored
4
.gitignore
vendored
@ -26,10 +26,14 @@ compile_commands.json
|
||||
*.exe
|
||||
*.out
|
||||
|
||||
# Binary files
|
||||
*.bin
|
||||
|
||||
# Libraries
|
||||
*.a
|
||||
*.lib
|
||||
SDL2
|
||||
SDL2_ttf
|
||||
|
||||
# Ignore any backup files
|
||||
*~
|
@ -12,17 +12,29 @@ if(WIN32)
|
||||
set(SDL2_LIBS "${SDL2_FOLDER}/lib/x64/SDL2.lib")
|
||||
set(SDL2_DLLS "${SDL2_FOLDER}/lib/x64/SDL2.dll")
|
||||
set(SDL2_DIR "${SDL2_FOLDER}/cmake/")
|
||||
# On Linux, the library is found automatically if installed
|
||||
|
||||
# Specify downloaded SDL2_ttf library folder location
|
||||
set(SDL2_TTF_FOLDER "${CMAKE_SOURCE_DIR}/SDL2_ttf/")
|
||||
|
||||
set(SDL2_TTF_INCLUDE_DIRS "${SDL2_TTF_FOLDER}/include")
|
||||
set(SDL_TTF_LIBRARIES "${SDL2_TTF_FOLDER}/lib/x64/SDL2_ttf.lib")
|
||||
set(SDL2_TTF_LIBS "${SDL2_TTF_FOLDER}/lib/x64/SDL2_ttf.lib")
|
||||
set(SDL2_TTF_DLLS "${SDL2_TTF_FOLDER}/lib/x64/SDL2_ttf.dll")
|
||||
set(SDL2_TTF_DIR "${SDL2_TTF_FOLDER}/cmake/")
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "${SDL2_TTF_FOLDER}")
|
||||
endif()
|
||||
# Note: On Linux, the libraries are found automatically if installed
|
||||
|
||||
|
||||
# --- Locate SDL2 ---
|
||||
# Uses SDL2_DIR on Windows, on Linux it's found automatically
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# --- Locate SDL2_ttf ---
|
||||
find_package(SDL2_ttf REQUIRED)
|
||||
|
||||
#--- Include directories ---
|
||||
include_directories(${SDL2_INCLUDE_DIRS} ./include ./src)
|
||||
include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIRS} ./include ./src)
|
||||
|
||||
|
||||
# --- Source files ---
|
||||
@ -39,17 +51,72 @@ set(SOURCES
|
||||
src/map.c
|
||||
src/common.c
|
||||
src/sound.c
|
||||
src/difficulty.c
|
||||
src/files.c
|
||||
)
|
||||
|
||||
|
||||
#--- executable ---
|
||||
add_executable(Snake ${SOURCES})
|
||||
target_link_libraries(Snake ${SDL2_LIBRARIES})
|
||||
|
||||
|
||||
#--- link libraries ---
|
||||
if(WIN32)
|
||||
# Link libraries statically on Windows to prevent missing basic libraries on other systems.
|
||||
target_link_options(Snake PRIVATE -static)
|
||||
target_link_libraries(Snake ${SDL2_LIBRARIES} ${SDL_TTF_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(Snake SDL2::SDL2 SDL2_ttf::SDL2_ttf)
|
||||
endif()
|
||||
|
||||
|
||||
# --- Copy assets to output folder ---
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/assets DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
|
||||
# --- Copy SDL2 DLLs to the output folder on Windows ---
|
||||
if(WIN32)
|
||||
foreach(DLL ${SDL2_DLLS})
|
||||
foreach(DLL ${SDL2_DLLS} ${SDL2_TTF_DLLS})
|
||||
add_custom_command(TARGET Snake POST_BUILD COMMAND
|
||||
${CMAKE_COMMAND} -E copy_if_different ${DLL} $<TARGET_FILE_DIR:Snake>)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
############################################
|
||||
######## generate release .zip file ########
|
||||
############################################
|
||||
# Generate a distributable ZIP archive for sharing the game.
|
||||
# Note: currently only intended for windows systems
|
||||
# Usage:
|
||||
# 1. Build the project: cd build && cmake .. && make
|
||||
# 2. Generate ZIP file: cpack
|
||||
# 3. Share the ZIP file for others to run the game effortlessly.
|
||||
if(WIN32)
|
||||
# generator for zip archive TODO add other generator e.g. installer
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
|
||||
# Specify to exclude the top-level directory from the archive.
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY ON)
|
||||
|
||||
# Specify the components to be included in the package
|
||||
install(TARGETS Snake
|
||||
RUNTIME DESTINATION .
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# copy DLL files
|
||||
install(FILES ${SDL2_DLLS} ${SDL2_TTF_DLLS}
|
||||
DESTINATION .
|
||||
COMPONENT Runtime
|
||||
)
|
||||
# copy assets folder
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/assets
|
||||
DESTINATION .
|
||||
COMPONENT Assets
|
||||
)
|
||||
|
||||
include(CPack)
|
||||
endif()
|
37
README.md
37
README.md
@ -1,11 +1,26 @@
|
||||
Development of the game Snake++ using C, C++ and SDK2 for the Software Engineering course as part of our studies.
|
||||
# Snake++
|
||||
Development of the game "Snake" featuring portals, custom maps and sound effects.
|
||||
Cross-platform compatibility using CMake, C, C++ and SDL2.
|
||||
A project for the Software Engineering course as part of our studies.
|
||||
|
||||
## Preview
|
||||
<img src="demo.jpg" width="70%">
|
||||
|
||||
|
||||
# Installation
|
||||
For Windows 64-Bit a pre-compiled release, that also includes the required .dll files and assets is available:
|
||||
- Download the zip file from [Releases](https://github.com/Jonny999999/snake-pp/releases/latest) (e.g. "Snake-1.0-win64.zip")
|
||||
- Extract zip file
|
||||
- Navigate to the folder and run `Snake.exe`
|
||||
On other Systems you currently have to compile from source (see next sections)
|
||||
|
||||
|
||||
# Compilation
|
||||
|
||||
## Linux
|
||||
**Install tools and SDL2**
|
||||
```bash
|
||||
pacman -S sdl2 sdl2_rrf
|
||||
pacman -S sdl2 sdl2_ttf
|
||||
pacman -S cmake gcc
|
||||
```
|
||||
**Build**
|
||||
@ -18,10 +33,16 @@ make
|
||||
|
||||
## Windows
|
||||
**Download SDL**
|
||||
- Download `SDL2-devel-2.28.5-VC.zip` from https://github.com/libsdl-org/SDL/releases/tag/release-2.28.5
|
||||
- Download `SDL2-devel-2.28.5-VC.zip` from [github/libsdl-org](https://github.com/libsdl-org/SDL/releases/download/release-2.28.5/SDL2-devel-2.28.5-VC.zip)
|
||||
- Unzip the file and rename the folder to `SDL2` (avoid unnecessary subfolder)
|
||||
- Place it in the root folder of this repository.
|
||||
|
||||
**Download SDL_ttf**
|
||||
- Download `SDL2_ttf-devel-2.20.2-VC.zip` from [github/libsdl-org](https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.2/SDL2_ttf-devel-2.20.2-VC.zip)
|
||||
- Unzip the file and rename the folder to `SDL2_ttf` (avoid unnecessary subfolder)
|
||||
- Place it in the root folder of this repository.
|
||||
|
||||
|
||||
**Install compiler** (if not available already)
|
||||
- download mingw:
|
||||
- visit https://altushost-swe.dl.sourceforge.net/project/mingw-w64/
|
||||
@ -40,17 +61,17 @@ See VS Code section
|
||||
|
||||
# VS Code instructions
|
||||
## Required extensions
|
||||
- Cmake
|
||||
- CmakeTools
|
||||
- C/C++
|
||||
- [Cmake](https://open-vsx.org/extension/twxs/cmake)
|
||||
- [CmakeTools](https://open-vsx.org/extension/ms-vscode/cmake-tools)
|
||||
- [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)
|
||||
|
||||
## setup
|
||||
## Setup
|
||||
- With CmakeTools installed open the project folder in VS Code
|
||||
- open cmd-prompt with `CTRL + SHIFT + P` run `cmake.build`
|
||||
- select kit (gcc or installed minGW compiler)
|
||||
Alternatively use cmake related buttons in bottom toolbar (CMake: Kit, Build, [all]...)
|
||||
|
||||
## compile
|
||||
## Compile
|
||||
- Use buttons in bottom toolbar (CMake: ... Build [BUG-BUTTON] [PLAY-BUTTON] ... )
|
||||
You can also use:
|
||||
- `ctrl-F5` run without debugger
|
||||
|
BIN
assets/fonts/CenturyGothic.ttf
Normal file
BIN
assets/fonts/CenturyGothic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Prototype.ttf
Normal file
BIN
assets/fonts/Prototype.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Quirkus.ttf
Normal file
BIN
assets/fonts/Quirkus.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/ZIPERHEA.ttf
Normal file
BIN
assets/fonts/ZIPERHEA.ttf
Normal file
Binary file not shown.
BIN
assets/sounds/crash_rock-cinematic.wav
Normal file
BIN
assets/sounds/crash_rock-cinematic.wav
Normal file
Binary file not shown.
BIN
assets/sounds/portal3_in-out.wav
Normal file
BIN
assets/sounds/portal3_in-out.wav
Normal file
Binary file not shown.
BIN
demo.jpg
Normal file
BIN
demo.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 26 KiB |
@ -24,6 +24,21 @@
|
||||
#define LOGI(format, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
//conditional logging when ERROR_OUTPUT_ENABLED is defined in config.h
|
||||
//also prints in text in red color (windows not supported)
|
||||
//example: LOGE("game: %d", count)
|
||||
#ifdef ERROR_OUTPUT_ENABLED
|
||||
#ifdef _WIN32 //print error output in default color (currently no color support for windows)
|
||||
#define LOGE(format, ...) printf("[E] " format, ##__VA_ARGS__)
|
||||
#else //print error output in red color
|
||||
#define RED_TEXT "\033[1;31m"
|
||||
#define RESET_TEXT "\033[0m"
|
||||
#define LOGE(format, ...) printf(RED_TEXT "[E] " format RESET_TEXT, ##__VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define LOGE(format, ...) do {} while (1)
|
||||
#endif
|
||||
|
||||
|
||||
//===========================
|
||||
//========== DELAY ==========
|
||||
|
@ -7,6 +7,7 @@
|
||||
// logging settings
|
||||
//#define DEBUG_OUTPUT_ENABLED
|
||||
#define INFO_OUTPUT_ENABLED
|
||||
#define ERROR_OUTPUT_ENABLED
|
||||
//#define RENDER_GAME_TO_CONSOLE
|
||||
|
||||
|
||||
|
12
include/difficulty.h
Normal file
12
include/difficulty.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "config.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// All functions/options regarding/affecting the difficulty level
|
||||
// -> collected in one file for easy adjustment
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
// set parameters for food placement by current difficulty level
|
||||
void difficulty_getFoodPlacementParam(float * minDist, float * maxDist);
|
27
include/files.h
Normal file
27
include/files.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "config.h"
|
||||
|
||||
#define MAX_PRINTED_SCORES 10
|
||||
|
||||
extern int recordsInFile;
|
||||
|
||||
// struct that store player score at the end of the game
|
||||
typedef struct playerScore_t
|
||||
{
|
||||
int score;
|
||||
char playerName[30];
|
||||
int difficulty;
|
||||
char map[30];
|
||||
} playerScore_t;
|
||||
|
||||
// global struct for storing the 10 best players after reading it from the file (defined in files.c)
|
||||
extern playerScore_t topScores[MAX_PRINTED_SCORES];
|
||||
|
||||
// function which saves score in a .bin file
|
||||
void savePlayerScore(const char *filename);
|
||||
|
||||
// function which reads the 10 best finisher from the .bin file
|
||||
void readTopScores(const char *filename);
|
||||
|
||||
// counts records in file
|
||||
int countRecordsInFile(const char *filename);
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "snake.h"
|
||||
#include "config.h"
|
||||
@ -31,10 +32,15 @@ typedef struct gameData_t
|
||||
gameState_t gameState; // state the game is in
|
||||
} gameData_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// global struct for storing all game data (defined in game.c)
|
||||
extern gameData_t game;
|
||||
|
||||
|
||||
|
||||
// run once at game start and does the following:
|
||||
// - init snake
|
||||
// - load map
|
||||
@ -50,4 +56,5 @@ void handlePortals(); //(ran in gameCycle)
|
||||
// - moves snake to next position
|
||||
// - handles collision, portals, food
|
||||
// - triggers frame update (render.c)
|
||||
void runGameCycle();
|
||||
void runGameCycle();
|
||||
|
||||
|
@ -39,7 +39,7 @@ void updateBlockSizePx();
|
||||
|
||||
// search and load map by name in storedMaps[] (map.c)
|
||||
// stops program when map not found!
|
||||
void loadMapByName(char *name);
|
||||
void loadMapByName(const char *name);
|
||||
|
||||
|
||||
//load map by passed definition
|
||||
|
@ -1,6 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_ttf.h"
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_COLOURS 10
|
||||
#define MAX_LINES_TFF 20 //max lines (of ttf) for the whole project
|
||||
#define MAX_LINES_STARTSCREEN 6
|
||||
#define MAX_LINES_SETTINGS 14
|
||||
#define MAX_LINES_INFOSCREEN 17
|
||||
|
||||
#define TEXT_INPUT_SIZE 30
|
||||
#define TIME_BETWEEN_UPDATE_MENU 1000 // [ms]
|
||||
|
||||
// Enum that defines the active menu
|
||||
typedef enum menus_t
|
||||
{
|
||||
NONE = 0,
|
||||
START,
|
||||
SETTINGS,
|
||||
INFOSCREEN,
|
||||
LEADERBOARD,
|
||||
PAUSE
|
||||
} menus_t;
|
||||
|
||||
// Struct that include pointer for ttl-functions in menu.c and render.c
|
||||
typedef struct tllData_t
|
||||
{
|
||||
TTF_Font *ptrFont_20;
|
||||
TTF_Font *ptrFont_30; // used by menu.c and render.c
|
||||
TTF_Font *ptrFont_200; // used by menu.c and render.c
|
||||
SDL_Surface *textSurface; // used by menu.c and render.c
|
||||
SDL_Texture *textTexture; // used by menu.c and render.c
|
||||
SDL_Texture *textTextures[MAX_LINES_TFF];
|
||||
SDL_Color textColour[MAX_COLOURS]; // colour in which the text is printed
|
||||
const int fontSize_20;
|
||||
const int fontSize_30;
|
||||
const int fontSize_200;
|
||||
int64_t lastTimeStep;
|
||||
int lineHeight; // to print text in middle
|
||||
int totalHeight; // to print text in middle
|
||||
int textPrintPosition; // where first line is printed
|
||||
const time_t cycleDuration; // time between blinking ENTER
|
||||
bool showEnter; // ENTER should be printed only every second cycle
|
||||
int inputStatus; // 1 if player name was entered; 2 if difficulty level was entered, 3 map was entered
|
||||
char textInput[TEXT_INPUT_SIZE]; // auxiliary variable for user input
|
||||
char numbers[2][TEXT_INPUT_SIZE]; // auxiliary variable to store entered textInput-number into local pointer 'textLinesInMenu' in render.c
|
||||
char userName[TEXT_INPUT_SIZE]; // user name
|
||||
int userDifficultyLevel; // difficulty level which was entered by user
|
||||
int userSelectedMap; // map which was entered by user
|
||||
} ttlData_t;
|
||||
|
||||
extern ttlData_t ttlStorage;
|
||||
|
||||
extern menus_t activeMenu;
|
||||
|
||||
|
||||
|
||||
|
||||
// edit various menus
|
||||
// is called up in main.cpp
|
||||
void manageMenu();
|
||||
|
||||
void showStartScreen();
|
||||
//zum Starten Enter drücken
|
||||
@ -14,6 +75,12 @@ void showLeaderboard();
|
||||
void showSettings(); //optional
|
||||
//startet Settungs-Menü
|
||||
|
||||
|
||||
// show info screen
|
||||
void showInfoScreen();
|
||||
|
||||
void menuHandleInput(SDL_Event event); //als Übergabeparameter: int(?) event -> welcher Datentyp hängt von SDL ab
|
||||
//switch case für welcher Modus
|
||||
//switch case für welche Taste gedrückt wurde
|
||||
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include "game.h"
|
||||
#include "snake.h"
|
||||
#include "menu.h"
|
||||
#include "SDL.h"
|
||||
|
||||
void renderGame();
|
||||
@ -9,4 +11,12 @@ void renderGame();
|
||||
|
||||
int CreateSDLWindow();
|
||||
|
||||
void DestroySDLWindow();
|
||||
void DestroySDLWindow();
|
||||
|
||||
void renderStartMenu();
|
||||
|
||||
void renderSettings();
|
||||
|
||||
void renderInfoScreen();
|
||||
|
||||
void renderLeaderboard();
|
Binary file not shown.
Binary file not shown.
@ -8,7 +8,7 @@ config_t config = {
|
||||
.cycleDurationMs = 400,
|
||||
.difficulty = 1,
|
||||
.snakeDefaultLength = 2,
|
||||
.leaderboardFilename = "",
|
||||
.leaderboardFilename = "player_scores.bin",
|
||||
//.defaultMapName = "default" //10x10
|
||||
.defaultMapName = "intermediate" //15x15
|
||||
//.defaultMapName = "empty" //20x10
|
||||
|
33
src/difficulty.c
Normal file
33
src/difficulty.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include "difficulty.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// All functions/options regarding/affecting the difficulty level
|
||||
// -> collected in one file for easy adjustment
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
||||
//======================================
|
||||
//== difficulty_getFoodPlacementParam ==
|
||||
//======================================
|
||||
// set parameters for food placement by current difficulty level
|
||||
void difficulty_getFoodPlacementParam(float *minDist, float *maxDist)
|
||||
{
|
||||
switch (config.difficulty)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
case 1: //EASY/default: always place far away
|
||||
*minDist = 3.0; // new food has to be at least minDist blocks away from any object
|
||||
*maxDist = 5.0; // new food has to be closer than maxDist to an object
|
||||
break;
|
||||
case 2: //MEDIUM: place next to OR further away
|
||||
*minDist = 1.0;
|
||||
*maxDist = 4.0;
|
||||
break;
|
||||
case 3: //HARD: always place next to block
|
||||
*minDist = 1.0;
|
||||
*maxDist = 1.0;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
146
src/files.c
Normal file
146
src/files.c
Normal file
@ -0,0 +1,146 @@
|
||||
#include "game.h"
|
||||
#include "menu.h"
|
||||
#include "files.h"
|
||||
|
||||
|
||||
|
||||
//global struct for storing all data of the 10 best players
|
||||
playerScore_t topScores[];
|
||||
|
||||
int recordsInFile;
|
||||
|
||||
//==========================
|
||||
//==== savePlayerScores ====
|
||||
//==========================
|
||||
void savePlayerScore(const char *filename/*int score, int difficulty, const char *playerName, const char *map*/)
|
||||
{
|
||||
playerScore_t playerScore;
|
||||
|
||||
// copy data into struct
|
||||
playerScore.score = game.snake.length - config.snakeDefaultLength;
|
||||
playerScore.difficulty = config.difficulty;
|
||||
strcpy(playerScore.playerName, ttlStorage.userName);
|
||||
//strcpy(playerScore.map, "testmap");
|
||||
|
||||
strcpy(playerScore.map, storedMaps[ttlStorage.userSelectedMap - 1]->name);
|
||||
|
||||
|
||||
FILE *file;
|
||||
// open file
|
||||
file = fopen(filename, "ab");
|
||||
|
||||
// write data in file
|
||||
if (file != NULL)
|
||||
{
|
||||
fwrite(&playerScore, sizeof(playerScore_t), 1, file);
|
||||
fclose(file);
|
||||
|
||||
LOGI("Datei: Spielergebnis erfolgreich in %s gespeichert.\n", config.leaderboardFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("file: Fehler beim Öffnen der Datei!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================
|
||||
//==== readTopScores ======
|
||||
//==========================
|
||||
// number of reads depends on 'MAX_PRINTED_SCORES'
|
||||
void readTopScores(const char *filename)
|
||||
{
|
||||
FILE *filePtr;
|
||||
playerScore_t tempPlayerScore;
|
||||
int highestPlayerScore = 0;
|
||||
int count = 0; // increase up to 'MAX_PRINTED_SCORES'
|
||||
|
||||
// determine the number of contents in the file
|
||||
recordsInFile = countRecordsInFile(filename);
|
||||
// if failure
|
||||
if(recordsInFile == -1)
|
||||
{
|
||||
game.gameState = EXIT;
|
||||
return;
|
||||
}
|
||||
|
||||
filePtr = fopen(filename, "rb");
|
||||
|
||||
// fail with file opening
|
||||
if (filePtr == NULL)
|
||||
{
|
||||
LOGE("Datei: Fehler beim Öffnen der Datei für die besten 10 Ergebnisse!\n");
|
||||
game.gameState = EXIT;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LOGI("Datei: Datensaetze in Datei: %d\n", recordsInFile);
|
||||
|
||||
|
||||
//---- search for the highest score------
|
||||
for (int i = 0; i < recordsInFile; i++)
|
||||
{
|
||||
fread(&tempPlayerScore, sizeof(playerScore_t), 1, filePtr);
|
||||
if(tempPlayerScore.score > highestPlayerScore)
|
||||
{
|
||||
highestPlayerScore = tempPlayerScore.score;
|
||||
}
|
||||
}
|
||||
|
||||
//--- decrease highest score -----
|
||||
while((count < MAX_PRINTED_SCORES) && (count < recordsInFile))
|
||||
{
|
||||
// set file pointer to start of the file
|
||||
rewind(filePtr);
|
||||
// search for the highest score and then save it in topScores
|
||||
for (int i = 0; i < recordsInFile; i++)
|
||||
{
|
||||
// read record from the file
|
||||
fread(&tempPlayerScore, sizeof(playerScore_t), 1, filePtr);
|
||||
|
||||
// current highscore found
|
||||
if(tempPlayerScore.score == highestPlayerScore)
|
||||
{
|
||||
topScores[count] = tempPlayerScore;
|
||||
LOGI("Datei: score: %d name: %s schwierigkeit: %d map: %s\n", topScores[count].score, topScores[count].playerName, topScores[count].difficulty, topScores[count].map);
|
||||
count++;
|
||||
}
|
||||
// leave if limit is reached
|
||||
if(count >= recordsInFile || count >= MAX_PRINTED_SCORES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
highestPlayerScore--;
|
||||
}
|
||||
fclose(filePtr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================
|
||||
//==== readTop10Scores =====
|
||||
//==========================
|
||||
// return the number of various scores of the file
|
||||
int countRecordsInFile(const char *filename)
|
||||
{
|
||||
FILE *file;
|
||||
file = fopen(filename, "rb");
|
||||
|
||||
// failure
|
||||
if (file == NULL)
|
||||
{
|
||||
LOGI("Datei: Fehler beim Öffnen der Datei!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END); // Gehe zum Dateiende
|
||||
long fileSize = ftell(file); // Hole die Größe der Datei in Bytes
|
||||
fclose(file);
|
||||
|
||||
int recordSize = sizeof(playerScore_t);
|
||||
int numberOfRecords = fileSize / recordSize; // Berechne die Anzahl der Datensätze
|
||||
|
||||
return numberOfRecords;
|
||||
}
|
19
src/food.c
19
src/food.c
@ -5,6 +5,7 @@
|
||||
#include "common.h"
|
||||
#include "map.h"
|
||||
#include "game.h"
|
||||
#include "difficulty.h"
|
||||
|
||||
|
||||
|
||||
@ -56,7 +57,7 @@ newValues:
|
||||
{
|
||||
//decrease min distance but not below 1
|
||||
if ((in_minDist -= 0.1) < 1) in_minDist = 1;
|
||||
LOGI("[WARN] food: too much tries achieving min dist -> loosen limit to %.1f\n", in_minDist);
|
||||
LOGI("[WARN] food: too many tries achieving min dist -> loosen limit to %.1f\n", in_minDist);
|
||||
}
|
||||
//reset stored distance and reroll coordinates
|
||||
minActualDistance = MAX_MAP_SIZE;
|
||||
@ -87,14 +88,14 @@ newValues:
|
||||
void placeFood()
|
||||
{
|
||||
//--- config ---
|
||||
static const float minDist = 3; // new food has to be at least minDist blocks away from any object
|
||||
float maxDist = 5; // new food has to be closer than maxDist to an object
|
||||
float minDist; // new food has to be at least minDist blocks away from any object
|
||||
float maxDist; // new food has to be closer than maxDist to an object
|
||||
static const int maxTries = 25; // each maxTries the limit maxDist get loosened (prevents deadlock)
|
||||
// TODO calculate this range using configured difficulty level
|
||||
// e.g. in hard difficulty the maxDist could be <2 so it is always placed close to a collision
|
||||
// get food placement parameters according to difficulty (difficulty.c)
|
||||
difficulty_getFoodPlacementParam(&minDist, &maxDist);
|
||||
|
||||
//--- variables ---
|
||||
int foodX, foodY, triesMax = 0, triesMin;
|
||||
int foodX, foodY, triesMax = 0, triesMin, triesTotal = 0;
|
||||
float currentMinDist;
|
||||
|
||||
//--- generate random food position within min/max range ---
|
||||
@ -106,15 +107,17 @@ void placeFood()
|
||||
if (triesMax % maxTries == 0)
|
||||
{
|
||||
maxDist += 0.1;
|
||||
LOGI("[WARN] food: too many tries for MAX_DIST -> loosen limits to max=%.1f\n", maxDist);
|
||||
LOGI("[WARN] food: too many tries achieving max dist -> loosen limits to max=%.1f\n", maxDist);
|
||||
}
|
||||
// generate random coordinates respecting minimum distance to objects
|
||||
getRandomPositionWithMinDistance(&foodX, &foodY, ¤tMinDist, &triesMin, minDist);
|
||||
triesTotal += triesMin;
|
||||
//restart when max distance limit exceeded
|
||||
} while (currentMinDist > maxDist);
|
||||
|
||||
//--- update position ---
|
||||
LOGI("food: placed food at x=%d, y=%d (took %d = %d*%d tries)\n", foodX, foodY, triesMax * triesMin, triesMax, triesMin);
|
||||
LOGI("food: placed food at x=%d, y=%d (took %d tries)\n", foodX, foodY, triesTotal);
|
||||
LOGD("food: tries for constraints: Max=%d Min(last)=%d Total=%d)\n", triesMax, triesMin, triesTotal);
|
||||
game.foodX = foodX;
|
||||
game.foodY = foodY;
|
||||
return;
|
||||
|
45
src/game.c
45
src/game.c
@ -5,6 +5,7 @@
|
||||
#include "food.h"
|
||||
#include "render.h"
|
||||
#include "sound.h"
|
||||
#include "files.h"
|
||||
|
||||
|
||||
// global struct for storing all game data
|
||||
@ -16,16 +17,16 @@ gameData_t game = {
|
||||
.mapIsLoaded = false,
|
||||
.lifesRemaining = 1,
|
||||
.timestampLastCycle = 0,
|
||||
.gameState = RUNNING,
|
||||
.gameState = MENU,
|
||||
};
|
||||
|
||||
|
||||
// list of audio files randomly played when food eaten
|
||||
const char *eatSounds[] = {
|
||||
"../sounds/eat-bite1.wav",
|
||||
"../sounds/eat-bite2.wav",
|
||||
"../sounds/eat-crunch1.wav",
|
||||
"../sounds/eat-crunch2.wav"};
|
||||
"assets/sounds/eat-bite1.wav",
|
||||
"assets/sounds/eat-bite2.wav",
|
||||
"assets/sounds/eat-crunch1.wav",
|
||||
"assets/sounds/eat-crunch2.wav"};
|
||||
#define EAT_SOUNDS_COUNT 4
|
||||
|
||||
|
||||
@ -43,7 +44,9 @@ void gameInit()
|
||||
//load default map if no map loaded yet
|
||||
if (!game.mapIsLoaded){
|
||||
//loadMapByName("default");
|
||||
loadMapByName(config.defaultMapName);
|
||||
|
||||
//loadMapByName(config.defaultMapName);
|
||||
loadMap(*storedMaps[ttlStorage.userSelectedMap - 1]);
|
||||
}
|
||||
|
||||
//----- snake -----
|
||||
@ -51,8 +54,8 @@ void gameInit()
|
||||
snakeInit(); //TODO assign return value to game.snake?
|
||||
|
||||
//--- place initial food ---
|
||||
LOGI("game: placing initial food\n");
|
||||
placeFood();
|
||||
LOGI("game: placed initial food at x=%d, y=%d\n", game.foodX, game.foodY);
|
||||
}
|
||||
|
||||
|
||||
@ -73,11 +76,11 @@ void handlePortals()
|
||||
snakeSetHeadPos(p.targetX, p.targetY);
|
||||
LOGI("game: entered portal i=%d at x=%d, y=%d -> set head to x=%d y=%d\n", i, p.posX, p.posY, p.targetX, p.targetY);
|
||||
//--- play sound ---
|
||||
//playSoundAsync("../sounds/portal1_short.wav"); //too short
|
||||
//playSoundAsync("../sounds/portal2_oscillate.wav"); //too much bass
|
||||
//playSoundAsync("../sounds/space-gun.wav"); //too loud
|
||||
playSoundAsync("../sounds/portal3_in-out.wav");
|
||||
//playSoundAsync("../sounds/portal4_ramp.wav");
|
||||
//playSoundAsync("assets/sounds/portal1_short.wav"); //too short
|
||||
//playSoundAsync("assets/sounds/portal2_oscillate.wav"); //too much bass
|
||||
//playSoundAsync("assets/sounds/space-gun.wav"); //too loud
|
||||
playSoundAsync("assets/sounds/portal3_in-out.wav");
|
||||
//playSoundAsync("assets/sounds/portal4_ramp.wav");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -105,11 +108,19 @@ void runGameCycle()
|
||||
//--- handle collision ---
|
||||
//collision with map object or snake tail
|
||||
if (checkCollides(game.map, game.snake.headX, game.snake.headY) || !snakeIsAlive()){
|
||||
// show leaderboard when collided
|
||||
// TODO consider game.lifesRemaining and reset if still good?
|
||||
LOGI("game: collided with wall or self! => show leaderboard\n");
|
||||
//--- play crash sound ---
|
||||
LOGI("game: collided with wall or self\n");
|
||||
playSound("assets/sounds/crash_rock-cinematic.wav", false);
|
||||
DELAY(200);
|
||||
//--- leaderboard ---
|
||||
//game.gameState = MENU; //TODO add config.collisionEnabled option?
|
||||
showLeaderboard();
|
||||
LOGI("game: saving player score\n");
|
||||
savePlayerScore(config.leaderboardFilename/*(game.snake.length - config.snakeDefaultLength), ttlStorage.userName, config.difficulty, *storedMaps[ttlStorage.userSelectedMap - 1]*/);
|
||||
readTopScores(config.leaderboardFilename);
|
||||
LOGI("game: showing leaderboard\n");
|
||||
game.gameState = MENU;
|
||||
activeMenu = LEADERBOARD;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -124,7 +135,7 @@ void runGameCycle()
|
||||
// NOTE: order of place and grow is relevant, otherwise function in food.c will access invalid memory
|
||||
placeFood();
|
||||
snakeGrow();
|
||||
}
|
||||
}
|
||||
|
||||
//--- update frame ---
|
||||
renderGame();
|
||||
@ -132,4 +143,4 @@ void runGameCycle()
|
||||
printMap(game.map); //render game to console
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
35
src/input.c
35
src/input.c
@ -1,3 +1,5 @@
|
||||
#include <math.h>
|
||||
#include "render.h"
|
||||
#include "input.h"
|
||||
#include "common.h"
|
||||
#include "SDL.h"
|
||||
@ -25,6 +27,8 @@ void handleInput_runningState(SDL_Event event)
|
||||
|
||||
case SDLK_p: // p: pause
|
||||
case SDLK_ESCAPE:
|
||||
case SDLK_SPACE:
|
||||
LOGI("input: pausing game\n");
|
||||
game.gameState = PAUSED;
|
||||
showPauseScreen();
|
||||
break;
|
||||
@ -50,19 +54,19 @@ void handleInput_runningState(SDL_Event event)
|
||||
snakeSetDir(RIGHT);
|
||||
break;
|
||||
|
||||
case SDLK_m: // m: cycle through maps
|
||||
rotateMapNext();
|
||||
break;
|
||||
// case SDLK_m: // m: cycle through maps
|
||||
// rotateMapNext();
|
||||
// break;
|
||||
|
||||
case SDLK_2: // 2: speed up game by increment
|
||||
config.cycleDurationMs -= sqrt(config.cycleDurationMs) + 1;
|
||||
if (config.cycleDurationMs < 20)
|
||||
config.cycleDurationMs = 20;
|
||||
break;
|
||||
// case SDLK_2: // 2: speed up game by increment
|
||||
// config.cycleDurationMs -= sqrt(config.cycleDurationMs) + 1;
|
||||
// if (config.cycleDurationMs < 20)
|
||||
// config.cycleDurationMs = 20;
|
||||
// break;
|
||||
|
||||
case SDLK_1: // 1: slow down game by increment
|
||||
config.cycleDurationMs += 50;
|
||||
break;
|
||||
// case SDLK_1: // 1: slow down game by increment
|
||||
// config.cycleDurationMs += 50;
|
||||
// break;
|
||||
|
||||
default:
|
||||
LOGD("input: key %d is not handled in RUNNING mode\n", event.key.keysym.sym);
|
||||
@ -79,6 +83,7 @@ void handleInput_runningState(SDL_Event event)
|
||||
void processInputEvent()
|
||||
{
|
||||
SDL_Event event;
|
||||
|
||||
// loop through all queued input events that occoured since last run
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
@ -102,12 +107,20 @@ void processInputEvent()
|
||||
handleInput_runningState(event);
|
||||
break;
|
||||
case PAUSED:
|
||||
LOGI("input: resume game from paused state\n");
|
||||
game.gameState = RUNNING;
|
||||
case MENU:
|
||||
// pass key event to menu handle function which updates menu
|
||||
menuHandleInput(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//--- key text input ---
|
||||
// not possible to make this check in the else if query before
|
||||
else if(event.type == SDL_TEXTINPUT && game.gameState == MENU)
|
||||
{
|
||||
menuHandleInput(event);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
69
src/main.cpp
69
src/main.cpp
@ -7,48 +7,44 @@ extern "C"
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
#include "render.h"
|
||||
#include "menu.h"
|
||||
}
|
||||
|
||||
//initialize SDL window
|
||||
//ruft showStartScreen
|
||||
//initialize game
|
||||
//main loop: processInputEvents, runGameCycle
|
||||
//uninitialize SDL
|
||||
|
||||
|
||||
|
||||
//==========================
|
||||
//====== enabled test ======
|
||||
//==========================
|
||||
//uncomment one test at a time to run the corresponding code in main()
|
||||
//#define TEST__FOOD_PLACEMENT
|
||||
//#define TEST__SDL_INPUT
|
||||
#define TEST__GAME_WITH_CONSOLE_OUTPUT
|
||||
|
||||
|
||||
|
||||
//1. initialize SDL window
|
||||
//2. call showStartScreen
|
||||
//3. initialize game
|
||||
//4. main loop: processInputEvents, runGameCycle
|
||||
//5. uninitialize SDL
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
gameInit();
|
||||
{
|
||||
// gameInit(); moved to menu.c
|
||||
|
||||
// Initialisiere SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
printf("SDL konnte nicht initialisiert werden! SDL_Error: %s\n", SDL_GetError());
|
||||
LOGE("SDL: SDL konnte nicht initialisiert werden! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialisiere SDL_ttl, um Text ausgeben zu können
|
||||
if (TTF_Init() == -1) {
|
||||
LOGE("SDL: SDL_ttf konnte nicht initialisiert werden! SDL_Error: %s\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
CreateSDLWindow();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
time_t now;
|
||||
now = GET_TIME_MS(); // Timer startet
|
||||
game.timestampLastCycle = now;
|
||||
int diff;
|
||||
|
||||
while(game.gameState != EXIT) {
|
||||
if(game.gameState == MENU)
|
||||
{
|
||||
manageMenu();
|
||||
}
|
||||
if (game.gameState == RUNNING) {
|
||||
now = GET_TIME_MS(); // Timer startet
|
||||
diff = now-game.timestampLastCycle;
|
||||
@ -62,31 +58,6 @@ int main(int argc, char *argv[])
|
||||
processInputEvent();
|
||||
}
|
||||
|
||||
/* time_t t;
|
||||
|
||||
long long ms = time(NULL) *1000;
|
||||
game.timestampLastCycle = ms;
|
||||
|
||||
printf("timestamp: %lld",game.timestampLastCycle);
|
||||
printf("ms: %lld",ms);
|
||||
|
||||
while(game.gameState != EXIT) {
|
||||
if (game.gameState == RUNNING) {
|
||||
ms = time(NULL) *1000; // Timer startet
|
||||
|
||||
printf("ms2: %lld", ms);
|
||||
|
||||
if (ms - game.timestampLastCycle > config.cycleDurationMs){
|
||||
|
||||
game.timestampLastCycle = ms;
|
||||
runGameCycle();
|
||||
}
|
||||
}
|
||||
DELAY(5); //verhindert maximale Durchlaufgeschwindigkeit der Schleife
|
||||
processInputEvent();
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
DestroySDLWindow();
|
||||
|
@ -130,7 +130,7 @@ void updateBlockSizePx(){
|
||||
//===========================
|
||||
// search and load map by name in storedMaps[] (map.c)
|
||||
// stops program when map not found!
|
||||
void loadMapByName(char *name)
|
||||
void loadMapByName(const char *name)
|
||||
{
|
||||
// loop through all stored maps
|
||||
for (int i = 0; i < storedMapsCount; i++)
|
||||
@ -145,7 +145,7 @@ void loadMapByName(char *name)
|
||||
}
|
||||
}
|
||||
// map not found
|
||||
printf("[FATAL ERROR] map: could not find '%s' in storedMaps!\n", name);
|
||||
LOGE("[FATAL] map: could not find '%s' in storedMaps!\n", name);
|
||||
game.gameState = EXIT;
|
||||
return;
|
||||
}
|
||||
@ -224,7 +224,7 @@ static const map_t map_default = {
|
||||
|
||||
static const map_t map_empty = {
|
||||
.width = 20,
|
||||
.height = 15,
|
||||
.height = 20,
|
||||
.name = "empty",
|
||||
.collisions = {},
|
||||
.collisionCount = 0,
|
||||
|
281
src/menu.c
281
src/menu.c
@ -3,41 +3,292 @@
|
||||
#include "sound.h"
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
//#include <Windows.h>
|
||||
#include <math.h>
|
||||
#include "render.h"
|
||||
|
||||
|
||||
void showStartScreen(){
|
||||
LOGI("menu: showing start-screen\n");
|
||||
game.gameState = RUNNING;
|
||||
|
||||
//define global struct for tllStorage values
|
||||
//default values defined here: (note: gets modified by render.c or menu.c)
|
||||
ttlData_t ttlStorage =
|
||||
{
|
||||
.fontSize_20 = 20, // size of font
|
||||
.fontSize_30 = 30, // size of font
|
||||
.fontSize_200 = 200, // size of font
|
||||
.cycleDuration = 500, // time between show and not showing ENTER in start screen
|
||||
.showEnter = false,
|
||||
.inputStatus = 0,
|
||||
.textInput[0] = '\0'
|
||||
|
||||
|
||||
};
|
||||
|
||||
// Default
|
||||
menus_t activeMenu = START;
|
||||
|
||||
// is called by main function if game.gameState == MENU
|
||||
// choose between the selected menu functions
|
||||
void manageMenu()
|
||||
{
|
||||
switch(activeMenu)
|
||||
{
|
||||
case START:
|
||||
showStartScreen();
|
||||
break;
|
||||
|
||||
case SETTINGS:
|
||||
showSettings();
|
||||
break;
|
||||
|
||||
case INFOSCREEN:
|
||||
showInfoScreen();
|
||||
break;
|
||||
|
||||
case LEADERBOARD:
|
||||
showLeaderboard();
|
||||
break;
|
||||
|
||||
case PAUSE:
|
||||
showPauseScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// shows start screen with blinking ENTER
|
||||
void showStartScreen()
|
||||
|
||||
{
|
||||
LOGD("menu: showing start-screen\n");
|
||||
|
||||
time_t now = GET_TIME_MS();
|
||||
|
||||
// is used to make ENTER blink
|
||||
if(now > (ttlStorage.lastTimeStep + ttlStorage.cycleDuration))
|
||||
{
|
||||
ttlStorage.showEnter = !ttlStorage.showEnter;
|
||||
renderStartMenu();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void showLeaderboard(){
|
||||
LOGI("menu: showing leaderboard\n");
|
||||
|
||||
void showLeaderboard()
|
||||
{
|
||||
LOGD("menu: showing leaderboard\n");
|
||||
|
||||
|
||||
//--- play crash sound ---
|
||||
//play audio file, wait until playback is finished
|
||||
//note: when displaying actual leaderboard, the second parameter should be 'false' to not block the program
|
||||
playSound("../sounds/crash_rock-cinematic.wav", true);
|
||||
DELAY(100);
|
||||
|
||||
//--- quit game ---
|
||||
game.gameState = EXIT;
|
||||
renderLeaderboard();
|
||||
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void showPauseScreen(){
|
||||
LOGI("menu: showing leaderboard\n");
|
||||
void showPauseScreen()
|
||||
{
|
||||
LOGD("menu: showing leaderboard\n");
|
||||
game.gameState = PAUSED;
|
||||
return;
|
||||
}
|
||||
|
||||
void showSettings(){
|
||||
LOGI("menu: showing settings\n");
|
||||
|
||||
void showSettings()
|
||||
{
|
||||
LOGD("menu: showing settings\n");
|
||||
|
||||
|
||||
time_t now = GET_TIME_MS();
|
||||
|
||||
// is used to make ENTER blink
|
||||
if(now > (ttlStorage.lastTimeStep + ttlStorage.cycleDuration))
|
||||
{
|
||||
ttlStorage.showEnter = !ttlStorage.showEnter;
|
||||
renderSettings();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void showInfoScreen()
|
||||
{
|
||||
LOGD("menu: showing info-screen\n");
|
||||
|
||||
time_t now = GET_TIME_MS();
|
||||
|
||||
// is used to make ENTER blink
|
||||
if(now > (ttlStorage.lastTimeStep + ttlStorage.cycleDuration))
|
||||
{
|
||||
ttlStorage.showEnter = !ttlStorage.showEnter;
|
||||
renderInfoScreen();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// handle input over keyboard
|
||||
// delete text at the end of one menu section
|
||||
void menuHandleInput(SDL_Event event){
|
||||
//compare 'handleInput_runningState(SDL_Event event)' in input.c
|
||||
|
||||
int numberOfContents = 0; // count number of entered numbers by the user
|
||||
|
||||
switch(activeMenu)
|
||||
{
|
||||
// start
|
||||
case START:
|
||||
switch (event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_q: // q: quit
|
||||
game.gameState = EXIT;
|
||||
break;
|
||||
|
||||
case SDLK_RETURN: // Enter key
|
||||
activeMenu = SETTINGS;
|
||||
ttlStorage.lastTimeStep = 0;
|
||||
// delete text
|
||||
for (int i = 0; i < MAX_LINES_TFF; ++i)
|
||||
{
|
||||
SDL_DestroyTexture(ttlStorage.textTextures[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// settings
|
||||
case SETTINGS:
|
||||
switch(event.key.keysym.sym)
|
||||
{
|
||||
// case SDLK_q: // q: quit
|
||||
// game.gameState = EXIT;
|
||||
// break;
|
||||
|
||||
case SDLK_F1: // go to info screen
|
||||
activeMenu = INFOSCREEN;
|
||||
ttlStorage.lastTimeStep = 0;
|
||||
// delete text
|
||||
for (int i = 0; i < MAX_LINES_TFF; ++i)
|
||||
{
|
||||
SDL_DestroyTexture(ttlStorage.textTextures[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_RETURN: // confirm user input or start game
|
||||
|
||||
switch(ttlStorage.inputStatus)
|
||||
{
|
||||
case 0: // confirm user name
|
||||
// user must enter at least one letter
|
||||
if(ttlStorage.textInput[0] != '\0')
|
||||
{
|
||||
ttlStorage.inputStatus++;
|
||||
strcpy(ttlStorage.userName, ttlStorage.textInput); // copy textInput to userName
|
||||
memset(ttlStorage.textInput, 0, sizeof(ttlStorage.textInput)); // clear textInput[]
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // confirm difficulty level
|
||||
// count number of entered numbers
|
||||
while (ttlStorage.textInput[numberOfContents] != '\0' && numberOfContents < sizeof(ttlStorage.textInput))
|
||||
{
|
||||
numberOfContents++;
|
||||
}
|
||||
|
||||
// user input must be between 1 and 3
|
||||
if((ttlStorage.textInput[0] > '0') && (ttlStorage.textInput[0] <= '3') && numberOfContents == 1)
|
||||
{
|
||||
ttlStorage.inputStatus++;
|
||||
strcpy(ttlStorage.numbers[0], ttlStorage.textInput); // copy textInput to userDifficultyLevel
|
||||
ttlStorage.userDifficultyLevel = ttlStorage.textInput[0] - '0'; // copy textInput to userDifficultyLevel
|
||||
}
|
||||
memset(ttlStorage.textInput, 0, sizeof(ttlStorage.textInput)); // clear textInput[]
|
||||
break;
|
||||
|
||||
case 2: // confirm map
|
||||
// count number of entered numbers
|
||||
while (ttlStorage.textInput[numberOfContents] != '\0' && numberOfContents < sizeof(ttlStorage.textInput))
|
||||
{
|
||||
numberOfContents++;
|
||||
}
|
||||
|
||||
// user input must be between 1 and 3
|
||||
if((ttlStorage.textInput[0] > '0') && (ttlStorage.textInput[0] <= '3') && numberOfContents == 1)
|
||||
{
|
||||
ttlStorage.inputStatus++;
|
||||
strcpy(ttlStorage.numbers[1], ttlStorage.textInput); // copy textInput to userSelectedMap
|
||||
ttlStorage.userSelectedMap = ttlStorage.textInput[0] - '0'; // copy textInput to userSelectedMap
|
||||
}
|
||||
memset(ttlStorage.textInput, 0, sizeof(ttlStorage.textInput)); // clear textInput[]
|
||||
break;
|
||||
|
||||
case 3: // start game
|
||||
activeMenu = NONE;
|
||||
game.gameState = RUNNING;
|
||||
// delete text
|
||||
for (int i = 0; i < MAX_LINES_TFF; ++i)
|
||||
{
|
||||
SDL_DestroyTexture(ttlStorage.textTextures[i]);
|
||||
}
|
||||
|
||||
// initialize game
|
||||
config.difficulty = ttlStorage.userDifficultyLevel;
|
||||
config.cycleDurationMs = config.cycleDurationMs / sqrt(config.difficulty);
|
||||
gameInit();
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default: // for user inputs
|
||||
// keyboard input
|
||||
if (event.type == SDL_TEXTINPUT)
|
||||
{
|
||||
strcat(ttlStorage.textInput, event.text.text);
|
||||
}
|
||||
// delete single inputs via backspace
|
||||
else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_BACKSPACE && strlen(ttlStorage.textInput) > 0)
|
||||
{
|
||||
ttlStorage.textInput[strlen(ttlStorage.textInput) - 1] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// info screen
|
||||
case INFOSCREEN:
|
||||
switch(event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_q: // q: quit
|
||||
game.gameState = EXIT;
|
||||
break;
|
||||
|
||||
case SDLK_RETURN: // go return to settings
|
||||
activeMenu = SETTINGS;
|
||||
ttlStorage.lastTimeStep = 0;
|
||||
// delete text
|
||||
for (int i = 0; i < MAX_LINES_TFF; ++i)
|
||||
{
|
||||
SDL_DestroyTexture(ttlStorage.textTextures[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LEADERBOARD:
|
||||
switch(event.key.keysym.sym)
|
||||
{
|
||||
case SDLK_q: // q: quit
|
||||
case SDLK_RETURN:
|
||||
game.gameState = EXIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
472
src/render.c
472
src/render.c
@ -4,9 +4,14 @@
|
||||
#include "snake.h"
|
||||
#include "food.h"
|
||||
#include "config.h"
|
||||
#include "menu.h"
|
||||
#include "common.h"
|
||||
#include "files.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define NUM_COLUMNS 4
|
||||
|
||||
|
||||
void renderGame(){
|
||||
SDL_SetRenderDrawColor(game.renderer, 0, 0, 0, 255);
|
||||
@ -113,18 +118,470 @@ void renderGame(){
|
||||
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//-----------------START MENU-----------------------------------
|
||||
//--------------------------------------------------------------
|
||||
void renderStartMenu()
|
||||
{
|
||||
//=========== only first loop ================
|
||||
if(ttlStorage.lastTimeStep == 0)
|
||||
{
|
||||
ttlStorage.ptrFont_200 = TTF_OpenFont("assets/fonts/Quirkus.ttf", ttlStorage.fontSize_200);
|
||||
ttlStorage.ptrFont_30 = TTF_OpenFont("assets/fonts/Quirkus.ttf", ttlStorage.fontSize_30);
|
||||
|
||||
|
||||
SDL_Color textColor1 = {255, 0, 255}; // rosa Text
|
||||
SDL_Color textColor2 = {255, 200, 0}; // oranger Text
|
||||
SDL_Color textColor3 = {0, 255, 0}; // grüner Text
|
||||
SDL_Color textColor4 = {255, 0, 0}; // rot Text
|
||||
SDL_Color textColor5 = {0, 255, 255}; // türkiser Text
|
||||
SDL_Color textColor6 = {255, 255, 255}; // weißer Text
|
||||
|
||||
ttlStorage.textColour[0] = textColor1; // rosa
|
||||
ttlStorage.textColour[1] = textColor2; // orange
|
||||
ttlStorage.textColour[2] = textColor3; // grün
|
||||
ttlStorage.textColour[3] = textColor4; // rot
|
||||
ttlStorage.textColour[4] = textColor5; // türkis
|
||||
ttlStorage.textColour[5] = textColor6; // weiß
|
||||
|
||||
// text in start screen
|
||||
const char* textLines[] = {
|
||||
"Snake++",
|
||||
"In Pixeln bunt, sie schlaengelt flink,",
|
||||
"Durch Mauern, Portale, ohne Blink.",
|
||||
"Starte das Spiel, sei klug und schnell,",
|
||||
"Die Schlange wartet, auf dein Pixel-Ziel!",
|
||||
"-- ENTER --"
|
||||
};
|
||||
|
||||
|
||||
// render game name (SNAKE++) bigger than poem
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_200, textLines[0], ttlStorage.textColour[0]);
|
||||
ttlStorage.textTextures[0] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
|
||||
// render poem
|
||||
for (int i = 1; i < (MAX_LINES_STARTSCREEN - 1); ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, textLines[i], ttlStorage.textColour[i]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
}
|
||||
|
||||
// render ENTER
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, textLines[MAX_LINES_STARTSCREEN-1], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[MAX_LINES_STARTSCREEN-1] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
}
|
||||
|
||||
|
||||
//=========== is always performerd ================
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
int textWidth, textHeight;
|
||||
|
||||
// print game name
|
||||
ttlStorage.textPrintPosition = (config.windowSize) / 9; // print position for game name (SNAKE++)
|
||||
SDL_QueryTexture(ttlStorage.textTextures[0], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect = { (config.windowSize - textWidth) / 2, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[0], NULL, &dstRect);
|
||||
|
||||
// print poem
|
||||
ttlStorage.textPrintPosition = (config.windowSize) / 2.7; // change print position for poem
|
||||
for (int i = 1; i < (MAX_LINES_STARTSCREEN-1); ++i) {
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
|
||||
SDL_Rect dstRect = { (config.windowSize - textWidth) / 2, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
//print ENTER every second cycle
|
||||
if(ttlStorage.showEnter)
|
||||
{
|
||||
ttlStorage.textPrintPosition = (config.windowSize / 1.5); // print position for ENTER
|
||||
SDL_QueryTexture(ttlStorage.textTextures[MAX_LINES_STARTSCREEN-1], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect1 = { (config.windowSize - textWidth) / 2, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[MAX_LINES_STARTSCREEN-1], NULL, &dstRect1);
|
||||
}
|
||||
|
||||
// update screen
|
||||
SDL_RenderPresent(game.renderer);
|
||||
ttlStorage.lastTimeStep = GET_TIME_MS();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//-------------------SETTINGS-----------------------------------
|
||||
//--------------------------------------------------------------
|
||||
void renderSettings()
|
||||
{
|
||||
int textWidth, textHeight; // auxiliary variables for printing
|
||||
|
||||
// text in menu settings
|
||||
static char* textLinesInMenu[MAX_LINES_SETTINGS] = {
|
||||
"Fuer Infos zum Gameplay, sowie einigen Shortcuts druecken Sie bitte F1",
|
||||
" ",
|
||||
" ",
|
||||
"Bitte geben Sie ihren Nickname ein: ",
|
||||
" ",
|
||||
" ",
|
||||
"Bitte geben Sie ein Schwierigkeitslevel ein:",
|
||||
"1 fuer Beginner - 2 fuer Fortgeschrittener - 3 fuer Profi",
|
||||
" ",
|
||||
" ",
|
||||
"Bitte waehlen Sie eine Map:",
|
||||
"1 fuer Standard - 2 fuer Leer - 3 fuer Intermediate",
|
||||
" ",
|
||||
"-- ENTER --"
|
||||
};
|
||||
|
||||
|
||||
//=========== only first loop ================
|
||||
if(ttlStorage.lastTimeStep == 0)
|
||||
{
|
||||
ttlStorage.ptrFont_20 = TTF_OpenFont("assets/fonts/Prototype.ttf", ttlStorage.fontSize_20);
|
||||
SDL_StartTextInput(); // start text input
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========dependent of which text is currently entered(userName, difficultyLevel, userSelectedMap) =========
|
||||
switch(ttlStorage.inputStatus)
|
||||
{
|
||||
//=== no user inputs ===
|
||||
case 0:
|
||||
//--- rendering ---
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLinesInMenu[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
|
||||
}
|
||||
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, ttlStorage.textInput, ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[4] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
//--- printing ---
|
||||
ttlStorage.textPrintPosition = 0; // first print position
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect = { 1, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// === one user input ===
|
||||
case 1:
|
||||
textLinesInMenu[4] = ttlStorage.userName;
|
||||
//--- rendering ---
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLinesInMenu[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
|
||||
}
|
||||
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, ttlStorage.textInput, ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[8] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
//---printing ---
|
||||
ttlStorage.textPrintPosition = 0; // first print position
|
||||
for (int i = 0; i < 9; ++i)
|
||||
{
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect = { 1, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
break;
|
||||
|
||||
//=== two user inputs ===
|
||||
case 2:
|
||||
textLinesInMenu[8] = ttlStorage.numbers[0];
|
||||
//--- rendering ---
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLinesInMenu[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
|
||||
}
|
||||
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, ttlStorage.textInput, ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[12] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
//---printing ---
|
||||
ttlStorage.textPrintPosition = 0; // first print position
|
||||
for (int i = 0; i < 13; ++i)
|
||||
{
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect = { 1, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
break;
|
||||
|
||||
//=== user inputs completely
|
||||
case 3:
|
||||
textLinesInMenu[12] = ttlStorage.numbers[1];
|
||||
//--- rendering ---
|
||||
for (int i = 0; i < (MAX_LINES_SETTINGS - 1); ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLinesInMenu[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
|
||||
}
|
||||
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLinesInMenu[MAX_LINES_SETTINGS - 1], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[MAX_LINES_SETTINGS - 1] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
//---printing ---
|
||||
ttlStorage.textPrintPosition = 0; // first print position
|
||||
for (int i = 0; i < (MAX_LINES_SETTINGS - 1); ++i)
|
||||
{
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect = { 1, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
|
||||
//print ENTER every second cycle
|
||||
if(ttlStorage.showEnter)
|
||||
{
|
||||
ttlStorage.textPrintPosition = (config.windowSize / 1.5); // print position for ENTER
|
||||
SDL_QueryTexture(ttlStorage.textTextures[MAX_LINES_SETTINGS-1], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect1 = { (config.windowSize - textWidth) / 2, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[MAX_LINES_SETTINGS-1], NULL, &dstRect1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// update screen
|
||||
SDL_RenderPresent(game.renderer);
|
||||
ttlStorage.lastTimeStep = GET_TIME_MS();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//-------------------INFO SCREEN--------------------------------
|
||||
//--------------------------------------------------------------
|
||||
void renderInfoScreen()
|
||||
{
|
||||
//=========== only first loop ================
|
||||
if(ttlStorage.lastTimeStep == 0)
|
||||
{
|
||||
// text in start screen
|
||||
const char* textLines[] = {
|
||||
" STEUERUNG: W (nach oben)",
|
||||
" A (nach links)",
|
||||
" S (nach unten)",
|
||||
" D (nach rechts) ",
|
||||
" oder: Pfeiltasten ",
|
||||
" ",
|
||||
" ",
|
||||
" PAUSE: p",
|
||||
" oder: Leertaste",
|
||||
" ",
|
||||
" ",
|
||||
"SPIEL VERLASSEN: q ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
"By Jonas Schoenberger, Johannes Graf und Julia Steinberger",
|
||||
"-- ENTER --"
|
||||
};
|
||||
|
||||
|
||||
// render setting screen
|
||||
for (int i = 0; i < MAX_LINES_INFOSCREEN; ++i)
|
||||
{
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_20, textLines[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTextures[i] = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=========== is always performerd ================
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
int textWidth, textHeight;
|
||||
|
||||
// print settings
|
||||
ttlStorage.textPrintPosition = 0; // first print position
|
||||
for (int i = 0; i < (MAX_LINES_INFOSCREEN - 1); ++i) {
|
||||
|
||||
SDL_QueryTexture(ttlStorage.textTextures[i], NULL, NULL, &textWidth, &textHeight);
|
||||
|
||||
SDL_Rect dstRect = { 1, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[i], NULL, &dstRect);
|
||||
ttlStorage.textPrintPosition += textHeight; // increase print position
|
||||
}
|
||||
|
||||
//print ENTER every second cycle
|
||||
if(ttlStorage.showEnter)
|
||||
{
|
||||
ttlStorage.textPrintPosition = (config.windowSize / 1.5); // print position for ENTER
|
||||
SDL_QueryTexture(ttlStorage.textTextures[MAX_LINES_INFOSCREEN-1], NULL, NULL, &textWidth, &textHeight);
|
||||
SDL_Rect dstRect1 = { (config.windowSize - textWidth) / 2, ttlStorage.textPrintPosition, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTextures[MAX_LINES_INFOSCREEN-1], NULL, &dstRect1);
|
||||
}
|
||||
|
||||
|
||||
// update screen
|
||||
SDL_RenderPresent(game.renderer);
|
||||
ttlStorage.lastTimeStep = GET_TIME_MS();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//-------------------RENDER LEADERBOARD-------------------------
|
||||
//--------------------------------------------------------------
|
||||
void renderLeaderboard()
|
||||
{
|
||||
|
||||
char* menuDescription[] ={"LEADERBOARD"};
|
||||
char* columnDescriptions[NUM_COLUMNS] =
|
||||
{
|
||||
"Score",
|
||||
"Spieler",
|
||||
"Schwierigkeitslevel",
|
||||
"Map"
|
||||
};
|
||||
|
||||
SDL_SetRenderDrawColor(game.renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(game.renderer);
|
||||
|
||||
int rowHeight = config.windowSize / (MAX_PRINTED_SCORES * 10); // Höhe einer Zeile
|
||||
int columnWidth = config.windowSize / NUM_COLUMNS; // Breite einer Spalte
|
||||
int textWidth, textHeight;
|
||||
|
||||
|
||||
// rendering 'LEADERBOARD'
|
||||
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, menuDescription[0], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTexture = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
|
||||
SDL_QueryTexture(ttlStorage.textTexture, NULL, NULL, &textWidth, &textHeight);
|
||||
|
||||
SDL_Rect dstRect = {1 , 0, textWidth, textHeight };
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTexture, NULL, &dstRect);
|
||||
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_DestroyTexture(ttlStorage.textTexture);
|
||||
|
||||
|
||||
// rendering columns description
|
||||
for (int i = 0; i < NUM_COLUMNS; ++i) {
|
||||
ttlStorage.textSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, columnDescriptions[i], ttlStorage.textColour[5]);
|
||||
ttlStorage.textTexture = SDL_CreateTextureFromSurface(game.renderer, ttlStorage.textSurface);
|
||||
|
||||
SDL_Rect textRect;
|
||||
textRect.x = i * columnWidth + (columnWidth - ttlStorage.textSurface->w) / 2 - 30; // Zentrieren
|
||||
textRect.y = 50; // Y-Koordinate für die Spaltenbeschreibung
|
||||
textRect.w = ttlStorage.textSurface->w;
|
||||
textRect.h = ttlStorage.textSurface->h;
|
||||
|
||||
SDL_RenderCopy(game.renderer, ttlStorage.textTexture, NULL, &textRect);
|
||||
|
||||
SDL_FreeSurface(ttlStorage.textSurface);
|
||||
SDL_DestroyTexture(ttlStorage.textTexture);
|
||||
}
|
||||
|
||||
// rendering score data
|
||||
int maxCycles = (recordsInFile < MAX_PRINTED_SCORES) ? recordsInFile : MAX_PRINTED_SCORES;
|
||||
for (int i = 0; i < maxCycles; ++i) {
|
||||
char playerName[50]; // temporary buffer for text
|
||||
char map[50]; // temporary buffer for text
|
||||
strcpy(playerName, topScores[i].playerName);
|
||||
strcpy(map, topScores[i].map);
|
||||
|
||||
// player name
|
||||
SDL_Surface* playerNameSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, playerName, ttlStorage.textColour[5]);
|
||||
SDL_Texture* playerNameTexture = SDL_CreateTextureFromSurface(game.renderer, playerNameSurface);
|
||||
|
||||
// map name
|
||||
SDL_Surface* mapSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, map, ttlStorage.textColour[5]);
|
||||
SDL_Texture* mapTexture = SDL_CreateTextureFromSurface(game.renderer, mapSurface);
|
||||
|
||||
|
||||
// score
|
||||
SDL_Surface *scoreSurface = NULL;
|
||||
char numberScore[30]; // buffer for number 'score'
|
||||
snprintf(numberScore, sizeof(numberScore), "%d", topScores[i].score);
|
||||
scoreSurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, numberScore, ttlStorage.textColour[5]);
|
||||
SDL_Texture *numberTexture1 = SDL_CreateTextureFromSurface(game.renderer, scoreSurface);
|
||||
|
||||
// difficulty
|
||||
SDL_Surface *difficultySurface = NULL;
|
||||
char numberDifficulty[30]; // buffer for number 'difficulty'
|
||||
snprintf(numberDifficulty, sizeof(numberDifficulty), "%d", topScores[i].difficulty);
|
||||
difficultySurface = TTF_RenderText_Solid(ttlStorage.ptrFont_30, numberDifficulty, ttlStorage.textColour[5]);
|
||||
SDL_Texture *numberTexture2 = SDL_CreateTextureFromSurface(game.renderer, difficultySurface);
|
||||
|
||||
int numberS = 70; //x distance
|
||||
int textPlayer = 220; //x distance
|
||||
int numberD = 470; //x distance
|
||||
int textMap = 620; //x distance
|
||||
int y = 90 + i*30; //y distance
|
||||
|
||||
SDL_Rect scoreRect = {numberS, y, scoreSurface->w, scoreSurface->h};
|
||||
SDL_Rect playerNameRect = {textPlayer, y, playerNameSurface->w, playerNameSurface->h };
|
||||
SDL_Rect difficultyRect = {numberD, y, difficultySurface->w, difficultySurface->h};
|
||||
SDL_Rect mapNameRect = {textMap, y, mapSurface->w, mapSurface->h };
|
||||
|
||||
SDL_RenderCopy(game.renderer, numberTexture1, NULL, &scoreRect);
|
||||
SDL_RenderCopy(game.renderer, playerNameTexture, NULL, &playerNameRect);
|
||||
SDL_RenderCopy(game.renderer, numberTexture2, NULL, &difficultyRect);
|
||||
SDL_RenderCopy(game.renderer, mapTexture, NULL, &mapNameRect);
|
||||
|
||||
SDL_FreeSurface(playerNameSurface);
|
||||
SDL_FreeSurface(mapSurface);
|
||||
SDL_FreeSurface(scoreSurface);
|
||||
SDL_FreeSurface(difficultySurface);
|
||||
|
||||
SDL_DestroyTexture(playerNameTexture);
|
||||
SDL_DestroyTexture(mapTexture);
|
||||
SDL_DestroyTexture(numberTexture1);
|
||||
SDL_DestroyTexture(numberTexture2);
|
||||
}
|
||||
SDL_RenderPresent(game.renderer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int CreateSDLWindow(){
|
||||
// Erstelle ein SDL-Fenster
|
||||
game.window = SDL_CreateWindow("Snake", 350, 50, config.windowSize, config.windowSize, SDL_WINDOW_OPENGL);
|
||||
if (game.window == NULL) {
|
||||
printf("Fenster konnte nicht erstellt werden! SDL_Error: %s\n", SDL_GetError());
|
||||
LOGE("SDL: Fenster konnte nicht erstellt werden! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
game.renderer = SDL_CreateRenderer(game.window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (game.renderer == NULL) {
|
||||
printf("Renderer konnte nicht erstellt werden! SDL_Error: %s\n", SDL_GetError());
|
||||
LOGE("SDL: Renderer konnte nicht erstellt werden! SDL_Error: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -132,6 +589,15 @@ int CreateSDLWindow(){
|
||||
|
||||
void DestroySDLWindow(){
|
||||
// Zerstöre das Fenster und beende SDL
|
||||
|
||||
TTF_CloseFont(ttlStorage.ptrFont_20);
|
||||
TTF_CloseFont(ttlStorage.ptrFont_30);
|
||||
TTF_CloseFont(ttlStorage.ptrFont_200);
|
||||
|
||||
SDL_DestroyRenderer(game.renderer);
|
||||
SDL_DestroyWindow(game.window);
|
||||
}
|
||||
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
|
14
src/snake.c
14
src/snake.c
@ -1,6 +1,7 @@
|
||||
#include "snake.h"
|
||||
#include "game.h" //for access to global 'game' struct
|
||||
|
||||
bool snakeSetDirectionIsAllowed = true;
|
||||
|
||||
void snakeInit()
|
||||
{
|
||||
@ -36,14 +37,16 @@ void snakeGrow()
|
||||
game.snake.length++;
|
||||
// tail part is attached left of last tail part
|
||||
// maybe problem while rendering --> MUST BE SOLVED THEN
|
||||
game.snake.tail[game.snake.length][0] = game.snake.tail[game.snake.length - 1][0] - 1;
|
||||
game.snake.tail[game.snake.length][1] = game.snake.tail[game.snake.length - 1][1];
|
||||
game.snake.tail[game.snake.length - 1][0] = game.snake.tail[game.snake.length - 2][0];
|
||||
game.snake.tail[game.snake.length - 1][1] = game.snake.tail[game.snake.length - 2][1];
|
||||
return;
|
||||
}
|
||||
|
||||
void snakeMove()
|
||||
{
|
||||
int i = game.snake.length - 1; // counter for snake moving
|
||||
|
||||
snakeSetDirectionIsAllowed = true; // direction can be changed now
|
||||
|
||||
// update head position automatically
|
||||
snakeUpdateHeadPos();
|
||||
@ -63,6 +66,12 @@ void snakeMove()
|
||||
|
||||
void snakeSetDir(snakeDirection_t dir)
|
||||
{
|
||||
// if direction mustn changed
|
||||
if(!snakeSetDirectionIsAllowed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// check, if snake should be move in opposite direction -> new direction stays old direction
|
||||
switch(dir)
|
||||
{
|
||||
@ -92,6 +101,7 @@ void snakeSetDir(snakeDirection_t dir)
|
||||
|
||||
}
|
||||
game.snake.direction = dir;
|
||||
snakeSetDirectionIsAllowed = false; // direction cannot updated until next snakeMove
|
||||
return;
|
||||
}
|
||||
|
||||
|
11
src/sound.c
11
src/sound.c
@ -27,7 +27,7 @@ int initAudio()
|
||||
//--- initialize SDL audio ---
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
printf("SDL: could not init audio!\n");
|
||||
LOGE("sound: SDL - could not init audio!\n");
|
||||
return 1;
|
||||
}
|
||||
audioIsInitialized = true;
|
||||
@ -66,7 +66,8 @@ int playSound(const char *filePath, bool wait)
|
||||
//--- load file ---
|
||||
if (SDL_LoadWAV(filePath, &wavSpec, &wavBuffer, &wavLength) == NULL)
|
||||
{
|
||||
printf("sound: file '%s' could not be loaded E:'%s'\n", filePath, SDL_GetError());
|
||||
LOGE("sound: file '%s' could not be loaded E:'%s'\n", filePath, SDL_GetError());
|
||||
pthread_mutex_unlock(&mutexSoundPlaying);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -75,7 +76,7 @@ int playSound(const char *filePath, bool wait)
|
||||
deviceExists = true;
|
||||
SDL_QueueAudio(deviceId, wavBuffer, wavLength);
|
||||
SDL_PauseAudioDevice(deviceId, 0);
|
||||
LOGI("sound: playing file '%s'\n", filePath);
|
||||
LOGD("sound: playing file '%s'\n", filePath);
|
||||
|
||||
//--- wait until playback is finished ---
|
||||
while (SDL_GetQueuedAudioSize(deviceId) > 0)
|
||||
@ -123,14 +124,14 @@ int playSoundAsync(const char *filePath) {
|
||||
//--- allocate memory for filePath ---
|
||||
char *filePathCopy = strdup(filePath);
|
||||
if (filePathCopy == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
LOGE("sound: Memory allocation failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//--- create new thread ---
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, playSoundThread, filePathCopy) != 0) {
|
||||
fprintf(stderr, "Failed to create thread\n");
|
||||
LOGE("sound: Failed to create thread\n");
|
||||
free(filePathCopy);
|
||||
return 1;
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user