Remove duplicates

This commit is contained in:
Andras Schmelczer 2026-08-09 20:01:07 +01:00
commit 6e4f0428dc
186 changed files with 0 additions and 23208 deletions

Binary file not shown.

View file

@ -1,139 +0,0 @@
#include <time.h>
#include <string.h>
#include "modules/global.h"
#include "modules/camera.h"
#include "modules/color.h"
#include "modules/floaters.h"
#include "modules/particles.h"
#include "modules/world.h"
#include "modules/map.h"
#include "modules/menu/menuDraw.h"
#include "modules/menu/menuFSM.h"
/**\mainpage Leírás
* \section Röviden
* Egy könnyed, egyszerűen megismerhető, mégis jutalmazó játékélményt nyújtó programot szerettem volna.
* \section Hosszabban
* A játéktér 3D-s kockákból álló, dinamikus méretű, és véletlenszerűen generált.
* A játékosnak a generált akadályok tetején található jutalmakat kell gyűjtenie.
* Ezt természetesen repülő ellenségek nehezítik, akik meg is keresik a játékost.
* Nem csak az ellenségeket, hanem a pályát is lehet rombolni a karakter által lőtt lövedékekkel.
* A játékost segíti még az időlassítás lehetősége is. Persze egyik sincs ingyen, mindkét képesség
* használata a pontszám csökkenésével jár. A pontszám nem kerülhet nulla alá.
* A játék aktuális állása fájlba menthető és onnan be is tölthető.
* Ezen felül a program egy toplistát is tartalmaz a legjobb eredményekről.
* \section Érdekességek
* - Nem triviális a térbeli megjelenítés, a pálya generálás illetve a mentés tömörítésének eljárása.
* Ezeket bővebben kifejtve a megfelelő modulok függvényeinél lehet megtalálni. Röviden viszont itt is megmagyarázom őket.
* A 3D-s hatást egy kamera fizikai modelljével érem el. Általában nem ez a leggyorsabb megoldás, viszont a játék egyszerű grafikájából
* kifolyólag (kizárólag téglatestek vannak benne) itt nagyon jól beválik. Persze rengeteg optimalizálás kell hozzá, hogy hardveres gyorsítás nélkül,
* kizárólag a processzoron futva is élvezhetően gyors legyen. Ilyen optimalizálások a nem egyesével történő rajzolás; ha hasonló tulajdonságú kockák vannak
* egymás alatt, akkor azokat egy magas oszlopként rajzolja. Csak azokat az oldalakat rajzolja, amik valóban látszanak. Illetve értelemszerűen,
* csak azokat a kockákat rajzolja, amelyikek látszanak.
* Ezen felül a rajzolás sorrendje is igencsak érdekes kihívást tár elénk, de erről már csak a camera.c modulban kerül szó.
* - A pályagenerálás nem túl bonyolult, két részből áll. Előszőr a földszintet építi, ahol random magasságú oszlopok vannak,
* csak az van megszabva, hogy mekkora lehet két szomszédos oszlop közt a legnagyobb magasságkülönbség. A felső szint már érdekesebb. Ott vannak a platformok.
* A pályát x irányban kisebb egységekre osztjuk, minden egységben egy "oszlopot" építünk. Az oszlop alatt olyan platformokat értünk, amik közt a távolság olyan, hogy
* a játék aktuális fizikájával mindig legalább egy irányban tovább lehet haladni róluk. Igazából ennyi az egész.
* - A tömörítés a három közül a legprimitívebb megoldás, viszont nincs szükségünk bonyolultabbra, ez is elégséges. Mivel a pályák nagyrésze igen repetitív, ezért
* a blockok is gyakran ismétlődnek, ezt az ismétlődést a: "(ismétlődés száma)block típusa" formában rögzítjük. Erről bővebben a save.c modulban. Fontos megjegyezni, hogy
* a játék nincsen felkészítve a fájljaiba való külső beavatkozásra, tehát ezt igyekezzünk elkerülni.
* \section Modulok
* - camera.c: minden, ami 3d-s megjelenítéssel kapcsolatos
* - map.c: minden, ami a pálya generálásával és ütközésteszteléssel kapcsolatos
* - floaters.c: ellenségek, lövedékek
* - particles.c: hóeséssel kapcsolatos függvények és tömbök
* - player.c: nagyrészt a player mozgásáért felelős
* - saves.c: mentés, betöltés, mentés fájlok kezelése
* - world.c: meghívja a világ létrehozásához szükséges függvényeket
* - color.c: a pálya színei itt készülnek (HSL -> RGB átalakítás is van itt)
* - menuFSM.c: a játékban és a menüben történő inputra reagál elsősorban, itt zajlanak az állapotátmenetek
* - menuDraw.c: a menüt rajzolja le, illetve meghívja a játéktér rajzolását is, ha szükséges
* - unicodePrint.c: a név sugallja, az unicode szövegek kezelése és megjelenítése a feladata
* - auxSDL.c: a megfelelő sdl-es függvények meghívása, illetve az egér interakciók kezelése
* - auxFunctions.c: gyakran használ inline függvények
* \section Kompatibilitás
* A program lefordításához szükséges az SDL.h, SDL_ttf.h, illetve SDL_gfxPrimitives.h könyvtárak. Nincs tesztelve, viszont valószínűleg
* az SDL 1.2 által platformokon működőképes az alkalmazás. A játék fix FPS-sel fut, ez jelenleg 50-re van beállítva, így átlagos teljesítményű gépeken is
* el kell futnia, viszont ehhez szükség van egy átlagos képességű gépre, szóval ez egy lényeges rendszerkövetelmény.
* A játék működéséhez szükségesek a megfelelő ttf-fájlok.
* \section Készítette
* Schmelczer András, 2017. 12. 03.
*/
/**
* \file main.c
*/
bool isLMBDown = false, isRMBDown = false, prematureClick;
int main(int argc, char *argv[]) {
srand(time(NULL));
SDL_Cursor* cursor = openWindow();
SDL_Event event;
SDL_TimerID timerID;
timerID = SDL_AddTimer((int)(1000.0 / FPS), timer, NULL);
keys = SDL_GetKeyState(NULL);
//setlocale(0, "");
Uint32 backgroundColor = SDL_MapRGB(screen -> format, 40, 0, 0);
createMenuWorld();
while (getGameState() != quit) {
SDL_WaitEvent(&event);
switch(event.type) {
case SDL_USEREVENT:
SDL_FillRect(screen, NULL, backgroundColor);
doStuff(isLMBDown, isRMBDown, prematureClick);
SDL_UpdateRect(screen, 0, 0, 0, 0);
break;
case SDL_MOUSEBUTTONDOWN:
if(event.button.button == SDL_BUTTON_LEFT) handleClicks(&prematureClick);
if(event.button.button == SDL_BUTTON_RIGHT) isRMBDown = true;
if(event.button.button == SDL_BUTTON_LEFT) isLMBDown = true;
break;
case SDL_MOUSEBUTTONUP:
if(event.button.button == SDL_BUTTON_RIGHT) isRMBDown = false;
if(event.button.button == SDL_BUTTON_LEFT) isLMBDown = prematureClick = false;
break;
case SDL_MOUSEMOTION:
setMousePosition(event.motion.x, event.motion.y);
break;
case SDL_KEYDOWN:
if(strcmp(SDL_GetKeyName(event.key.keysym.unicode), "escape") == 0) whatToDoOnEscape();
else processTyping(event);
break;
}
}
//Felszabadítás
freeFloaterList();
freeColorMap();
freeMap();
freeRenderMap();
freeParticleList();
SDL_FreeSurface(text);
freeFonts();
SDL_RemoveTimer(timerID);
SDL_FreeCursor(cursor);
SDL_Quit();
return 0;
}

View file

@ -1,22 +0,0 @@
#include "auxFunctions.h"
extern inline float randomWithinBorders(float base, float variance);
extern inline int randomIntWithinBorders(int base, int variance);
extern inline int randomPlusOrMinus();
extern inline int larger(int a, int b);
extern inline int smaller(int a, int b);
extern inline float largerF(float a, float b);
extern inline float smallerF(float a, float b);
extern inline void putInsideInterval(int min, int* a, int max);
extern inline void putInsideIntervalF(float min, float* a, float max);
extern inline float dist(int a, int b);
extern inline float fDist(float a, float b);
extern inline float square(float a);

View file

@ -1,58 +0,0 @@
#ifndef AUXFUNCTIONS_H_INCLUDED
#define AUXFUNCTIONS_H_INCLUDED
#include "../global.h"
inline float randomWithinBorders(float base, float variance) {
int intVariance = (int)(1e4 * variance);
return (1e4 * base + (rand() % (2 * intVariance + 1) - intVariance)) / 1e4;
}
inline int randomIntWithinBorders(int base, int variance) {
return base + (rand() % (2 * variance + 1) - variance);
}
inline int randomPlusOrMinus() {
return rand() % 2 ? 1 : -1;
}
inline int larger(int a, int b) {
return a > b ? a : b;
}
inline int smaller(int a, int b) {
return a < b ? a : b;
}
inline float largerF(float a, float b) {
return a > b ? a : b;
}
inline float smallerF(float a, float b) {
return a < b ? a : b;
}
inline void putInsideInterval(int min, int* a, int max) {
*a = largerF(min, smallerF(*a, max));
}
inline void putInsideIntervalF(float min, float* a, float max) {
*a = largerF(min, smallerF(*a, max));
}
inline float dist(int a, int b) {
return sqrt(a*a + b*b);
}
inline float fDist(float a, float b) {
return sqrt(a*a + b*b);
}
inline float square(float a) {
return a*a;
}
#endif // AUXFUNCTIONS_H_INCLUDED

View file

@ -1,73 +0,0 @@
#include "auxSDL.h"
mousePos mouse; /**< Az egér koordinátáit tárolja, így egyszerűbb, mint közvetlen az eventek figyelése. */
/** \brief https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlcreatecursor.html
* Hogy erre miért van szükség, az egy nagyon jó kérdés. Ha ehelyett a "SDL_ShowCursor(SDL_DISABLE);"-t
* használnám az egér random ugrálna néha néha, így viszont ezt nem teszi.
*/
static SDL_Cursor* createCursor() {
Uint8 data[4*32] = {0};
Uint8 mask[4*32] = {0};
return SDL_CreateCursor(data, mask, 32, 32, 0, 0);
}
/** \brief Létrehozza a játék futtatásához szükséges környezetet.
* Megnézi az elérhető legnagyobb felbontást, azzal a felbontással létrehoz egy ablakot, illetve
* meghív pár, az inicializáláshoz szükséges függvényt.
* \return kurzor amit fel kell később szabadítanunk
*/
SDL_Cursor* openWindow() {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_Rect** screenSizes = SDL_ListModes(NULL, SDL_FULLSCREEN);
screen = SDL_SetVideoMode(screenSizes[0] -> w, screenSizes[0] -> h, 0, SDL_FULLSCREEN);
if (!screen) {
fprintf(stderr, "Nem sikerult megnyitni az ablakot!\n");
exit(1);
}
SDL_WM_SetCaption("Platform játék", "Schmelczer András: Platform játék");
SDL_Cursor* cursor = createCursor();
SDL_SetCursor(cursor);
openFonts();
SDL_WM_GrabInput(SDL_GRAB_FULLSCREEN);
return cursor;
}
///A játék időzítéséhez szükséges.
Uint32 timer(Uint32 ms, void* param) {
SDL_Event event;
event.type = SDL_USEREVENT;
SDL_PushEvent(&event);
return ms;
}
///Frissití az egér pozicióját.
void setMousePosition(Uint16 x, Uint16 y) {
mouse.x = x;
mouse.y = y;
}
///Visszaadja az egérpoziciót.
mousePos getMousePosition() {
return mouse;
}
///Lerajzolja a képernyőre az egeret, erre azért van szükség, mert az SDL csak fekete-fehér egérmutatót kezel.
void drawMouse(bool isBW) {
Sint16 x1 = mouse.x - mouseSize / 2;
Sint16 x2 = mouse.x + mouseSize / 2;
Sint16 y1 = mouse.y - mouseSize / 2;
Sint16 y2 = mouse.y + mouseSize / 2;
boxColor(screen, x1, y1, x2, y2, isBW ? 0x7F7F7FFF : 0xFFFF00FF);
}
///Egy téglalap felett van-e az egérmutató közepe?
bool isMouseOverRect(int xCenter, int yCenter, int xSize, int ySize) {
return
screen -> w / 100.0 * (xCenter - xSize) < mouse.x && mouse.x < screen -> w / 100.0 * (xCenter + xSize) &&
screen -> h / 100.0 * (yCenter - ySize) < mouse.y && mouse.y < screen -> h / 100.0 * (yCenter + ySize);
}

View file

@ -1,20 +0,0 @@
#ifndef AUXSDL_H_INCLUDED
#define AUXSDL_H_INCLUDED
#include "../global.h"
#include "unicodePrint.h"
#define mouseSize 20 /**< Az egérmutató négyzetnek az oldalhosszusága. */
typedef struct mousePos {
Uint16 x, y;
} mousePos;
SDL_Cursor* openWindow();
Uint32 timer(Uint32 ms, void* param);
void setMousePosition(Uint16 x, Uint16 y);
mousePos getMousePosition();
void drawMouse(bool isBW);
bool isMouseOverRect(int xCenter, int yCenter, int xSize, int ySize);
#endif // AUXSDL_H_INCLUDED

View file

@ -1,84 +0,0 @@
#include "unicodePrint.h"
///Indításnál betölti a szükséges betűtípusokat.
void openFonts() {
SDL_EnableUNICODE(1);
TTF_Init();
fontSmaller = TTF_OpenFont("resources/Roboto-Thin.ttf", 40);
fontSmall = TTF_OpenFont("resources/Roboto-Thin.ttf", 48);
fontSmallBold = TTF_OpenFont("resources/Roboto-Light.ttf", 53);
fontLarge = TTF_OpenFont("resources/Roboto-Thin.ttf", 120);
}
///Ha már nem kellenek, felszabadítja a betűtípusokat.
void freeFonts() {
TTF_CloseFont(fontSmaller);
TTF_CloseFont(fontSmall);
TTF_CloseFont(fontSmallBold);
TTF_CloseFont(fontLarge);
}
///Itt tárolom a használható karaktereket. Eldönti, hogy a bemenet köztük van-e.
bool isValidChar(Uint16 chr) {
if(('A' <= chr && chr <= 'Z') || ('a' <= chr && chr <= 'z') || ('0' <= chr && chr <= '9')) return true;
Uint16 validChars[] = L"öüóőúéáűíÖÜÓŐÚÉÁŰÍ|€ß$Łł¤<>#&@{}Đ[]đ+!%=×)(.- ";
int i;
for(i = 0; validChars[i] != 0; i++) if(chr == validChars[i])
return true;
return false;
}
///Az egyszerűbb kezelhetőség miatt kell. Visszadja az enumhoz tartozó ttf-et.
TTF_Font* selectFont(fontType fontType) {
switch(fontType) {
case tiny:
return fontSmaller;
case small:
return fontSmall;
case smallBold:
return fontSmallBold;
case large:
default:
return fontLarge;
}
}
///A százalékot a képernyő mérete alapján átváltja pixelekre.
Sint16 percentageToPx(int percentage, bool isWidth) {
return (int)((float)percentage/100 * (isWidth ? screen -> w: screen -> h));
}
/** \brief Uint16-ból álló stringet összefűz.
* A két paramétert egy harmadik dinamikusan foglalt stringben fűzi össze.
* \param string eleje
* \param string vége
* \return string konkatenált
*/
Uint16* concatUnicode(const Uint16 a[], const Uint16 b[]) {
int aLength, bLength;
for(aLength = 0; a[aLength] != 0; aLength++)
;
for(bLength = 0; b[bLength] != 0; bLength++)
;
Uint16* newStr = (Uint16*) malloc((aLength + bLength - 1 + 100) * sizeof(Uint16));
int i = 0, ai, bi;
for(ai = 0; ai < aLength; ai++)
newStr[i++] = a[ai];
for(bi = 0; bi < bLength; bi++)
newStr[i++] = b[bi];
newStr[i] = 0;
return newStr;
}
///A megjelenítendő szöveg közepét kell megadni x,y-ként. A szöveget Uint16*-ban, a betűtipust pedig a betűtipus enumból. Ugyan mindent x = 50-nel hívunk meg, ez később változhat, ezért hagyom meg paraméternek.
void unicodePrintCentered(Uint16 textToWrite[], int x, int y, fontType fontType) {
TTF_Font* currentFont = selectFont(fontType);
SDL_FreeSurface(text);
text = TTF_RenderUNICODE_Blended(currentFont, textToWrite, (SDL_Color) {255, 255, 255});
SDL_BlitSurface(text, NULL, screen, &(SDL_Rect){percentageToPx(x, true) - text -> w / 2, percentageToPx(y, false) - text -> h / 2, 0, 0});
}

View file

@ -1,16 +0,0 @@
#ifndef UNICODEPRINT_H_INCLUDED
#define UNICODEPRINT_H_INCLUDED
#include "../global.h"
typedef enum fontType {
tiny, small, smallBold, large
} fontType;
void openFonts();
void freeFonts();
bool isValidChar(Uint16 chr);
Uint16* concatUnicode(const Uint16 a[], const Uint16 b[]);
void unicodePrintCentered(Uint16 textToWrite[], int x, int y, fontType fontType);
#endif // UNICODEPRINT_H_INCLUDED

View file

@ -1,326 +0,0 @@
#include "camera.h"
#define focalLength 0.05 /**< gyújtótáv [m] */
#define sensorSize 0.07 /**< szenzor hosszabb oldalának a fele [m] */
#define renderObjectsPerCube 10 /**< max( (projectile + player + enemy) / m^3), erről később bővebben. */
vector3D cameraPosition; /**< A kamera térbeli helyvektora. */
float magnification, columns, rows; /**< Ennyi látszik a pályából [m]. */
bool watchBounds = false; /**< Feldarabolós rajzolás van-e? */
float leftBound, rightBound, upperBound, lowerBound; /**< Darabolás határai. */
int leftMostBound, rightMostBound, topBound, bottomBound;
renderObject*** renderMap = NULL; /**< Darabolás mátrixa. */
///A feldarabolós rajzoláshoz szükséges mátrixot szabadítja fel.
void freeRenderMap() {
if(renderMap != NULL) {
free(renderMap[0][0]);
free(renderMap[0]);
free(renderMap);
}
}
///A feldarabolós rajzoláshoz szükséges mátrixot mallocolja.
static void initRenderMap() {
renderMap = (renderObject***) malloc(sizeof(renderObject**));
renderMap[0] = (renderObject**) malloc(sizeof(renderObject*));
renderMap[0][0] = (renderObject*) malloc(sizeof(renderObject));
}
/** \brief Kiszámolja a kamera poziciója és a képernyő mérete alapján, hogy mennyi látszik a pályából.
*
* Elsősorban ezeket a változókat változtatja:
* columns, rows = képernyőn megjelenő oszlopok és sorok száma,
* magnification = megmondja egy méter a kamera "szenzorán" hány pixelnek felel meg (jó soknak)
*
* Hasonló háromszögek alapján kiszámolja mennyi látszik a pályából:
* sensor size
* |
* V
* ----
* \ |
* \ | <- focalLength
* \|
* |
* |\
* | \
* Z -> | \
* | \
* | \
* -------------
* columns
*
* Ezek alapján reallocolja a darabolós mátrixot.
*
*/
static void calculateRowsAndColumns() {
magnification = (float) screen -> w / 2 / sensorSize;
columns = 2 * cameraPosition.z * sensorSize / focalLength;
//hogy a négyzet négyzet is maradjon
rows = (float)screen -> h / screen -> w * columns;
if(renderMap == NULL) initRenderMap();
int xSize = (int)columns + 4; //a nagyobb testek átlóghatnak a képernyő határán
int ySize = (int)rows + 4;
int iSize = renderObjectsPerCube;
renderObject* innerTmp = (renderObject*) realloc(renderMap[0][0], xSize * ySize * iSize * sizeof(renderObject));
renderObject** outerTmp = (renderObject**) realloc(renderMap[0], xSize * ySize * sizeof(renderObject*));
renderMap = (renderObject***) realloc(renderMap, xSize * sizeof(renderObject**));
renderMap[0] = outerTmp;
int x;
for(x = 1; x < xSize; x++)
renderMap[x] = renderMap[0] + x * ySize;
renderMap[0][0] = innerTmp;
renderObject empty;
empty.doesExist = false;
int y, i = 0;
int j;
for(x = 0; x < xSize; x++)
for(y = 0; y < ySize; y++) {
renderMap[x][y] = renderMap[0][0] + i++ * iSize;
for(j = 0; j < iSize; j++) renderMap[x][y][j] = empty;
}
}
///A játékost követi a kamerával, nem hagyja, hogy a pálya alá lásson.
void followPlayerWithCamera() {
renderObject player = getPlayerRenderObject();
cameraPosition.x = (2 * player.position.x + player.size.x) / 2;
cameraPosition.y = (player.position.y < rows / 2 ? rows / 2 : player.position.y) + 1;
}
///A menüben használt kamerát állítja be, visszaadja a columns-t és rows-t is.
void menuCamera(int* getColumns, int* getRows) {
cameraPosition.z = cameraDeltaZDistanceStart;
calculateRowsAndColumns();
*getColumns = columns;
*getRows = rows;
cameraPosition.x = columns / 2;
cameraPosition.y = rows / 2;
}
///Visszaadja, hogy hány oszlop látszik a pályából.
int getColumns() {
return columns;
}
/** \brief A kamera dollyzását végzi, a kamerát mozgatja
* A dolly állását staticban tárolja, ezért új dolly eseteén először egy restart = true-val kell meghívni,
* \param ms eltelt idő
* \param kell-e újrakezdés?
* \return elérte-e a végállást?
* Minden frameban újraszámolja a rows&coulmns-t,
*/
bool cameraDollyZ(int deltaTime, bool restart) {
static int timePassed;
if(restart) timePassed = 0;
bool done = true;
timePassed += deltaTime;
float t = (float)(cameraDollyTime - timePassed) / cameraDollyTime;
float cube = t*t*t;
float currentZ = cameraDeltaZDistanceEnd + (cameraDeltaZDistanceStart - cameraDeltaZDistanceEnd) * cube;
if(currentZ <= cameraDeltaZDistanceEnd) {
currentZ = cameraDeltaZDistanceEnd;
done = false;
}
cameraPosition.z = currentZ;
calculateRowsAndColumns();
return done;
}
/** \brief Térbeli pontból síkbelit képez.
* A 3D-s pont koordinátái és a kamera alapján kiszámolja, hogy a kamera 2D-s szenzorán hol jelenik meg a pont.
* Geometriailag leírva, a két ponton (point, cameraPosition) átmenő egyenes egyenletének, és a 'szenzor' vízszintesének metszéspontja
* a keresett pontunk. A lent és a linken is látható egyenletet először a tér x-z, majd az y-z vetületére alkalmazzuk,
* és megkapjuk a térbeli pontunk megfelelő síkbeli leképezést.
* Ez interaktívan ábrázolva így néz ki: https://www.desmos.com/calculator/8qjvkptjmn
* \param Térvektor típusú pont
* \return Síkvektor típusú pont
* A visszaadott pontnak int koordinátái vannak, hiszen valamelyik pixel koordinátáit jelöli.
* Ez a függvény igazából egy camera obscura modellje.
*/
intVector2D get2DPoint(vector3D point) {
//Ha watchBounds = true, csak egy megadott kockán belül adja vissza az értéket (rajzol), ez a feldarabolás miatt fontos
if(watchBounds) {
putInsideIntervalF(leftBound, &point.x, rightBound);
putInsideIntervalF(lowerBound, &point.y, upperBound);
}
//Ezt a két kifejezést újra felhasználjuk
float zDist = point.z - cameraPosition.z;
float zDistFromHole = zDist - focalLength;
float dx = (cameraPosition.x * zDistFromHole + focalLength * point.x) / zDist - cameraPosition.x;
float dy = (cameraPosition.y * zDistFromHole + focalLength * point.y) / zDist - cameraPosition.y;
//Megfelelően tükrözzük a kapot pontot, és a méterben kapott értéket átváltjuk pixelekre
intVector2D resultPoint = {
(int)(screen -> w / 2 - magnification * dx),
(int)(screen -> h / 2 + magnification * dy)
};
return resultPoint;
}
/** \brief Lerajzol egy négyszöget
* A bemenetként kapott 4 pont alkotta négyszöget rajzolja ki.
* \param síkvektor a csúcshoz
* \param síkvektor b csúcshoz
* \param síkvektor c csúcshoz
* \param síkvektor d csúcshoz
* \param szín négyszög színe
* Ezt egyszer kitöltéssel csinálja, egyszer pedig anti-aliasing poligonnal,
* hogy a rajz széle ne legyen recegős.
*/
static void drawTetragon(intVector2D a, intVector2D b, intVector2D c, intVector2D d, color color) {
Sint16 xCoords[4] = {(Sint16)a.x, (Sint16)b.x, (Sint16)c.x, (Sint16)d.x};
Sint16 yCoords[4] = {(Sint16)a.y, (Sint16)b.y, (Sint16)c.y, (Sint16)d.y};
filledPolygonRGBA(screen, xCoords, yCoords, 4, color.r, color.g, color.b, color.a);
aapolygonRGBA(screen, xCoords, yCoords, 4, color.r, color.g, color.b, color.a);
}
/** \brief Lerajzol egy téglatestet
* A kamera modul lényege, a rajzolást végzi el az eddigi függvények segítségével.
* \param test téglatest adatai
* \param bool melyik oldalakat kell rajzolni?
* Végez egy plusz ellenőrést is, nem csak a paraméterben megadott tömb alapján nem rajzol oldalakat,
* azokat se rajzolja le, amiket a helyzetük miatt biztosan nem láthat a kamera.
*/
void drawCuboid(renderObject object, bool sides[]) {
intVector2D vertexes[8];
//A téglatestet bejárva megkeressük a csúcsait
int i;
for(i = 0; i < 8; i++) {
vertexes[i] = get2DPoint(object.position);
switch(i % 4) {
case 0:
object.position.x += object.size.x;
break;
case 1:
object.position.y += object.size.y;
break;
case 2:
object.position.x -= object.size.x;
break;
case 3:
object.position.y -= object.size.y;
}
if(i == 3) object.position.z += object.size.z;
}
//alja
if(sides[0] && object.position.y > cameraPosition.y) drawTetragon(vertexes[0], vertexes[4], vertexes[5], vertexes[1], object.colors.dark);
//bal oldali lap
if(sides[1] && object.position.x > cameraPosition.x) drawTetragon(vertexes[0], vertexes[3], vertexes[7], vertexes[4], object.colors.dark);
//jobb oldali lap
if(sides[2] && object.position.x + object.size.x < cameraPosition.x) drawTetragon(vertexes[1], vertexes[2], vertexes[6], vertexes[5], object.colors.dark);
//teteje
if(sides[3] && object.position.y + object.size.y < cameraPosition.y) drawTetragon(vertexes[3], vertexes[7], vertexes[6], vertexes[2], object.colors.light);
//eleje
if(sides[4]) drawTetragon(vertexes[4], vertexes[5], vertexes[6], vertexes[7], object.colors.normal);
}
/// Az egy m^3-ben lévő testeket rajzolja le
static void drawPlace(int x, int* y, int direction, int bound) {
bool sides[5] = {true, true, true, true, true};
renderObject toDraw = getBlockRenderObject(x, y, direction, bound, sides);
if(toDraw.doesExist) {
watchBounds = false;
drawCuboid(toDraw, sides);
watchBounds = true;
}
else {
int i = 0;
toDraw = renderMap[x - leftMostBound][*y - bottomBound][i];
while(toDraw.doesExist && i < renderObjectsPerCube) {
drawCuboid(toDraw, sides);
i++;
toDraw = renderMap[x - leftMostBound][*y - bottomBound][i];
}
renderObject player = getPlayerRenderObject();
if(
(x == (int)floor(player.position.x) || x == (int)floor(player.position.x + player.size.x)) &&
(*y == (int)floor(player.position.y) || *y == (int)floor(player.position.y + player.size.y))
)
drawCuboid(player, sides);
}
}
///Az egy oszlopban lévő testeket rajzolja le, a részecskéket mozgatja közvetlen
static void drawY(int deltaTime, int x) {
int y;
for(y = topBound; y > cameraPosition.y; y--) {
upperBound = y + 1;
lowerBound = y;
drawPlace(x, &y, -1, (int)cameraPosition.y);
}
for(y = bottomBound; y <= cameraPosition.y; y++) {
upperBound = y + 1;
lowerBound = y;
drawPlace(x, &y, 1, (int)cameraPosition.y + 1);
}
watchBounds = false;
drawParticles(x, bottomBound, topBound, deltaTime);
watchBounds = true;
}
/** \brief Lerajzolja a teljes látható játékteret.
* A megfelelő rajzoláshoz figyelni kell a testek takarására, nem mindegy ezért, hogy milyen sorrendben rajzoljuk őket.
* A részecskéknél ezzel nincs probléma, azokat bármilyen sorrendben rajzolhatjuk. Ők a játékteret kitakarják, egymásnál viszont nem látszik,
* hogy melyik takrja melyiket, ezért azokat elég a játéktér után bármilyen sorrendben lerajzolni.
* A térképet alkotó kockáknál már nem ilyen egyszeű a helyzet. Azokat 4 irányból kell rajzolni, bal lentről, jobb lentről, bal fentről, illetve jobb fentről középre.
* Ha ebben a négy lépésben rajzoljuk őket (ezek között a sorrend nem számít) akkor mindig a legfesőket rajzoljuk utoljára, és így valósnak néz ki az eredmény.
* A játékos, a lövedékek, illetve az ellenségek viszont sajnos nincsenek egy négyzethálóhoz kötve. Így gyakran megesik, hogy az egyik felüket a pálya
* egyik kockája előtt, a másikat pedig az után, de még a kövektező előtt kéne rajzolni. Ezt nem tudjuk megcsinálni, hatalmas erőforrásba kerülne ez a majdnem pixelről
* pixelre történő szkennelés. A pályát továbbra is m^3-ént szeretnénk lerajzolni. Hogy ez megfelelően nézzen ki, minden köbméterben csak az abban a kockában lévő dolgokat
* szeretnénk megrajzolni. Ehhez fel kell "darabolnunk" a játékost, a lövedékeket, illetve az ellenségeket is. A darabolás azt jelenti, hogy egy mátrix minden cellájába
* beírjuk, hogy az adott kockában melyik, milyen tulajdonsággal rendelkező testek lógnak be. Rajzolásnál pedig a testeknek mindig csak az adott kockabeli részét rajzoljuk le.
* Mivel a kockákat jó sorrendben rajzoljuk, így a bennük levő "testekrészeket" is jó sorrendbe fogjuk rajzolni. A problémát megoldottuk.
* \param ms eltelt idő
* Ez a függvény elvégzi a részecskék, a pálya, illetve minden más test rajzolását is. A részecskék mozgatása innen van meghívva.
*/
void drawMap(int deltaTime) {
leftMostBound = (int)(cameraPosition.x - (float)columns / 2) - 1;
rightMostBound = (int)(cameraPosition.x + (float)columns / 2) + 1;
topBound = (int)(cameraPosition.y + (float)rows / 2 + 1);
bottomBound = (int)(cameraPosition.y - (float)rows / 2 - 1);
int y, x;
for(x = leftMostBound; x < rightMostBound; x++)
for(y = bottomBound; y < topBound; y++)
getFloatersRenderObject(x, y, renderMap[x - leftMostBound][y - bottomBound], renderObjectsPerCube);
float slicer = cameraPosition.x;
watchBounds = true;
for(x = leftMostBound; x <= slicer; x++) {
leftBound = x - 0.005;
rightBound = x + 1 + 0.005;
drawY(deltaTime, x);
}
for(x = rightMostBound; x > slicer - 1; x--) {
leftBound = x - 0.005;
rightBound = x + 1 + 0.005;
drawY(deltaTime, x);
}
watchBounds = false;
}

View file

@ -1,24 +0,0 @@
#ifndef CAMERA_H_INCLUDED
#define CAMERA_H_INCLUDED
#include "global.h"
#include "floaters.h"
#include "player.h"
#include "map.h"
#include "particles.h"
#define cameraDeltaZDistanceStart 25 /**<dolly start táv [m]*/
#define cameraDeltaZDistanceEnd 14 /**<dolly end táv [m]*/
#define cameraDollyTime 3250 /**<[ms]*/
void freeRenderMap();
void followPlayerWithCamera();
void menuCamera(int* getColumns, int* getRows);
int getColumns();
bool cameraDollyZ(int deltaTime, bool restart);
intVector2D get2DPoint(vector3D point);
void drawCuboid(renderObject object, bool sides[]);
void drawMap(int deltaTime);
#endif // CAMERA_H_INCLUDED

View file

@ -1,56 +0,0 @@
#include "color.h"
#define baseHue 30 /**< A kockák átlagos színezetsége. */
#define hueVariance 20 /**< A kockák színezetségének átlagostól vlaó eltérése. */
#define baseSaturation 0.75 /**< A kockák átlagos telítettsége. */
#define baseLightness 0.6 /**< A kockák átlagos fényessége. */
#define lightnessStep 0.08 /**< A kockák oldalainak háromféle fényesség értéke van, ezzel szimuláljuk a fény-árnyékot. */
///Ez a függvény majdnem teljes egészében erről az oldalról származik: https://www.programmingalgorithms.com/algorithm/hsl-to-rgb?lang=C
static float hueToRgb(float v1, float v2, float vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return (v1 + (v2 - v1) * 6 * vH);
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return (v1 + (v2 - v1) * ((2.0f / 3) - vH) * 6);
return v1;
}
///Ez a függvény majdnem teljes egészében erről az oldalról származik: https://www.programmingalgorithms.com/algorithm/hsl-to-rgb?lang=C
static color hslToRgb(int h, float s, float l) {
color rgb;
if (s == 0) rgb.r = rgb.g = rgb.b = (unsigned char)(l * 255);
else {
float v1, v2;
float hue = (float)h / 360;
v2 = (l < 0.5) ? (l * (1 + s)) : ((l + s) - (l * s));
v1 = 2 * l - v2;
rgb.r = (unsigned char)(255 * hueToRgb(v1, v2, hue + (1.0 / 3)));
rgb.g = (unsigned char)(255 * hueToRgb(v1, v2, hue));
rgb.b = (unsigned char)(255 * hueToRgb(v1, v2, hue - (1.0 / 3)));
rgb.a = 255;
}
return rgb;
}
/** \brief Véletlenszerűre színez egy kockát.
* \return színek
* Az alapszín, és hueVariance alapján meghatároz
* véletlenszerűen egy színezetséget, és az ahhoz a színezetséghez tartozó világosabb és sötétebb
* árnyalatú színeket is, majd ezeket RGBA-ba kódolja.
* Egy random hue-val készített paletta sokkal jobban néz ki, mint egy random RGB-vel készített.
*/
colors createBlockColor() {
int hue = randomIntWithinBorders(baseHue, hueVariance);
if(hue < 0) hue += 360;
return (colors) {
hslToRgb(hue, baseSaturation, baseLightness + lightnessStep),
hslToRgb(hue, baseSaturation, baseLightness),
hslToRgb(hue, baseSaturation, baseLightness - lightnessStep)
};
}

View file

@ -1,8 +0,0 @@
#ifndef COLOR_H_INCLUDED
#define COLOR_H_INCLUDED
#include "global.h"
colors createBlockColor();
#endif // COLOR_H_INCLUDED

View file

@ -1,281 +0,0 @@
#include "floaters.h"
#define enemyMinSize 0.75 /**< Ellenségek oldalhosszának kezdeti értéke [m].*/
#define enemyMaxSize 4 /**< Az ellenségek, ha összeérnek, összetapadnak, de ennél ne lehetnek nagyobbak [m]. */
#define enemyVelocity 0.006 /**< [m/ms] */
#define enemySlowing 0.15 /**< Ha az ellenség nekiütközik valaminek, akkor a sebessége ennyiszeresére csökken. */
#define slownessDebuffDuration 2000 /**< Ha az ellenség nekiütközik valaminek, akkor a sebessége ennyi ideig lesz alacsnyabb. [ms] */
#define projectileSize 0.2 /**< A játékos által lőtt lövedék oldalhossza [m]. */
#define projectileVelocity 0.03 /**< A játékos által lőtt lövedék kezdő sebessége [m/ms] */
#define projectileVelocityYMax 0.035 /**< A lövedék sebességét befolyásolja a játékos sebessége (a kiulövés pillanatában), illetve a gravitációs gyorsulás, viszont sosem lehet gyorsabb. */
#define floaterColor (colors) { (color) {229, 22, 22, 255}, (color) {200, 20, 20, 255}, (color) {168, 16, 16, 255} } /**< A lövedék és az ellenség ugyanolyan színű */
floater* floaterList = NULL; /**< Láncolt lista, ami az ellenségeket és lövedékeket is tartalmazza. Nagyon hasonló a kettő, ezért célszerű közösen kezelni őket.*/
///Lista elejére beszúr.
floater* addToFloaterList() {
floater* newfloater = (floater*) malloc(sizeof(floater));
newfloater -> next = floaterList;
floaterList = newfloater;
return newfloater;
}
///Felszabadítja az ellenségek és lövedékek listáját.
void freeFloaterList() {
if(floaterList == NULL) return;
floater* moving = floaterList;
floater* tmp;
while(moving != NULL) {
tmp = moving;
moving = moving -> next;
free(tmp);
}
floaterList = NULL;
}
///A játékos közelében elhelyez egy új ellenséget.
void createEnemy() {
floater* newEnemy = addToFloaterList();
newEnemy -> type = enemy;
newEnemy -> velocity = (vector2D) {0, 0};
newEnemy -> slownessDebuffLeft = 0;
newEnemy -> size = enemyMinSize;
vector3D playerPosition = getPlayerRenderObject().position;
int i = 0;
do {
newEnemy -> position.x = playerPosition.x + (float) (randomPlusOrMinus() * randomIntWithinBorders(50 - i, 20));
newEnemy -> position.y = playerPosition.y + (float) (randomIntWithinBorders(40 - i, 15));
i++;
}
while(!isWithinTheMap(newEnemy -> position, (vector2D) {newEnemy -> size, newEnemy -> size}));
}
/** \brief Lövedék létrehozása.
* A megadott paraméterek függvényében a létrehozott lövedék az egér irányafele indul el.
* \param helyvektor a játékoshoz
* \param térvektor a játékos méretéhez
* \param síkvektor* játékos sebesség (ezt Newton III. értelmében megváltoztatja
* \param koordinátája az egérnek
* A játékost ellenkező irányba elmozdítja. A lövedék kezdősebbességéhez hozzáadódik a játékos sebessége.
*/
void createProjectile(vector3D playerPosition, vector3D playerSize, vector2D* playerVelocity, mousePos mouse) {
floater* newProjectile = addToFloaterList();
newProjectile -> type = projectile;
newProjectile -> position.x = playerPosition.x + playerSize.x / 2;
newProjectile -> position.y = playerPosition.y + playerSize.y / 2;
newProjectile -> size = projectileSize;
intVector2D playerCoord = get2DPoint((vector3D) {newProjectile -> position.x, newProjectile -> position.y, 0});
int deltaX = mouse.x - playerCoord.x;
int deltaY = playerCoord.y - mouse.y;
float distance = sqrt(deltaX * deltaX + deltaY * deltaY);
float compensation = 0;
if(distance > 0) compensation = projectileVelocity / distance;
newProjectile -> velocity = (vector2D) {
deltaX * compensation + playerVelocity -> x,
deltaY * compensation + playerVelocity -> y
};
playerVelocity -> x -= newProjectile -> velocity.x * 0.25;
playerVelocity -> y -= newProjectile -> velocity.y * 0.25;
}
///Listából kiszed.
void destroyFloater(floater* floaterToDestroy) {
if(floaterList == floaterToDestroy) floaterList = floaterToDestroy -> next;
else {
floater* previous = floaterList;
while(previous != NULL && previous -> next != floaterToDestroy)
previous = previous -> next;
previous -> next = floaterToDestroy -> next;
}
free(floaterToDestroy);
}
/** \brief Megnézi összeér-e két ellenség, ha igen, összeolvasztja őket.
* A kisebb ellenség eltűnik, a nagyobbnak a térfogata a kisebbéjével nő.
* \param ellenség1
* \param ellenség2
* \return Összeértek-e?
*/
static bool mergeEnemies(floater* enemy1, floater* enemy2) {
floater* enemyBigger = enemy1 -> size > enemy2 -> size ? enemy1 : enemy2;
floater* enemySmaller = enemy1 -> size > enemy2 -> size ? enemy2 : enemy1;
if(isBoxOverlappingBox(
enemy1 -> position, (vector2D) {enemy1 -> size, enemy1 -> size},
enemy2 -> position, (vector2D) {enemy2 -> size, enemy2 -> size}
)) {
enemyBigger -> size = smallerF(fDist(enemyBigger -> size, enemySmaller -> size), enemyMaxSize);
destroyFloater(enemySmaller);
return true;
}
return false;
}
///Kezeli ha egy ellenség le lett lőve, a lövedék eltűnik, az ellenség mérete pedig csökken, vagy ha túl kicsi, az ellenség eltűnik.
static void projectileEnemyCollision(floater* projectile) {
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(currentFloater -> type == enemy &&
isBoxOverlappingBox(
projectile -> position, (vector2D) {projectile -> size, projectile -> size},
currentFloater -> position, (vector2D) {currentFloater -> size, currentFloater -> size}
)
) {
destroyFloater(projectile);
currentFloater -> size = sqrt(square(currentFloater -> size) - 0.95 * square(enemyMinSize * enemyMinSize));
if(currentFloater -> size < enemyMinSize * 1.05)
destroyFloater(currentFloater);
}
currentFloater = currentFloater -> next;
}
}
/** \brief Megnézi, hogy ütközött-e valami egy kockával, ha igen, kezeli.
* A kocka mindenképpen megsemmisül.
* \param helyvektor a floaterhez
* \param mérete a flaoternek
* \return Ütköztek-e?
* A visszatérési érték alapján megfelelően lehet kezelni az esetet.
*/
static bool floaterSolidCollision(vector2D position, float size) {
block* blockToDestroy;
if(
isWithinTheMap(position, (vector2D) {size, size}) &&
(detectCollision(position, (vector2D) {size, size}, solid, &blockToDestroy) ||
detectCollision(position, (vector2D) {size, size}, treasure, &blockToDestroy)) &&
blockToDestroy != NULL
) {
*blockToDestroy = air;
return true;
}
return false;
}
/** \brief Mozgatja az adott ellenséget.
* Figyel minden lehetsége ütközésre.
* \param ellenség
* \param ms eltelt idő
*/
static void moveEnemy(floater* thisEnemy, int deltaTime) {
float velocity = enemyVelocity;
if(thisEnemy -> slownessDebuffLeft > 0) {
thisEnemy -> slownessDebuffLeft -= deltaTime;
velocity *= enemySlowing;
}
renderObject player = getPlayerRenderObject();
vector3D playerPosition = player.position;
float deltaX = (playerPosition.x - thisEnemy -> position.x);
float deltaY = (playerPosition.y - thisEnemy -> position.y);
float compensation = 0;
float distance = fDist(deltaX, deltaY);
if(distance > 0)
compensation = velocity / distance;
thisEnemy -> position.x += deltaX * compensation * deltaTime;
thisEnemy -> position.y += deltaY * compensation * deltaTime;
if(floaterSolidCollision(thisEnemy -> position, thisEnemy -> size))
thisEnemy -> slownessDebuffLeft = slownessDebuffDuration;
if(isBoxOverlappingBox((vector2D) {player.position.x, player.position.y}, (vector2D){player.size.x, player.size.y},
thisEnemy -> position, (vector2D) {thisEnemy -> size, thisEnemy -> size})) setDie(true);
floater* currentEnemy = floaterList;
while(currentEnemy != NULL && (thisEnemy == currentEnemy || !mergeEnemies(thisEnemy, currentEnemy)))
currentEnemy = currentEnemy -> next;
}
/** \brief Mozgatja az adott lövedéket.
* Figyel minden lehetsége ütközésre.
* \param lövedék
* \param ms eltelt idő
*/
static void moveProjectile(floater* thisProjectile, int deltaTime) {
thisProjectile -> velocity.y -= g * deltaTime;
if(thisProjectile -> velocity.y < -projectileVelocityYMax) thisProjectile -> velocity.y = -projectileVelocityYMax;
thisProjectile -> position.x += thisProjectile -> velocity.x * deltaTime;
thisProjectile -> position.y += thisProjectile -> velocity.y * deltaTime;
if(
!isWithinTheMap(thisProjectile -> position, (vector2D) {projectileSize, projectileSize}) ||
floaterSolidCollision(thisProjectile -> position, thisProjectile -> size)
)
destroyFloater(thisProjectile);
projectileEnemyCollision(thisProjectile);
}
///Minden floaterre meghívja a szükséges mozgatási műveletet.
void moveFloaters(int deltaTime) {
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(currentFloater -> type == projectile) moveProjectile(currentFloater, deltaTime);
else moveEnemy(currentFloater, deltaTime);
currentFloater = currentFloater -> next;
}
}
/** \brief Visszaadja az adott térfogatban lévő floater testeket.
* A sorrend nem elhanyagolható 3D-s testek rajzolásakor. Figyelni kell, melyik melyik másikat takarja el.
* Ezért kell "feldarabolni" őket. Itt még csak az egy adott m^3-ben lévő testek adatait adja oda a rajzoló függvénynek.
* Maga a feldarabolás már nem ennek a függvénynek a dolga.
* \param koord x
* \param koord y
* \param Hova kell írnia a testeket
* \param Max mennyit adhat vissza
* A feldarabolásról részletesebben a camera.c modulban.
*/
void getFloatersRenderObject(int x, int y, renderObject* renderMap, int renderObjectsPerCube) {
renderObject enemyRenderObject;
enemyRenderObject.colors = floaterColor;
int floaterCounter = 0;
floater* currentFloater = floaterList;
while(currentFloater != NULL) {
if(
(int)currentFloater -> position.x <= x && x <= (int)(currentFloater -> position.x + currentFloater -> size) &&
(int)currentFloater -> position.y <= y && y <= (int)(currentFloater -> position.y + currentFloater -> size)
) {
enemyRenderObject.position = (vector3D) {currentFloater -> position.x, currentFloater -> position.y, 0.5};
enemyRenderObject.size = (vector3D) {currentFloater -> size, currentFloater -> size, currentFloater -> size/2};
enemyRenderObject.doesExist = true;
if(floaterCounter < renderObjectsPerCube - 1) {
renderMap[floaterCounter] = enemyRenderObject;
floaterCounter++;
}
}
currentFloater = currentFloater -> next;
}
enemyRenderObject.doesExist = false;
renderMap[floaterCounter] = enemyRenderObject;
}

View file

@ -1,29 +0,0 @@
#ifndef FLOATERS_H_INCLUDED
#define FLOATERS_H_INCLUDED
#include "global.h"
#include "map.h"
#include "player.h"
#include "camera.h"
#include "auxiliary/auxSDL.h"
typedef enum floaterType {
enemy, projectile
} floaterType;
typedef struct floater {
floaterType type;
vector2D position;
vector2D velocity;
float slownessDebuffLeft;
float size;
struct floater* next;
} floater;
void freeFloaterList();
void createEnemy();
void createProjectile(vector3D playerPosition, vector3D playerSize, vector2D* playerVelocity, mousePos mouse);
void moveFloaters(int deltaTime);
void getFloatersRenderObject(int x, int y, renderObject* renderMap, int renderObjectsPerCube);
#endif // FLOATERS_H_INCLUDED

View file

@ -1,72 +0,0 @@
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
#include <SDL.h>
#include <SDL_ttf.h>
#include <SDL_gfxPrimitives.h>
#include "auxiliary/auxFunctions.h"
#ifndef GLOBAL_H_INCLUDED
#define GLOBAL_H_INCLUDED
#define FPS (int) 50
float g;
///<Globális változók>
SDL_Surface* screen;
TTF_Font* fontSmaller;
TTF_Font* fontSmall;
TTF_Font* fontSmallBold;
TTF_Font* fontLarge;
SDL_Surface* text;
unsigned char* keys;
///</Globális változók>
///<Vektorok>
typedef struct vector2D {
//1 egység == 1m
float x, y;
} vector2D;
typedef struct intVector2D {
int x, y;
} intVector2D;
typedef struct vector3D {
//1 egység == 1m
float x, y, z;
} vector3D;
///</Vektorok>
///<Színek>
typedef struct color {
unsigned char r, g, b, a;
} color;
typedef struct colors {
color light, normal, dark;
} colors;
///</Színek>
typedef enum block {
air, solid, invisible, treasure
} block;
typedef struct renderObject {
vector3D position;
vector3D size;
colors colors;
bool doesExist;
} renderObject;
#endif // GLOBAL_H_INCLUDED

View file

@ -1,379 +0,0 @@
#include "map.h"
#define maxPlatformLength 5 /**< A legszélesebb platformok szélessége [m]. */
#define minPlatformLength 2 /**< A legkeskenyebb platformok szélessége [m], ez nem teljesen igaz, mindig lesznek ksiebbek is, csak nem olyan sok. */
#define baseHeightPercent 0.3 /**< A pálya hányad része legyen tömör "föld". */
#define fuzziness 2 /**< Mennyire legyen recegős a pálya? (legnagyobb magasság különbségek a földszinten)*/
#define treasureColor (colors){ (color){69, 82, 160, 255}, (color){58, 69, 135, 255}, (color){44, 53, 102, 255} } /**< A jutalmak színe. */
#define treasureSize (vector3D){0.5, 0.5, 0.25} /**< A jutalom mérete. */
colors* colorMap = NULL; /**< A színeket tartalmazó 1 dimenziós tömb. (minden oszlop egyszínű) */
block** map = NULL; /**< A pályát tartalmazó 2 dimenziós tömb. */
intVector2D mapSize; /**< A pálya mérete [m]. */
///A pálya méretét lehet vele beállítani.
void setMapSize(int x, int y) {
mapSize.x = x;
mapSize.y = y;
}
///A pálya méretét adja vissza.
intVector2D getMapSize() {
return mapSize;
}
///A pályát adja vissza.
block** getMap() {
return map;
}
///Megmondja, hogy egy pont benne van-e egy négyzetben.
bool isPointWithinBox(vector2D point, vector2D boxPosition, vector2D boxSize) {
return
boxPosition.x < point.x && point.x < boxPosition.x + boxSize.x &&
boxPosition.y < point.y && point.y < boxPosition.y + boxSize.y;
}
///Megmondja, hogy két négyzet fedésben van-e.
bool isBoxOverlappingBox(vector2D box1Position, vector2D box1Size, vector2D box2Position, vector2D box2Size) {
int i;
for(i = 0; i < 4; i++) {
switch(i) {
case 1:
box1Position.x += box1Size.x;
break;
case 2:
box1Position.y += box1Size.y;
break;
case 3:
box1Position.x -= box1Size.x;
}
if(isPointWithinBox(box1Position, box2Position, box2Size)) return true;
}
return false;
}
///Megmondja, hogy egy pont a pályán belül van-e.
bool isWithinTheMap(vector2D point, vector2D size) {
return
0 <= point.x && point.x + size.x < mapSize.x &&
0 <= point.y && point.y + size.y < mapSize.y;
}
///Megmondja, hogy történt-e ütközés egy négyzet és a pálya között. Visszaadja a szóban forgó kocka pointerét is.
bool detectCollision(vector2D position, vector2D size, block collidableBlock, block** collidedWith) {
int x, y;
if (isWithinTheMap(position, size)) {
for(x = (int)position.x; x < position.x + size.x; x++) {
for(y = (int)position.y; y < position.y + size.y; y++) {
*collidedWith = &map[x][y];
if(**collidedWith == collidableBlock) return true;
}
}
return false;
}
else {
collidedWith = NULL;
return true;
}
}
///Felszabadítja a színek tömbjét.
void freeColorMap() {
free(colorMap);
}
///Véletlenszerűen létrehozza a kockák palettáját, egy oszlopon belül minden kocka egyszínű.
void generateColorMap() {
freeColorMap();
colorMap = (colors*) malloc(mapSize.x * sizeof(colors));
int x;
for(x = 0; x < mapSize.x; x++)
colorMap[x] = createBlockColor();
}
///Felszabadítja a térkép tömbjét.
void freeMap() {
if(map != NULL) {
free(map[0]);
free(map);
}
}
///Létrehoz egy tömböt a térképnek.
void createMap() {
freeMap();
int x;
map = (block**) malloc(mapSize.x * sizeof(block*));
map[0] = (block*) malloc(mapSize.x * mapSize.y * sizeof(block));
for(x = 1; x < mapSize.x; x++)
map[x] = map[0] + x * mapSize.y;
}
///Kiszámolja, hogy ellehet-e érni egy adott kockát a játékosnak.
static bool canBeReached(int deltaX, int deltaY, vector2D maxVelocity, float hMax) {
float t1 = maxVelocity.y / g;
float t2 = sqrt(2 * (hMax - (float)deltaY) / g);
float possibleDeltaX = maxVelocity.x * (t1 + t2);
return possibleDeltaX > deltaX;
}
///Egy kiszámolt mátrix alapján megmondja, hogy a játékos elérhet-e egy adott kockát.
static bool canJumpThere(intVector2D from, intVector2D to, int width, int height, bool possiblePlacesToJumpTo[][height]) {
int xIndex;
int yIndex = to.y - from.y - 1;
if(yIndex >= height || yIndex < 0) return false;
xIndex = to.x > from.x ? to.x - from.x - 1 : from.x - to.x - 1;
if(xIndex >= width || xIndex < 0) return false;
return possiblePlacesToJumpTo[xIndex][yIndex];
}
///A pálya földszintjét hozza létre.
static void createMapBase() {
int x, y, currentHeight;
int baseHeight = (int) (mapSize.y * baseHeightPercent);
for(x = 0; x < mapSize.x; x++)
for(y = 0; y < mapSize.y; y++)
map[x][y] = air;
currentHeight = baseHeight / 2;
for(x = 0; x < mapSize.x; x++) {
if(fuzziness > 0) {
currentHeight = randomIntWithinBorders(currentHeight, fuzziness);
putInsideInterval(1, &currentHeight, baseHeight);
}
for(y = 0; y <= baseHeight + currentHeight; y++)
map[x][y] = solid;
}
}
///Létrehoz egy platformot, ami nem lóg túl a pályarészek határán, ezekről később bővebben.
static void buildPlatform(int direction, intVector2D* previous, int x, int xMaxBound) {
int platformLength = rand() % (maxPlatformLength - minPlatformLength) + minPlatformLength + 1;
if(direction == 1 && previous -> x + platformLength - 1 >= xMaxBound)
platformLength = xMaxBound - previous -> x;
if(direction == -1 && previous -> x - platformLength < x)
platformLength = previous -> x - x + 1;
int m;
for(m = 0; m < platformLength; m++) {
map[previous -> x + direction * m][previous -> y] = solid;
if(rand() % 3 == 0) map[previous -> x][previous -> y + 1] = treasure;
}
previous -> x += direction * rand() % platformLength;
}
///Primitív megoldás, de lthatatlan falakat csinál, amik megakadályozzák, hogy olyan helyre menjen a játékos, ahova nem kéne.
static void createInvisibleBounds() {
int x, y;
for(x = 0; x < mapSize.x; x++) {
map[x][0]= invisible;
map[x][mapSize.y - 1]= invisible;
}
for(y = 0; y < mapSize.y; y++) {
map[0][y]= invisible;
map[mapSize.x - 1][y]= invisible;
}
}
/** \brief Térkép platformokkal betöltése, úgy hogy játszható legyen
* Kisebb egységekre, részekre bontja a pályát. Minden egységben egy darab platform oszlop van. A platformok magasság különbsége olyan,
* hogy a jelenlegi fizikával a játékos ugrálva elérhesse őket. A pályagenerálás előtt ezért ki kell számolni, milyen messzire ugorhat.
* \param sebességvektor player max
* A platformokra random tesz jutalmakat (kék kockákat) is.
* Nem túl hosszú a függvény, de nem is a legrövidebb, ennél több részre sajnos nem lehet szétszedni szépen.
*/
void generateMap(vector2D maxVelocity) {
createMapBase();
int x, y;
//A játékos képességeinek maximuma
float realHMax = (maxVelocity.y * maxVelocity.y / 2 / g);
int lowerHMax = (int)realHMax;
int upperHMax = lowerHMax + 1;
int lowerXMax = (int)(2 * maxVelocity.x * maxVelocity.y / g);
//Hova ugorhat a (-1, -1) -ről?
int possibleWidth = lowerXMax;
int possibleHeight = lowerHMax;
bool possiblePlacesToJumpTo[possibleWidth][possibleHeight];
for(y = 0; y < possibleHeight; y++)
for(x = 0; x < possibleWidth; x++)
possiblePlacesToJumpTo[x][y] = canBeReached(x + 1, y + 1, maxVelocity, realHMax);
intVector2D min, max, previous, test;
int previousPreviousY;
bool isOkey;
int xInterval = 10;
int minHeightDiff = 2;
int l, direction, optionsCount, xMaxBound;
bool alreadyTried;
intVector2D options[possibleWidth * possibleHeight];
for(x = 0; x < mapSize.x; x += xInterval) {
//Kezdőpont keresés
previous.x = smaller(x + rand() % xInterval, mapSize.x - 1);
xMaxBound = smaller(x + xInterval, mapSize.x);
min.y = getHeightAtX(previous.x);
previous.y = min.y;
previousPreviousY = -100;
max.y = min.y;
//Addig csináljuk, amíg nem érünk a tetejére
while(previous.y + 2 * upperHMax < mapSize.y) {
//Határok meghatározása véletlenszerű irány alapján
direction = randomPlusOrMinus();
alreadyTried = false;
newDirection:
min.y = larger(previousPreviousY + upperHMax + 1, previous.y + minHeightDiff) + 1;
max.y = previous.y + possibleHeight;
//[határok)
if(direction == 1) {
//jobbra
min.x = smaller(previous.x + 1, mapSize.x - 1);
max.x = smaller(previous.x + possibleWidth + 1, xMaxBound);
}
else {
//balra
min.x = larger(larger(previous.x - possibleWidth, x), 0);
max.x = larger(previous.x, 1);
}
//A kiválasztott határok közé lehet-e ugrani egyáltalán?
isOkey = false;
optionsCount = 0;
for(test.x = min.x; test.x < max.x; test.x++)
for(test.y = min.y; test.y < max.y; test.y++)
if(canJumpThere(previous, test, possibleWidth, possibleHeight, possiblePlacesToJumpTo)) {
isOkey = true;
options[optionsCount++] = test;
}
//Ha igen, válasszunk ki egyet véletlenszerűen
if(isOkey) {
previousPreviousY = previous.y;
l = rand() % optionsCount;
previous = options[l];
buildPlatform(direction, &previous, x, xMaxBound);
}
else {
if(alreadyTried) break;
alreadyTried = true;
direction *= -1;
goto newDirection; // a goto nem a legjobb, de mindenképpen szebb egy for ciklusnál
}
}
}
createInvisibleBounds();
}
///Megmondja egy adott x-hez milyen magas földszint tartozik
int getHeightAtX(int x) {
int y;
for(y = 1; map[(int)x][y] == solid; y++)
;
return y;
}
///Ez a player modul szempontjából lényeges, megmondja, hogy belelóg-e egy kockába
bool isInBlockSideways(int x, int lowestY, int uppermostY) {
return map[x][lowestY]== solid || map[x][lowestY]== invisible ||
map[x][uppermostY]== solid || map[x][uppermostY]== invisible;
}
///Ez a player modul szempontjából lényeges, megmondja, hogy belelóg-e egy kockába
bool isInBlockPerpendicular(int leftmostX, int rightmostX, int y) {
return map[leftmostX][y]== solid || map[leftmostX][y]== invisible ||
map[rightmostX][y]== solid || map[rightmostX][y ]== invisible;
}
///Melyik oldalai látszanak egy kockának? Azok biztos nem, amiket más kockák eltakarnak.
static char calculateSides(bool sides[], int x, int y) {
sides[0] = map[x][y - 1] != solid; //alja
sides[1] = map[x - 1][y] != solid; //bal oldal
sides[2] = map[x + 1][y] != solid; //jobb oldal
sides[3] = map[x][y + 1] != solid; //teteje
sides[4] = true; //eleje
char sum = 0;
int i;
for(i = 0; i < 5; i++)
if(sides[i]) sum += 1 << i;
return sum;
}
/** \brief A térkép rajzolást teszi lehetővé.
* A sebesség növelés végett az egymás alatt lévő kockákat (mivel egyszínűek) egyszerre is rajzolhatjuk.
* Ilyenkor több dologra is kell figyelnünk. Először is, nem elég az azonos szín, arra is figyelni kell, hogy
* azonos oldalaik is látszódjanak. Ha tud oszlopot rajzolni, akkor az y koordját módosítja hívójának.
* \param koord x
* \param koord* y
* \return test oszlop
* Pályakockákat és jutalmakat is rajzol.
*/
renderObject getBlockRenderObject(int x, int* y, int direction, int bound, bool sides[]) {
renderObject blockRenderObject;
if(isWithinTheMap((vector2D){x, *y}, (vector2D){1, 1}) && map[x][*y] == treasure) {
calculateSides(sides, x, *y);
sides[1] = true;
sides[2] = true;
sides[3] = true;
//jutalmat rajzol
blockRenderObject.position = (vector3D){x + 0.25, *y, 0.375};
blockRenderObject.size = treasureSize;
blockRenderObject.colors = treasureColor;
blockRenderObject.doesExist = true;
}
else if(isWithinTheMap((vector2D){x, *y}, (vector2D){1, 1}) && map[x][*y] == solid) {
char sidesSum = calculateSides(sides, x, *y);
int i = 1;
while(((direction == -1 && *y - i > bound) || (direction == 1 && *y + i < bound)) &&
map[x][*y + i * direction] == solid &&
sidesSum == calculateSides(sides, x, *y + i * direction))
i++;
sidesSum = calculateSides(sides, x, *y);
blockRenderObject.size = (vector3D){1, i, 1};
int newY = direction == 1 ? *y : *y - (i-1);
blockRenderObject.position = (vector3D){x, newY, 0};
blockRenderObject.colors = colorMap[x];
blockRenderObject.doesExist = true;
*y += direction * (i - 1);
}
else {
blockRenderObject.doesExist = false;
}
return blockRenderObject;
}

View file

@ -1,29 +0,0 @@
#ifndef MAP_H_INCLUDED
#define MAP_H_INCLUDED
#include "global.h"
#include "color.h"
#include "camera.h"
void setMapSize(int x, int y);
intVector2D getMapSize();
bool isPointWithinBox(vector2D point, vector2D boxPosition, vector2D boxSize);
bool isBoxOverlappingBox(vector2D box1Position, vector2D box1Size, vector2D box2Position, vector2D box2Size);
bool isWithinTheMap(vector2D point, vector2D size);
bool detectCollision(vector2D position, vector2D size, block collidableBlock, block** collidedWith);
void freeColorMap();
void generateColorMap();
void freeMap();
void createMap();
void generateMap(vector2D maxVelocity);
block** getMap();
int getHeightAtX(int x);
bool isInBlockSideways(int x, int lowestY, int uppermostY);
bool isInBlockPerpendicular(int leftmostX, int rightmostX, int y);
renderObject getBlockRenderObject(int x, int* y, int direction, int bound, bool sides[]);
#endif // MAP_H_INCLUDED

View file

@ -1,236 +0,0 @@
#include "menuDraw.h"
///A menüben lévő havazást kezeli, sokkal olcsóbb, mintha egy mapDrawt hívnánk.
static void drawMenuParticles(int deltaTime) {
int columns = getColumns();
int i;
for(i = 0; i < columns; i++)
drawParticles(i, 0, columns, deltaTime);
}
///A főmenüt rajzolja.
void drawMainMenu(int deltaTime) {
Uint16 menuOptions[][50] = {
L"Indítás",
L"Betöltés",
L"Beállítások",
L"Ranglista",
L"Leírás",
L"Kilépés",
L""
};
unicodePrintCentered(L"Platform játék", 50, 20, large);
int i;
for(i = 0; menuOptions[i][0] != '\0'; i++)
unicodePrintCentered(menuOptions[i], 50, menuStart + i * menuTav, isMouseOverRect(50, menuStart + menuTav*i, 15, menuTav / 2) ? smallBold : small);
drawMenuParticles(deltaTime);
}
///A "vissza" gombot rajzolja
static inline void printBack() {
unicodePrintCentered(L"Visszalépés", 50, menuStart + 5 * menuTav, isMouseOverRect(50, menuStart + menuTav*5, 15, menuTav / 2) ? smallBold : small);
}
///A beállítások menüpont rajzolása, a beállításokat a betöltött mentések is befolyásolják. Figyelni kell, hogy a belépés kattintását ne érzékelje csúszka kattintásnak.
void drawOptionsMenu(int deltaTime, int currentOptions[], bool click) {
Uint16 optionsMenuOptions[][50] = {
L"Világ szélessége",
L"Világ magassága",
L"Részecskék száma",
L"Gravitáció",
L"Ellenségek közti idő",
L""
};
int minOptions[] = { 10, 20, 0, 150, 2000};
int maxOptions[] = {3000, 3000, 50, 800, 20000};
int i = 0;
while(optionsMenuOptions[i][0] != '\0') {
unicodePrintCentered(optionsMenuOptions[i], 50, optionsStart + (i - 0.3) * optionsTav, tiny);
lineColor(
screen,
0.3 * screen -> w, (float) screen -> h / 100 * (optionsStart + i * optionsTav),
0.7 * screen -> w, (float) screen -> h / 100 * (optionsStart + i * optionsTav), 0xFFFFFFFF
);
filledCircleColor(screen, (float) screen -> w / 100 * (30 + 0.4 * 100 * (float)(currentOptions[i] - minOptions[i]) / (maxOptions[i] - minOptions[i])),
(float) screen -> h / 100 * (optionsStart + i * optionsTav), 20, 0xFFFFFFFF
);
aacircleColor(screen, (float) screen -> w / 100 * (30 + 0.4 * 100 * (float)(currentOptions[i] - minOptions[i]) / (maxOptions[i] - minOptions[i])),
(float) screen -> h / 100 * (optionsStart + i * optionsTav), 20, 0xFFFFFFFF
);
i++;
}
if(click)
for(i = 0; i < 5; i++)
if(isMouseOverRect(50, optionsStart + optionsTav * i, 50, optionsTav / 2)) {
//Ezek nem magic számok, csak egy kicsit.
currentOptions[i] = minOptions[i] + (int)((maxOptions[i] - minOptions[i]) * (getMousePosition().x - screen -> w * 0.3) / (screen -> w * 0.4) );
putInsideInterval(minOptions[i], &currentOptions[i], maxOptions[i]);
}
printBack();
drawMenuParticles(deltaTime);
}
///A játék leírását rajzolja meg.
void drawInfoMenu(int deltaTime) {
Uint16 info[][150] = {
L"Ebben a játékban pontokat szerezni és költeni",
L"is tudsz. Minden megtalált kék kockáért jár egy pont.",
L"Mozogni a WAD billentyűk, szóköz, vagy a nyilak",
L"segítségével tudsz. A bal egérgombbal egy pontért",
L"cserébe lőhetsz. A jobb egérgombbal lelassíthatod",
L"az időt, természetesen ez sincs ingyen. A repülő piros",
L"kockák az ellenségek, ha hozzád érnek a játéknak vége.",
L"A játékot az ESC megnyomásával szüneteltetheted.",
L""
};
int i = 0;
while(info[i][0] != '\0') {
unicodePrintCentered(info[i], 50, infoStart + i * infoTav, tiny);
i++;
}
printBack();
drawMenuParticles(deltaTime);
}
Uint16 names[topListItemsMax][100];
int points[topListItemsMax];
int namesIndex;
///Ha megtelt a toplista, rendezi, és az utolsó elemét lecseréli. A ranglista fájlt se kéne nagyon piszkálni a felhasználónak.
static void sortTopList() {
int i, j;
int tmpInt;
Uint16 tmpStr[100];
for(i = 0; i < namesIndex; i++) {
int maxIndex = i;
for(j = i + 1; j <= namesIndex; j++)
if(points[maxIndex] < points[j])
maxIndex = j;
tmpInt = points[i];
points[i] = points[maxIndex];
points[maxIndex] = tmpInt;
wcscpy(tmpStr, names[i]);
wcscpy(names[i], names[maxIndex]);
wcscpy(names[maxIndex], tmpStr);
}
}
///A toplista menü előtt meg kell hívni, hogy rendezett létező toplistát lássunk.
void loadTopList() {
FILE* topList = fopen("rang.lst", "r");
int i;
if(topList != NULL) {
namesIndex = 0;
while(true) {
i = 0;
do {
if(fwscanf(topList, L"%lc", &names[namesIndex][i]) == 0) goto end;
}
while(names[namesIndex][i++] != L',');
names[namesIndex][i] = 0;
fwscanf(topList, L"%d", &points[namesIndex]);
fwscanf(topList, L"\n");
if(namesIndex < topListItemsMax - 1) namesIndex++;
else sortTopList();
}
end:
sortTopList();
fclose(topList);
}
}
///Maga a toplista razoló függvény, az első pár helyet nagyobb betűvel rajzolja.
void drawTopList(int deltaTime) {
int i;
for(i = 0; i < namesIndex; i++) {
Uint16 nameWScore[600];
swprintf(nameWScore, L"%ls %d", names[i], points[i]);
unicodePrintCentered(nameWScore, 50, 20 + ((i == 0) ? 0 : (i + 1)*infoTav), (i == 0) ? large : (i == 1) ? small : tiny);
}
printBack();
drawMenuParticles(deltaTime);
}
///Ha a játékos szüneteltei a játékot(ESC) ezt fogja látni. Elég drága az átlátszó box, szóval csak óvatosan a méretével.
void drawPauseMenu(bool saved) {
Uint16 pauseOptions[][100] = {
L"Játék folytatása",
L"Pálya elmentése",
L"Mentés betöltése",
L"Kilépés a főmenübe",
L"",
L"Elmentve"
};
boxRGBA(screen, screen -> w * 0.3, screen -> h * 0.3, screen -> w * 0.7, screen -> h * 0.7, 0, 0, 0, 200);
int i;
for(i = 0; pauseOptions[i][0] != '\0'; i++) {
if(i == 1 && saved) unicodePrintCentered(pauseOptions[5], 50, menuStart - 5 + i * menuTav, isMouseOverRect(50, (menuStart - 5) + menuTav*i, 15, menuTav/2) ? smallBold : small);
else unicodePrintCentered(pauseOptions[i], 50, menuStart - 5 + i * menuTav, isMouseOverRect(50, (menuStart - 5) + menuTav*i, 15, menuTav/2) ? smallBold : small);
}
}
///Itt lehet mentett játékot betölteni, ha belépünk ebbe a menübe, a jelenlegi játékmenetünk elveszik.
void drawLoadMenu(int deltaTime) {
time_t ages[maxNOSaves];
int points[maxNOSaves];
getExistingSaveFiles(ages, points);
int i;
Uint16 timeStrings[][50] = {
L"másodperccel",
L"perccel",
L"órával",
L"nappal",
L"héttel",
L"hónappal",
L"évvel"
};
int timeConversion[] = {
1, 60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 60*60*24*365
};
int timeStringsIndex;
int k =0;
for(i = 0; i < maxNOSaves; i++) {
if(existingSaves[i]) {
int deltaRealTime = time(NULL) - ages[i];
for(timeStringsIndex = 0; timeStringsIndex < 6; timeStringsIndex++)
if(deltaRealTime < timeConversion[timeStringsIndex+1])
break;
int elapsedSaveTime = deltaRealTime / timeConversion[timeStringsIndex];
Uint16 save[200];
swprintf(save, L"%d %ls ezelőtti mentés, %d ponttal", elapsedSaveTime, timeStrings[timeStringsIndex], points[i]);
unicodePrintCentered(save, 50, loadStart + k * loadTav, isMouseOverRect(50, loadStart + loadTav*k, 25, 3) ? smallBold : small);
unicodePrintCentered(L"Törlés", 50, loadStart + k * loadTav + 5, isMouseOverRect(50, loadStart + loadTav*k + 5, 25, 2) ? smallBold : small);
k++;
}
}
printBack();
drawMenuParticles(deltaTime);
}

View file

@ -1,45 +0,0 @@
#ifndef MENU_H_INCLUDED
#define MENU_H_INCLUDED
#include <stdio.h>
#include <wchar.h>
#include "../global.h"
#include "../particles.h"
#include "../saves.h"
#include "../auxiliary/unicodePrint.h"
#include "../auxiliary/auxSDL.h"
#define topListItemsMax 8
#define menuStart (int) 42
#define menuTav (int) 9
#define optionsStart (int) 25
#define optionsTav (int) 13
#define infoStart (int) 20
#define infoTav (int) 7
#define loadStart (int) 10
#define loadTav (int) 15
typedef enum states {
mainMenu,
optionsMenu,
infoMenu,
pauseMenu,
game,
nameInput,
fading,
topListMenu,
loadMenu,
quit,
initGame,
initLoadedGame
} states;
void drawMainMenu(int deltaTime);
void drawOptionsMenu(int deltaTime, int currentOptions[], bool click);
void drawInfoMenu(int deltaTime);
void loadTopList();
void drawTopList(int deltaTime);
void drawPauseMenu(bool saved);
void drawLoadMenu(int deltaTime);
#endif // MENU_H_INCLUDED

View file

@ -1,245 +0,0 @@
#include "menuFSM.h"
int currentOptions[] = {2000, 2000, 3, 450, 6000};
int currentSpawnRate, fadeStartTime, elapsedTime = 0, nameIndex = 0, cursorBlinking = 0;
bool prematureClick, isDollying, saved = false;
Uint16 name[100] = {0};
FILE* topList;
states gameState = mainMenu; /**< Kezdőállapot. */
///Visszaadja a jelenlegi állapotot, a quit-hez kell a mainnek.
states getGameState() {
return gameState;
}
///Minden állapothoz tartozó kattintós eventet kezel, a beállítások menü miatt kell a prematureClick, hogy a belépést ne érzékelje beállításmódosításnak.
void handleClicks(bool* prematureClick) {
switch(gameState) {
case game:
if(isAlive()) shoot(getMousePosition());
break;
case mainMenu:
if(isMouseOverRect(50, menuStart, 15, menuTav / 2)) {
gameState = initGame;
}
else if(isMouseOverRect(50, menuStart + menuTav, 15, menuTav / 2)) {
gameState = loadMenu;
}
else if(isMouseOverRect(50, menuStart + menuTav*2, 15, menuTav / 2)) {
gameState = optionsMenu;
*prematureClick = true;
}
else if(isMouseOverRect(50, menuStart + menuTav*3, 15, menuTav / 2)) {
loadTopList();
gameState = topListMenu;
}
else if(isMouseOverRect(50, menuStart + menuTav*4, 15, menuTav / 2))
gameState = infoMenu;
else if(isMouseOverRect(50, menuStart + menuTav*5, 15, menuTav / 2)) {
gameState = quit;
}
break;
case pauseMenu:
if(isMouseOverRect(50, menuStart - 5, 15, menuTav/2)) {
gameState = game;
updateText(getPoints());
}
if(isMouseOverRect(50, menuStart - 5 + menuTav, 15, menuTav/2)) {
saveGame(currentOptions);
saved = true;
}
if(isMouseOverRect(50, menuStart - 5 + menuTav*2, 15, menuTav/2)) {
createMenuWorld();
gameState = loadMenu;
}
if(isMouseOverRect(50, menuStart - 5 + menuTav*3, 15, menuTav/2)) {
createMenuWorld();
gameState = mainMenu;
}
break;
case loadMenu:
if(isMouseOverRect(50, menuStart + menuTav * 5, 15, menuTav/2)) {
gameState = mainMenu;
break;
}
int i, j = 0;
for(i = 0; i < maxNOSaves; i++) {
if(existingSaves[i]) {
if(isMouseOverRect(50, loadStart + loadTav*j, 25, 3)) {
loadGame(currentOptions, i);
gameState = initLoadedGame;
}
else if(isMouseOverRect(50, loadStart + loadTav*j + 5, 25, 2)) {
deleteSave(i);
}
j++;
}
}
break;
case topListMenu:
case optionsMenu:
case infoMenu:
if(isMouseOverRect(50, menuStart + menuTav*5, 15, menuTav/2))
gameState = mainMenu;
break;
}
}
/** \brief Ez igenis egy kifejező függyvény név. Az állapothoz megfelelő műveletek elvéűgzése, és a kimenet létrehozása. Tehát ha a játék magától csinál valamit, azt itt csinálja.
* Ez az egész egy nagy állapotgép. Igazából a mainben is lehetne, de szerintem így szebben néz ki.
* \param egérgomb bal
* \param egérgomb jobb
* \param prematureClick a beállítás menünek
* A deltaTime a menüben kisebb, mint máshol, így egyszerűbben meg leeht oldani a hóesés lassítását. Amúgy az egész játék deltaTimera épült, ha
* c-ben megbízhatóan lehetne miliseces pontossággal lehetne időt mérni, akkor az FPS sokkal dinamikusabb lehetne (a jelenlegi fix 50-hez képest).
* Ilyet viszont nem lehet, de azért benne hagytam, egyrészről a menü miatt, másrészről az időlassítás miatt, meg esetleg azért is, mert később, más
* környezetben még lehet jól jön ez a megoldás.
*/
void doStuff(bool isLMBDown, bool isRMBDown, bool prematureClick) {
bool isInSlowMo = false;
int deltaTime = 20;
switch(gameState) {
case mainMenu:
drawMainMenu(deltaTime);
break;
case optionsMenu:
drawOptionsMenu(deltaTime, currentOptions, isLMBDown && !prematureClick);
break;
case infoMenu:
drawInfoMenu(deltaTime);
break;
case topListMenu:
drawTopList(deltaTime);
break;
case pauseMenu:
deltaTime = 0;
drawMap(deltaTime);
drawPauseMenu(saved);
break;
case loadMenu:
drawLoadMenu(deltaTime);
break;
case initGame:
createWorld(currentOptions);
case initLoadedGame:
currentSpawnRate = currentOptions[4];
isDollying = true;
elapsedTime = 0;
cameraDollyZ(0, true);
gameState = game;
break;
case game:
isInSlowMo = slowTime(isRMBDown);
deltaTime = (int)(1000.0 / FPS) * (isInSlowMo ? slowMoMult : 1);
elapsedTime += deltaTime;
if(isDollying) isDollying = cameraDollyZ(deltaTime, false);
drawMap(deltaTime);
if(isAlive()) {
movePlayer(keys, deltaTime);
moveFloaters(deltaTime);
drawPointsText();
if(elapsedTime > currentSpawnRate) {
createEnemy();
elapsedTime = 0;
if(currentSpawnRate > 1500) currentSpawnRate -= 10;
}
}
else {
fadeStartTime = elapsedTime;
name[0] = 0;
nameIndex = 0;
gameState = fading;
}
break;
case fading:
drawMap(0);
elapsedTime += deltaTime;
if((elapsedTime - fadeStartTime) / fadeTime >= 1)
gameState = nameInput;
boxRGBA(screen, 0, 0, screen -> w, screen -> h, 0, 0, 0, smaller(255, (int)(255 * (elapsedTime - fadeStartTime) / fadeTime)));
break;
case nameInput:
boxRGBA(screen, 0, 0, screen -> w, screen -> h, 0, 0, 0, 255);
cursorBlinking += deltaTime;
if(cursorBlinking > 1000) cursorBlinking = 0;
Uint16 loseText[300];
swprintf(loseText, L"%ls vesztett, a pontszáma %d.", cursorBlinking > 500 ? L"|" : L"", getPoints());
Uint16* endText = concatUnicode(name, loseText);
unicodePrintCentered(endText, 50, 50, small);
free(endText);
break;
}
if(isAlive()) drawMouse(isInSlowMo);
}
///A nameInputból returnnel és ESC-vel is ki lehet lépni, ezért kell ez a függvény rá, amit mindkét esetben meg lehet hívni.
static void escapeFromNameInput() {
if(gameState == nameInput) {
gameState = mainMenu;
setDie(false);
createMenuWorld();
topList = fopen("rang.lst", "a");
if(topList != NULL) {
fwprintf(topList, L"%ls,%d\n", name[0] == 0 ? L"Névtelen" : name, getPoints());
fclose(topList);
}
}
}
///Az ESC billenytű lenyomását kezeli.
void whatToDoOnEscape() {
switch(gameState) {
case pauseMenu:
gameState = game;
updateText(getPoints());
break;
case game:
if(isAlive()) {
gameState = pauseMenu;
saved = false;
}
break;
case mainMenu:
gameState = quit;
break;
case fading:
gameState = nameInput;
break;
case nameInput:
escapeFromNameInput();
gameState = mainMenu;
break;
default:
gameState = mainMenu;
}
}
///A szöveges bemenetet továbbítja egy Uint16-os stringbe.
void processTyping(SDL_Event event) {
if(gameState == nameInput) {
Uint16 chr = event.key.keysym.unicode;
if(strcmp(SDL_GetKeyName(chr), "backspace") == 0) name[--nameIndex] = 0;
else if(strcmp(SDL_GetKeyName(chr), "return") == 0) escapeFromNameInput();
else if(isValidChar(chr)) name[nameIndex++] = chr;
name[nameIndex] = 0;
}
}

Some files were not shown because too many files have changed in this diff Show more