From 700d025fe40d3708ad1533f40df15e2bb7888be9 Mon Sep 17 00:00:00 2001 From: Filip Chochol Date: Sun, 10 May 2026 10:44:22 +0200 Subject: [PATCH] final --- final/Makefile | 24 ++ final/README.md | 321 +++++++++++++++++++ final/game.c | 814 +++++++++++++++++++++++++++++++++++++++++++++++ final/game.h | 90 ++++++ final/main.c | 6 + final/scores.txt | 27 ++ final/world.c | 198 ++++++++++++ final/world.h | 113 +++++++ 8 files changed, 1593 insertions(+) create mode 100644 final/Makefile create mode 100644 final/README.md create mode 100644 final/game.c create mode 100644 final/game.h create mode 100644 final/main.c create mode 100644 final/scores.txt create mode 100644 final/world.c create mode 100644 final/world.h diff --git a/final/Makefile b/final/Makefile new file mode 100644 index 0000000..183fdc9 --- /dev/null +++ b/final/Makefile @@ -0,0 +1,24 @@ +CC = gcc +CFLAGS = -Wall -Wextra -g +LIBS = -lncurses -lm + +TARGET = game + +all: $(TARGET) + +$(TARGET): main.o game.o world.o + $(CC) $(CFLAGS) -o $(TARGET) main.o game.o world.o $(LIBS) + +main.o: main.c + $(CC) $(CFLAGS) -c -o main.o main.c + +game.o: game.c + $(CC) $(CFLAGS) -c -o game.o game.c + +world.o: world.c + $(CC) $(CFLAGS) -c -o world.o world.c + +clean: + rm -f main.o game.o world.o $(TARGET) + +.PHONY: all clean diff --git a/final/README.md b/final/README.md new file mode 100644 index 0000000..d4a48ca --- /dev/null +++ b/final/README.md @@ -0,0 +1,321 @@ +# Arkanoid + +Autor: Filip Chochol +Predmet: Programovanie, TUKE FEI, 2026 + +--- + +## Popis projektu + +Projekt je terminálová hra Arkanoid napísaná v jazyku C s využitím +knižnice ncurses. Hra beží priamo v termináli bez akéhokoľvek +grafického rozhrania. Všetka grafika je tvorená ASCII znakmi +s farebnými atribútmi ktoré poskytuje ncurses. + +Hráč ovláda palku, odráža loptu a ničí tehly usporiadané do špirálového +vzoru. Hra obsahuje tri levely s rastúcou rýchlosťou, systém power-upov, +bossa s vlastnou logikou správania a automatické ukladanie skóre +do textového súboru po každom skončení hry. + +--- + +## Preklad a spustenie + + make + ./game + +Vymazanie skompilovaných súborov: + + make clean + +Hra sa prekladá kompilátorom gcc s prepínačmi -Wall -Wextra -g +a linkuje sa s knižnicami ncurses (-lncurses) a matematickou +knižnicou (-lm). + +--- + +## Architektura projektu + +Projekt je rozdelený do troch logických vrstiev: + + main.c vstupný bod, odovzdá riadenie knižnici world + world.c/h engine – event loop, vykreslovanie, vstup + game.c/h herná logika – všetky herné pravidlá a stav + +Táto separácia zabezpečuje že herná logika je úplne nezávislá +od konkrétnej implementácie vykreslovania. Keby sa zmenila +knižnica world, game.c by ostalo nedotknuté. + +--- + +## Datove struktury + +Celý stav hry je uložený v jednej štruktúre Hra ktorá sa +alokuje dynamicky pri štarte a uvoľňuje pri ukončení. + +### Hra – hlavna struktura + + typedef struct { + Palka palka; + Lopta lopty[MAX_LOPT]; + int pocet_lopt; + Tehla tehly[MAX_RIADKOV][MAX_STLPCOV]; + int zivoty; + int skore; + int zostatok_tehiel; + int level; + float rychlost; + StavHry stav; + int sirka, vyska; + char meno[32]; + PowerUp powerupy[2]; + int hit_counter; + int sirka_timer; + int cas_tikov; + int special_dostupny; + Boss boss; + } Hra; + +### Tehla + + typedef struct { + int ziva; + int farba; + char znak; + } Tehla; + +Tehly sú uložené v dvojrozmernom poli tehly[MAX_RIADKOV][MAX_STLPCOV] +teda 7x10 pozícií. Nie všetky sú obsadené – rozloženie určuje +statické pole spirala[7][10] kde 1 znamená tehla existuje, 0 prázdne. +Atribút ziva slúži na označenie zničených tehiel bez nutnosti +preusporiadavať pole. + +### Lopta + + typedef struct { + float x, y; + float dx, dy; + int ziva; + } Lopta; + +Pozícia a smer sú float aby pohyb bol plynulý a uhly presné. +Hra podporuje až MAX_LOPT = 16 lôpt súčasne v jednom statickom poli. +Pridávanie lôpt prechádza pole a hľadá prvý slot kde ziva == 0. + +### Palka + + typedef struct { + int x, y; + int sirka; + } Palka; + +### PowerUp + + typedef struct { + int ziva; + float x, y; + char znak; + short farba; + } PowerUp; + +### Boss + + typedef struct { + int ziva; + float x, y; + float dx; + int pada; + } Boss; + +### StavHry – stavovy automat + +Hra funguje ako stavový automat s týmito stavmi: + + STAV_UVOD úvodná obrazovka, zadávanie mena + STAV_CAKA lopta čaká na palke pred štartom + STAV_HRAJE hra beží + STAV_KONIEC hráč prehral + STAV_VYHRAL hráč vyhral všetky 3 levely + STAV_DALSI_LEVEL level dokončený, čaká na pokračovanie + +Každý stav určuje čo sa vykresluje a aké udalosti sa spracúvajú. + +--- + +## Herna logika + +### Event loop + +Knižnica world.c beží v nekonečnej slučke. Pri každom tiku +(každých RYCHLOST_HRY = 120 ms) zavolá funkciu game_event() +s informáciou o stlačenom klávesu alebo uplynutom čase. +game_event() spracuje vstup, zavolá pohni_lopty() a vykresli_hru(). + +### Fyzika lopty + +Každý tick sa pre každú živú loptu zavolá pohni_jednu_loptu(): + +1. Vypočíta sa nová pozícia: + nx = lopta.x + lopta.dx + ny = lopta.y + lopta.dy + +2. Kontrola narazu na steny – ak lopta dosiahne okraj, + príslušná zložka smeru sa neguje: + ak nx <= lavy_okraj -> dx = +|dx| + ak nx >= pravy_okraj -> dx = -|dx| + ak ny <= horny_okraj -> dy = +|dy| + +3. Kontrola odrazu od palky – ak lopta dosiahne riadok palky + a je v jej horizontálnom rozsahu: + offset = (nx - stred_palky) / (sirka_palky / 2) + dx = offset * rychlost + Čím ďalej od stredu, tým väčší horizontálny smer. + dy sa vždy nastaví na -rychlost (lopta ide hore). + +4. Kontrola kolízie s tehlami – porovnanie pozície lopty + s pozíciou každej živej tehly. Pri zhode sa tehla označí + ziva = 0, skóre sa zvýši a dy sa neguje (odraz). + +5. Ak ny >= dolna_hranica (pod palkou) – lopta zomrie, + pocet_lopt sa zníži. + +### Pohyb palky + +Palka sa pohybuje o 2 znaky na každé stlačenie klávesi. +Pri pohybe myšou sa x palky nastaví priamo na pozíciu kurzora +mínus polovica šírky palky, čo dáva plynulé sledovanie myši. +Pohyb je ohraničený na lavy_okraj až pravy_okraj – sirka_palky. + +### Boss logika + +Boss sa objaví po BOSS_CAS = 240 tikoch od začiatku levelu. + +Fáza 1 – horizontálny pohyb: + boss.x += boss.dx + pri náraze na okraj sa dx neguje + +Fáza 2 – pád (keď cas_tikov >= BOSS_CAS): + boss.pada = 1 + boss.y += 0.15 každý tick + ak boss trafí tehlu – tehla zomrie, skore -= 50 + ak boss.y >= palka.y – okamžite STAV_KONIEC + +Zásah bossa loptou: + boss.ziva = 0, skore += 200 + vystrelí 5 lôpt rôznymi smermi (vystrel_z_bossa) + +### Power-upy + +hit_counter počíta každú zničenú tehlu. + hit_counter % 3 == 0 -> power-up $ (extra lopty) + hit_counter % 12 == 0 -> power-up W (siroka palka) + +Power-upy padajú dolu (y += 0.5 každý tick). +Pri chytení palkou sa aktivuje efekt: + $ – pridajú sa 2 nové lopty s mierne odchýleným smerom + W – palka.sirka = PALKA_SIRKA_MAX, sirka_timer = 300 tickov + po 300 tickoch sa palka vráti na PALKA_SIRKA_MIN + +### Specialny vystrel + +Stlačením E sa vypália 2 lopty pod uhlom od stredu palky. +Dostupnosť sa sleduje premennou special_dostupny (1/0). +Po použití sa nastaví na 0 a už sa nedá obnoviť. + +### Skore + +Za každú zničenú tehlu: 10 * cislo_levelu bodov. +Za zničenie bossa: +200 bodov. +Za tehlu zničenú bossom: -50 bodov. + +### Ukladanie skore + +Po každom skončení hry (STAV_KONIEC aj STAV_VYHRAL) sa zavolá +funkcia uloz_skore() ktorá otvorí scores.txt v append móde ("a") +a pripíše formátovaný záznam s týmito údajmi: + - meno hráča + - dosiahnuté skóre + - level na ktorom hra skončila + - čas od začiatku levelu (prepočítaný z cas_tikov) + - výsledok (VYHRAL / GAME OVER) + - dátum a čas (zo systémovej funkcie localtime) + +Súbor sa nevymazáva – každá hra sa pripíše na koniec +takže ostáva kompletná história všetkých hier. + +--- + +## Vykreslovanie + +Celá hra sa vykresluje funkciou vykresli_hru() ktorá sa volá +po každej udalosti. Funkcia najprv vymaže obrazovku (clear_screen) +a potom podľa aktuálneho stavu hry vykreslí príslušné prvky. + +Súradnicový systém: [0,0] je ľavý horný roh obrazovky. +X rastie doprava, Y rastie smerom nadol. + +Hracia plocha má dva rámčeky: + vonkajší – ohraničuje celú obrazovku (žltý) + vnútorný – ohraničuje hernú plochu (biely) + +Rozmery hracej plochy sa počítajú dynamicky podľa aktuálnej +veľkosti terminálu (h->sirka, h->vyska) čo zabezpečuje +že hra funguje správne aj pri zmene veľkosti okna. + +Pozície tehiel: + tx = tehla_zacatek_x + stlpec * 3 + ty = tehla_zac_y + riadok +Každá tehla zaberá 3 znaky: [X] kde X je znak z mena hráča. + +--- + +## Ovládanie + +| Kláves | Akcia | +|----------------------|------------------------------| +| A / sipka vlavo | Pohyb palky vlavo | +| D / sipka vpravo | Pohyb palky vpravo | +| Pohyb mysou | Palka sleduje kurzor | +| Medzernik | Spustenie lopty | +| Lavy klik mysou | Spustenie lopty | +| E | Specialny vystrel | +| Q | Ukoncenie hry | + +--- + +## Bodovanie + +| Udalost | Body | +|--------------------------|-------| +| Znicenie tehly – level 1 | +10 | +| Znicenie tehly – level 2 | +20 | +| Znicenie tehly – level 3 | +30 | +| Znicenie bossa | +200 | +| Boss znici tehlu | -50 | + +--- + +## Levely + +| Level | Rychlost lopty | Body za tehlu | +|-------|----------------|---------------| +| 1 | 0.7 | 10 | +| 2 | 0.9 | 20 | +| 3 | 1.1 | 30 | + +--- + +## Subory projektu + +| Subor | Obsah | +|------------|------------------------------------------------| +| main.c | Vstupny bod, spusta hernu slucku cez world | +| game.c | Cela herna logika – pohyb, kolizie, kreslenie | +| game.h | Struktury, konstanty, deklaracie funkcii | +| world.c | Kniźnica pre vykreslovanie v terminali | +| world.h | Rozhranie kniźnice world | +| Makefile | Preklad projektu prikazom make | +| README.md | Tato dokumentacia | +| scores.txt | Ukladanie skore (vytvori sa automaticky) | + +--- diff --git a/final/game.c b/final/game.c new file mode 100644 index 0000000..43653d4 --- /dev/null +++ b/final/game.c @@ -0,0 +1,814 @@ +#include +#include +#include +#include +#include +#include "game.h" +#include "world.h" + +static short farby_riadkov_level1[7] = { + COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, + COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_WHITE, +}; + +static short farby_riadkov_level2[7] = { + COLOR_CYAN, COLOR_GREEN, COLOR_WHITE, + COLOR_YELLOW, COLOR_RED, COLOR_MAGENTA, COLOR_BLUE, +}; + +static short farby_riadkov_level3[7] = { + COLOR_YELLOW, COLOR_WHITE, COLOR_RED, + COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN, COLOR_BLUE, +}; + +static int von_lav(Hra* h) { (void)h; return 0; } +static int von_prav(Hra* h) { return h->sirka - 1; } +static int von_hor(Hra* h) { (void)h; return 0; } +static int von_dno(Hra* h) { return h->vyska - 2; } + +static int lavy_okraj(Hra* h) { return h->sirka / 4; } +static int pravy_okraj(Hra* h) { return h->sirka - h->sirka / 4; } +static int horny_okraj(Hra* h) { (void)h; return 3; } +static int dolna_hranica(Hra* h) { return h->palka.y + 1; } + +static int tehla_zacatek_x(Hra* h) { + return (h->sirka - MAX_STLPCOV * 3) / 2; +} + +static int tehla_zac_y(Hra* h) { + return horny_okraj(h) + 2; +} + +static int palka_default_y(Hra* h) { + return tehla_zac_y(h) + MAX_RIADKOV + 12; +} + +static void inicializuj_boss(Hra* h) { + h->boss.ziva = 1; + h->boss.x = (float)(h->sirka / 2); + h->boss.y = 1.0f; + h->boss.dx = 0.5f; + h->boss.pada = 0; +} + +void inicializuj_tehly(Hra* h) { + h->zostatok_tehiel = 0; + int dlzka = h->meno_dlzka > 0 ? h->meno_dlzka : 4; + + static const int spirala[7][10] = { + {1,1,1,1,1,1,1,1,1,1}, + {1,0,0,0,0,0,0,0,0,1}, + {1,0,1,1,1,1,1,1,0,1}, + {1,0,1,0,0,0,0,1,0,0}, + {1,0,1,0,1,1,0,1,0,1}, + {1,0,1,0,1,0,0,1,0,1}, + {1,0,0,0,1,1,1,1,0,1}, + }; + + short* farby; + switch (h->level) { + case 2: farby = farby_riadkov_level2; break; + case 3: farby = farby_riadkov_level3; break; + default: farby = farby_riadkov_level1; break; + } + + for (int r = 0; r < MAX_RIADKOV; r++) { + for (int s = 0; s < MAX_STLPCOV; s++) { + if (spirala[r][s]) { + h->tehly[r][s].ziva = 1; + h->tehly[r][s].farba = farby[r % 7]; + h->tehly[r][s].znak = h->meno[(r + s) % dlzka]; + h->zostatok_tehiel++; + } else { + h->tehly[r][s].ziva = 0; + } + } + } +} + +static void nastav_level(Hra* h) { + switch (h->level) { + case 1: h->riadkov = 7; h->rychlost = 0.7f; break; + case 2: h->riadkov = 7; h->rychlost = 0.9f; break; + case 3: h->riadkov = 7; h->rychlost = 1.1f; break; + default: h->riadkov = 7; h->rychlost = 0.7f; break; + } +} + +static void pridaj_loptu(Hra* h, float x, float y, float dx, float dy) { + for (int i = 0; i < MAX_LOPT; i++) { + if (!h->lopty[i].ziva) { + h->lopty[i].x = x; + h->lopty[i].y = y; + h->lopty[i].dx = dx; + h->lopty[i].dy = dy; + h->lopty[i].ziva = 1; + h->pocet_lopt++; + return; + } + } +} + +static void inicializuj_lopty(Hra* h) { + for (int i = 0; i < MAX_LOPT; i++) h->lopty[i].ziva = 0; + h->pocet_lopt = 0; + pridaj_loptu(h, + (float)(h->palka.x + h->palka.sirka / 2), + (float)(h->palka.y - 1), + h->rychlost, -h->rychlost); +} + +static void vystrel_z_bossa(Hra* h, float bstred, float bposy) { + float smery[5][2] = { + { 0.0f, h->rychlost }, + {-h->rychlost, h->rychlost }, + { h->rychlost, h->rychlost }, + {-h->rychlost * 0.7f, -h->rychlost * 0.7f }, + { h->rychlost * 0.7f, -h->rychlost * 0.7f }, + }; + for (int s = 0; s < 5; s++) { + if (h->pocet_lopt < MAX_LOPT) + pridaj_loptu(h, bstred, bposy, smery[s][0], smery[s][1]); + } +} + +void pohni_palku(Hra* h, int smer) { + int novy_x = h->palka.x + smer * 2; + int lav = lavy_okraj(h); + int prav = pravy_okraj(h) - h->palka.sirka + 1; + if (novy_x < lav) novy_x = lav; + if (novy_x > prav) novy_x = prav; + h->palka.x = novy_x; + if (h->stav == STAV_CAKA) + h->lopty[0].x = (float)(h->palka.x + h->palka.sirka / 2); +} + +static void vystrel_special(Hra* h) { + if (!h->special_dostupny) return; + if (h->stav != STAV_HRAJE) return; + h->special_dostupny = 0; + + float stred = (float)(h->palka.x + h->palka.sirka / 2); + float y = (float)(h->palka.y - 1); + + if (h->pocet_lopt < MAX_LOPT) + pridaj_loptu(h, stred - 1, y, -h->rychlost * 0.5f, -h->rychlost); + if (h->pocet_lopt < MAX_LOPT) + pridaj_loptu(h, stred + 1, y, h->rychlost * 0.5f, -h->rychlost); +} + +static void uloz_skore(Hra* h) { + FILE* f = fopen("scores.txt", "a"); + if (!f) return; + + time_t t = time(NULL); + struct tm* tm_info = localtime(&t); + char datum[32]; + strftime(datum, sizeof(datum), "%d.%m.%Y %H:%M:%S", tm_info); + + int sekundy = h->cas_tikov * RYCHLOST_HRY / 1000; + int min = sekundy / 60; + int sek = sekundy % 60; + + const char* vysledok; + if (h->stav == STAV_VYHRAL) vysledok = "VYHRAL"; + else if (h->stav == STAV_KONIEC) vysledok = "GAME OVER"; + else vysledok = "KONIEC"; + + fprintf(f, "+-------------------------------------------------+\n"); + fprintf(f, "| Hrac : %-36s|\n", h->meno); + fprintf(f, "| Skore : %-36d|\n", h->skore); + fprintf(f, "| Level : %-36d|\n", h->level); + fprintf(f, "| Cas : %02d:%02d%-32s|\n", min, sek, ""); + fprintf(f, "| Vysledok: %-36s|\n", vysledok); + fprintf(f, "| Datum : %-36s|\n", datum); + fprintf(f, "+-------------------------------------------------+\n\n"); + + fclose(f); +} + +static void pohni_boss(Hra* h) { + if (!h->boss.ziva) return; + + if (h->cas_tikov >= BOSS_CAS && !h->boss.pada) { + h->boss.pada = 1; + } + + if (h->boss.pada) { + h->boss.y += 0.15f; + + int tx0 = tehla_zacatek_x(h); + int ty0 = tehla_zac_y(h); + int by = (int)h->boss.y; + int bx = (int)h->boss.x; + + for (int r = 0; r < MAX_RIADKOV; r++) { + for (int s = 0; s < MAX_STLPCOV; s++) { + if (!h->tehly[r][s].ziva) continue; + int tx = tx0 + s * 3; + int ty = ty0 + r; + if (by == ty && bx + 4 >= tx && bx <= tx + 2) { + h->tehly[r][s].ziva = 0; + h->zostatok_tehiel--; + h->skore -= 50; + } + } + } + + if ((int)h->boss.y >= h->palka.y) { + h->stav = STAV_KONIEC; + uloz_skore(h); + return; + } + } else { + h->boss.x += h->boss.dx; + if (h->boss.x <= (float)lavy_okraj(h)) { + h->boss.x = (float)lavy_okraj(h); + h->boss.dx = fabsf(h->boss.dx); + } + if (h->boss.x + 4 >= (float)pravy_okraj(h)) { + h->boss.x = (float)(pravy_okraj(h) - 4); + h->boss.dx = -fabsf(h->boss.dx); + } + } + + for (int i = 0; i < MAX_LOPT; i++) { + if (!h->lopty[i].ziva) continue; + int bx = (int)h->boss.x; + int by = (int)h->boss.y; + int lx = (int)h->lopty[i].x; + int ly = (int)h->lopty[i].y; + if (ly == by && lx >= bx && lx <= bx + 4) { + h->boss.ziva = 0; + h->skore += 200; + h->lopty[i].dy = fabsf(h->lopty[i].dy); + vystrel_z_bossa(h, h->boss.x + 2.0f, h->boss.y); + break; + } + } +} + +static void pohni_powerupy(Hra* h) { + for (int i = 0; i < 2; i++) { + if (!h->powerupy[i].ziva) continue; + + h->powerupy[i].y += 0.5f; + + if ((int)h->powerupy[i].y >= h->palka.y && + (int)h->powerupy[i].x >= h->palka.x && + (int)h->powerupy[i].x < h->palka.x + h->palka.sirka) { + h->powerupy[i].ziva = 0; + + if (i == 0) { + int idx = h->posledna_lopta; + if (h->lopty[idx].ziva) { + if (h->pocet_lopt < MAX_LOPT) + pridaj_loptu(h, h->lopty[idx].x, h->lopty[idx].y, + -h->lopty[idx].dx + 0.2f, -fabsf(h->lopty[idx].dy)); + if (h->pocet_lopt < MAX_LOPT) + pridaj_loptu(h, h->lopty[idx].x, h->lopty[idx].y, + h->lopty[idx].dx - 0.2f, -fabsf(h->lopty[idx].dy)); + } + } else { + h->palka.sirka = PALKA_SIRKA_MAX; + h->sirka_timer = 300; + } + continue; + } + + if ((int)h->powerupy[i].y >= dolna_hranica(h)) + h->powerupy[i].ziva = 0; + } +} + +static int pohni_jednu_loptu(Hra* h, int idx) { + Lopta* l = &h->lopty[idx]; + if (!l->ziva) return 0; + + float nx = l->x + l->dx; + float ny = l->y + l->dy; + + int lav = lavy_okraj(h); + int prav = pravy_okraj(h); + int hor = horny_okraj(h); + + if (nx <= lav) { nx = (float)lav; l->dx = fabsf(l->dx); } + if (nx >= prav) { nx = (float)prav; l->dx = -fabsf(l->dx); } + if (ny <= hor) { ny = (float)hor; l->dy = fabsf(l->dy); } + + if (h->boss.ziva) { + int bx = (int)h->boss.x; + int by = (int)h->boss.y; + if ((int)ny == by && (int)nx >= bx && (int)nx <= bx + 4) { + h->boss.ziva = 0; + h->skore += 200; + l->dy = fabsf(l->dy); + vystrel_z_bossa(h, h->boss.x + 2.0f, h->boss.y); + } + } + + int py = h->palka.y; + if ((int)ny >= py - 1 && (int)ny <= py) { + if ((int)nx >= h->palka.x && (int)nx < h->palka.x + h->palka.sirka) { + float stred = (float)(h->palka.x) + h->palka.sirka / 2.0f; + float offset = (nx - stred) / (h->palka.sirka / 2.0f); + l->dx = offset * h->rychlost; + if (l->dx > h->rychlost) l->dx = h->rychlost; + if (l->dx < -h->rychlost) l->dx = -h->rychlost; + if (l->dx == 0.0f) l->dx = 0.3f; + l->dy = -h->rychlost; + ny = (float)(py - 1); + } + } + + int tx0 = tehla_zacatek_x(h); + int ty0 = tehla_zac_y(h); + + for (int r = 0; r < MAX_RIADKOV && h->zostatok_tehiel > 0; r++) { + for (int s = 0; s < MAX_STLPCOV; s++) { + if (!h->tehly[r][s].ziva) continue; + int tx = tx0 + s * 3; + int ty = ty0 + r; + if ((int)ny == ty && (int)nx >= tx && (int)nx < tx + 3) { + h->tehly[r][s].ziva = 0; + h->zostatok_tehiel--; + h->skore += 10 * h->level; + l->dy = -l->dy; + + h->posledna_lopta = idx; + h->hit_counter++; + + if (h->hit_counter % 3 == 0 && h->hit_counter % 12 != 0 && !h->powerupy[0].ziva) { + h->powerupy[0].ziva = 1; + h->powerupy[0].x = nx; + h->powerupy[0].y = ny; + h->powerupy[0].znak = '$'; + h->powerupy[0].farba = COLOR_YELLOW; + } + + if (h->hit_counter % 12 == 0 && !h->powerupy[1].ziva) { + h->powerupy[1].ziva = 1; + h->powerupy[1].x = nx; + h->powerupy[1].y = ny; + h->powerupy[1].znak = 'W'; + h->powerupy[1].farba = COLOR_CYAN; + } + + if (h->zostatok_tehiel == 0) { + h->stav = (h->level < 3) ? STAV_DALSI_LEVEL : STAV_VYHRAL; + if (h->stav == STAV_VYHRAL) uloz_skore(h); + return 0; + } + break; + } + } + } + + if ((int)ny >= dolna_hranica(h)) { + l->ziva = 0; + h->pocet_lopt--; + return 1; + } + + l->x = nx; + l->y = ny; + return 0; +} + +void pohni_lopty(Hra* h) { + if (h->stav != STAV_HRAJE) return; + + h->cas_tikov++; + + pohni_boss(h); + if (h->stav == STAV_KONIEC) return; + + if (h->sirka_timer > 0) { + h->sirka_timer--; + if (h->sirka_timer == 0) + h->palka.sirka = PALKA_SIRKA_MIN; + } + + pohni_powerupy(h); + + for (int i = 0; i < MAX_LOPT; i++) { + if (h->lopty[i].ziva) pohni_jednu_loptu(h, i); + } + + if (h->pocet_lopt <= 0) { + h->zivoty--; + if (h->zivoty <= 0) { + h->stav = STAV_KONIEC; + uloz_skore(h); + } else { + h->palka.sirka = PALKA_SIRKA_MIN; + h->stav = STAV_CAKA; + inicializuj_lopty(h); + } + } +} + +static void vykresli_uvod(Hra* h) { + int sx = h->sirka / 2; + int sy = h->vyska / 2 - 12; + + static const char* logo[] = { + " _ ____ _ __ _ _ _ ___ ___ ____", + " / \\ | _ \\| |/ / / \\ | \\ | |/ _ \\|_ _| _ \\", + " / _ \\ | |_) | ' / / _ \\ | \\| | | | || | | | |", + " / ___ \\| _ <| . \\ / ___ \\| |\\ | |_| || | |_| |", + "/_/ \\_\\_| \\_\\_|\\_\\/_/ \\_\\_| \\_|\\___/|___|____/", + }; + static const short logo_farby[] = { + COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_GREEN, COLOR_CYAN, + }; + + int logo_x = sx - 25; + if (logo_x < 0) logo_x = 0; + + for (int r = 0; r < 5; r++) { + const char* riadok = logo[r]; + int dl = (int)strlen(riadok); + for (int c = 0; c < dl; c++) { + if (logo_x + c < h->sirka) + set_color_cell(riadok[c], logo_x + c, sy + r, logo_farby[r], COLOR_BLACK); + } + } + + int b1y = sy + 6; + set_message("* . * . * . * . * . *", sx - 16, b1y); + + int b2y = b1y + 2; + set_color_cell('[', sx - 14, b2y, COLOR_YELLOW, COLOR_BLACK); + set_message(" Zadaj meno a stlac ENTER ", sx - 13, b2y); + set_color_cell(']', sx + 13, b2y, COLOR_YELLOW, COLOR_BLACK); + + int b3y = b2y + 2; + char riadok_mena[40]; + snprintf(riadok_mena, sizeof(riadok_mena), "Meno: %s_", h->meno); + int meno_dl = (int)strlen(riadok_mena); + int meno_x = sx - meno_dl / 2; + set_color_cell('>', meno_x - 1, b3y, COLOR_CYAN, COLOR_BLACK); + set_message(riadok_mena, meno_x, b3y); + + int b4y = b3y + 2; + static const short uk_farby[] = { + COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, + COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_WHITE + }; + static const char* uk_znaky[] = {"[R]","[M]","[Y]","[G]","[C]","[B]","[W]"}; + int uk_x = sx - 10; + for (int i = 0; i < 7; i++) { + const char* z = uk_znaky[i]; + for (int c = 0; c < 3; c++) + set_color_cell(z[c], uk_x + i*3 + c, b4y, uk_farby[i], COLOR_BLACK); + } + + int lpy = b4y + 2; + set_color_cell('O', sx - 12, lpy, COLOR_WHITE, COLOR_BLACK); + set_color_cell('o', sx - 8, lpy, COLOR_WHITE, COLOR_BLACK); + set_color_cell('.', sx - 5, lpy, COLOR_WHITE, COLOR_BLACK); + set_color_cell('.', sx + 5, lpy, COLOR_WHITE, COLOR_BLACK); + set_color_cell('o', sx + 8, lpy, COLOR_WHITE, COLOR_BLACK); + set_color_cell('O', sx + 12, lpy, COLOR_WHITE, COLOR_BLACK); + + int pky = lpy + 1; + set_color_cell('<', sx - 10, pky, COLOR_CYAN, COLOR_BLACK); + for (int i = -9; i <= 9; i++) + set_color_cell('=', sx + i, pky, COLOR_CYAN, COLOR_BLACK); + set_color_cell('>', sx + 10, pky, COLOR_CYAN, COLOR_BLACK); + + set_message("* . * . * . * . * . *", sx - 16, pky + 2); +} + +void vykresli_hru(Hra* h) { + clear_screen(); + + if (h->stav == STAV_UVOD) { + vykresli_uvod(h); + return; + } + + if (h->stav == STAV_DALSI_LEVEL) { + int sx = h->sirka / 2; + int sy = h->vyska / 2 - 2; + char msg[40]; + snprintf(msg, sizeof(msg), "*** LEVEL %d DOKONCENY! ***", h->level); + set_message(msg, sx - 14, sy); + snprintf(msg, sizeof(msg), " Skore: %d", h->skore); + set_message(msg, sx - 14, sy + 1); + set_message(" Stlac MEDZERNIK na dalsi level", sx - 16, sy + 3); + return; + } + + int vl = von_lav(h), vp = von_prav(h), vh = von_hor(h), vd = von_dno(h); + set_color_cell('+', vl, vh, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('+', vp, vh, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('+', vl, vd, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('+', vp, vd, COLOR_YELLOW, COLOR_BLACK); + for (int x = vl + 1; x < vp; x++) { + set_color_cell('=', x, vh, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('=', x, vd, COLOR_YELLOW, COLOR_BLACK); + } + for (int y = vh + 1; y < vd; y++) { + set_color_cell('|', vl, y, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('|', vp, y, COLOR_YELLOW, COLOR_BLACK); + } + + int il = lavy_okraj(h) - 1; + int ip = pravy_okraj(h) + 1; + int ih = horny_okraj(h) - 1; + int id = h->palka.y + 2; + set_color_cell('+', il, ih, COLOR_WHITE, COLOR_BLACK); + set_color_cell('+', ip, ih, COLOR_WHITE, COLOR_BLACK); + set_color_cell('+', il, id, COLOR_WHITE, COLOR_BLACK); + set_color_cell('+', ip, id, COLOR_WHITE, COLOR_BLACK); + for (int x = il + 1; x < ip; x++) { + set_color_cell('-', x, ih, COLOR_WHITE, COLOR_BLACK); + set_color_cell('-', x, id, COLOR_WHITE, COLOR_BLACK); + } + for (int y = ih + 1; y < id; y++) { + set_color_cell('|', il, y, COLOR_WHITE, COLOR_BLACK); + set_color_cell('|', ip, y, COLOR_WHITE, COLOR_BLACK); + } + + if (h->boss.ziva) { + short boss_farba = h->boss.pada ? COLOR_RED : COLOR_MAGENTA; + int bx = (int)h->boss.x; + int by = (int)h->boss.y; + set_color_cell('[', bx, by, COLOR_WHITE, boss_farba); + set_color_cell('!', bx + 1, by, COLOR_WHITE, boss_farba); + set_color_cell('!', bx + 2, by, COLOR_WHITE, boss_farba); + set_color_cell('!', bx + 3, by, COLOR_WHITE, boss_farba); + set_color_cell(']', bx + 4, by, COLOR_WHITE, boss_farba); + if (h->boss.pada) { + int sx = h->sirka / 2; + set_message("!!! BOSS PADA !!!", sx - 8, vh + 1); + } + } else { + set_message("BOSS ZNICENY! +200", lavy_okraj(h), vh + 1); + } + + int tx0 = tehla_zacatek_x(h); + int ty0 = tehla_zac_y(h); + for (int r = 0; r < MAX_RIADKOV; r++) { + for (int s = 0; s < MAX_STLPCOV; s++) { + if (!h->tehly[r][s].ziva) continue; + int tx = tx0 + s * 3; + int ty = ty0 + r; + short fc = h->tehly[r][s].farba; + set_color_cell('[', tx, ty, COLOR_WHITE, fc); + set_color_cell(h->tehly[r][s].znak, tx + 1, ty, COLOR_WHITE, fc); + set_color_cell(']', tx + 2, ty, COLOR_WHITE, fc); + } + } + + for (int i = 0; i < h->palka.sirka; i++) + set_color_cell('=', h->palka.x + i, h->palka.y, COLOR_CYAN, COLOR_BLACK); + set_color_cell('<', h->palka.x, h->palka.y, COLOR_CYAN, COLOR_BLACK); + set_color_cell('>', h->palka.x + h->palka.sirka - 1, h->palka.y, COLOR_CYAN, COLOR_BLACK); + + for (int i = 0; i < 2; i++) { + if (h->powerupy[i].ziva) + set_color_cell(h->powerupy[i].znak, + (int)h->powerupy[i].x, (int)h->powerupy[i].y, + h->powerupy[i].farba, COLOR_BLACK); + } + + if (h->stav != STAV_KONIEC && h->stav != STAV_VYHRAL) { + for (int i = 0; i < MAX_LOPT; i++) { + if (h->lopty[i].ziva) + set_color_cell('O', (int)h->lopty[i].x, (int)h->lopty[i].y, + COLOR_WHITE, COLOR_BLACK); + } + } + + char skore_msg[32]; + snprintf(skore_msg, sizeof(skore_msg), "Skore: %d", h->skore); + set_message(skore_msg, vl + 1, vh + 1); + + if (h->special_dostupny) + set_message("[E=SPECIAL]", vl + 1, vh + 2); + else + set_message("[E=POUZITY]", vl + 1, vh + 2); + + if (h->sirka_timer > 0) + set_message("[WIDE]", vl + 1, vh + 3); + + int sekundy = h->cas_tikov * RYCHLOST_HRY / 1000; + int min = sekundy / 60; + int sek = sekundy % 60; + char timer[16]; + snprintf(timer, sizeof(timer), "%02d:%02d", min, sek); + set_message(timer, vp - 5, vh + 1); + + if (h->boss.ziva && !h->boss.pada) { + int zostava = (BOSS_CAS - h->cas_tikov) / (1000 / RYCHLOST_HRY); + if (zostava < 0) zostava = 0; + char boss_msg[20]; + snprintf(boss_msg, sizeof(boss_msg), "BOSS:%ds", zostava); + set_message(boss_msg, vp - 8, vh + 2); + } + + char hud[120]; + snprintf(hud, sizeof(hud), + " %s | Level: %d | Zivoty: %d | Lopty: %d | A/D=pohyb E=special Q=koniec", + h->meno, h->level, h->zivoty, h->pocet_lopt); + set_message(hud, 0, h->vyska - 1); + + if (h->stav == STAV_CAKA) { + int sx = h->sirka / 2; + int tehly_koniec = tehla_zac_y(h) + MAX_RIADKOV; + int volny_stred = (tehly_koniec + h->palka.y) / 2; + set_message("** STLAC MEDZERNIK / KLIKNI **", sx - 15, volny_stred); + + } else if (h->stav == STAV_KONIEC || h->stav == STAV_VYHRAL) { + + int lav = lavy_okraj(h); + int prav = pravy_okraj(h); + int tehly_koniec = tehla_zac_y(h) + MAX_RIADKOV; + int volny_stred = (tehly_koniec + h->palka.y) / 2; + + int sx2 = (lav + prav) / 2; + int cy = volny_stred - 5; + if (cy < tehly_koniec + 1) cy = tehly_koniec + 1; + + for (int x = lav + 1; x < prav; x++) + set_color_cell('=', x, cy, COLOR_CYAN, COLOR_BLACK); + + if (h->stav == STAV_VYHRAL) { + char t[] = "** VYHRAL SI! **"; + set_color_cell('*', sx2 - 8, cy + 1, COLOR_YELLOW, COLOR_BLACK); + set_color_cell('*', sx2 + 7, cy + 1, COLOR_YELLOW, COLOR_BLACK); + set_message(t, sx2 - (int)strlen(t) / 2, cy + 1); + char s[] = "Gratulujeme, hrdina!"; + set_message(s, sx2 - (int)strlen(s) / 2, cy + 2); + } else { + char t[] = "** GAME OVER **"; + set_color_cell('X', sx2 - 8, cy + 1, COLOR_RED, COLOR_BLACK); + set_color_cell('X', sx2 + 7, cy + 1, COLOR_RED, COLOR_BLACK); + set_message(t, sx2 - (int)strlen(t) / 2, cy + 1); + char s[] = "Nevzdavaj sa, bojovnik!"; + set_message(s, sx2 - (int)strlen(s) / 2, cy + 2); + } + + char riadok[40]; + snprintf(riadok, sizeof(riadok), "Skore: %d Level: %d", h->skore, h->level); + set_message(riadok, sx2 - (int)strlen(riadok) / 2, cy + 3); + + for (int x = lav + 1; x < prav; x++) + set_color_cell('-', x, cy + 4, COLOR_CYAN, COLOR_BLACK); + + char inf[] = "Skore zapisane do:"; + set_message(inf, sx2 - (int)strlen(inf) / 2, cy + 5); + + char sub[] = "> scores.txt <"; + set_color_cell('>', sx2 - (int)strlen(sub) / 2, cy + 6, COLOR_YELLOW, COLOR_BLACK); + set_message(sub, sx2 - (int)strlen(sub) / 2, cy + 6); + set_color_cell('<', sx2 + (int)strlen(sub) / 2 - 1, cy + 6, COLOR_YELLOW, COLOR_BLACK); + + char kde[] = "(priecinok hry)"; + set_message(kde, sx2 - (int)strlen(kde) / 2, cy + 7); + + for (int x = lav + 1; x < prav; x++) + set_color_cell('=', x, cy + 8, COLOR_CYAN, COLOR_BLACK); + + char kon[] = "Stlac Q pre ukoncenie"; + set_message(kon, sx2 - (int)strlen(kon) / 2, cy + 9); + } +} + +void* init_game() { + Hra* h = (Hra*)malloc(sizeof(Hra)); + if (!h) return NULL; + + h->sirka = 60; + h->vyska = 30; + h->zivoty = ZIVOTY; + h->skore = 0; + h->level = 1; + h->stav = STAV_UVOD; + h->meno[0] = '\0'; + h->meno_dlzka = 0; + h->hit_counter = 0; + h->sirka_timer = 0; + h->posledna_lopta = 0; + h->cas_tikov = 0; + h->special_dostupny = 1; + h->powerupy[0].ziva = 0; + h->powerupy[1].ziva = 0; + + nastav_level(h); + + h->palka.sirka = PALKA_SIRKA_MIN; + h->palka.y = palka_default_y(h); + h->palka.x = h->sirka / 2 - PALKA_SIRKA_MIN / 2; + + inicializuj_boss(h); + inicializuj_lopty(h); + inicializuj_tehly(h); + game_speed(RYCHLOST_HRY); + return h; +} + +void destroy_game(void* hra) { + free(hra); +} + +int game_event(struct event* udalost, void* hra) { + Hra* h = (Hra*)hra; + + if (udalost->width > 0) h->sirka = udalost->width; + if (udalost->height > 0) h->vyska = udalost->height; + + if (h->stav == STAV_UVOD) { + if (udalost->type == EVENT_KEY) { + int k = udalost->key; + if (k == '\n' || k == KEY_ENTER) { + if (h->meno_dlzka == 0) { + strcpy(h->meno, "Hrac"); + h->meno_dlzka = 4; + } + nastav_level(h); + inicializuj_tehly(h); + h->palka.sirka = PALKA_SIRKA_MIN; + h->palka.y = palka_default_y(h); + h->palka.x = h->sirka / 2 - PALKA_SIRKA_MIN / 2; + h->hit_counter = 0; + h->sirka_timer = 0; + h->posledna_lopta = 0; + h->cas_tikov = 0; + h->special_dostupny = 1; + h->powerupy[0].ziva = 0; + h->powerupy[1].ziva = 0; + inicializuj_boss(h); + inicializuj_lopty(h); + h->stav = STAV_CAKA; + } else if (k == KEY_BACKSPACE && h->meno_dlzka > 0) { + h->meno[--h->meno_dlzka] = '\0'; + } else if (k >= 32 && k < 127 && h->meno_dlzka < 31) { + h->meno[h->meno_dlzka++] = (char)k; + h->meno[h->meno_dlzka] = '\0'; + } + } + vykresli_hru(h); + return 0; + } + + if (h->stav == STAV_DALSI_LEVEL) { + int next = 0; + if (udalost->type == EVENT_KEY && udalost->key == ' ') next = 1; + if (udalost->type == EVENT_MOUSE && udalost->mouse_left) next = 1; + if (next) { + h->level++; + nastav_level(h); + inicializuj_tehly(h); + h->palka.sirka = PALKA_SIRKA_MIN; + h->palka.y = palka_default_y(h); + h->palka.x = h->sirka / 2 - PALKA_SIRKA_MIN / 2; + h->hit_counter = 0; + h->sirka_timer = 0; + h->posledna_lopta = 0; + h->cas_tikov = 0; + h->special_dostupny = 1; + h->powerupy[0].ziva = 0; + h->powerupy[1].ziva = 0; + inicializuj_boss(h); + inicializuj_lopty(h); + h->stav = STAV_CAKA; + } + vykresli_hru(h); + return 0; + } + + if (udalost->type == EVENT_MOUSE) { + int ciel_x = udalost->mouse_x - h->palka.sirka / 2; + int lav = lavy_okraj(h); + int prav = pravy_okraj(h) - h->palka.sirka + 1; + if (ciel_x < lav) ciel_x = lav; + if (ciel_x > prav) ciel_x = prav; + h->palka.x = ciel_x; + if (h->stav == STAV_CAKA) + h->lopty[0].x = (float)(h->palka.x + h->palka.sirka / 2); + if (udalost->mouse_left && h->stav == STAV_CAKA) + h->stav = STAV_HRAJE; + } + + if (udalost->type == EVENT_KEY) { + int k = udalost->key; + if (k == 'q' || k == 'Q') return 1; + if (k == 'e' || k == 'E') { + vystrel_special(h); + } else if (k == KEY_LEFT || k == 'a' || k == 'A') { + pohni_palku(h, -1); + } else if (k == KEY_RIGHT || k == 'd' || k == 'D') { + pohni_palku(h, +1); + } else if (k == ' ' && h->stav == STAV_CAKA) { + h->stav = STAV_HRAJE; + } + } + + pohni_lopty(h); + vykresli_hru(h); + return 0; +} diff --git a/final/game.h b/final/game.h new file mode 100644 index 0000000..5f04441 --- /dev/null +++ b/final/game.h @@ -0,0 +1,90 @@ +#ifndef _GAME_H_ +#define _GAME_H_ + +#include "world.h" + +#define MAX_RIADKOV 7 +#define MAX_STLPCOV 10 +#define ZIVOTY 3 +#define RYCHLOST_HRY 120 +#define PALKA_SIRKA_MIN 7 +#define PALKA_SIRKA_MAX 14 +#define PALKA_ROW_OD_DNA 2 +#define MAX_LOPT 16 +#define BOSS_CAS 240 + +typedef enum { + STAV_UVOD, + STAV_CAKA, + STAV_HRAJE, + STAV_KONIEC, + STAV_VYHRAL, + STAV_DALSI_LEVEL, +} StavHry; + +typedef struct { + int x, y; + int sirka; +} Palka; + +typedef struct { + float x, y; + float dx, dy; + int ziva; +} Lopta; + +typedef struct { + int ziva; + int farba; + char znak; +} Tehla; + +typedef struct { + int ziva; + float x, y; + char znak; + short farba; +} PowerUp; + +typedef struct { + int ziva; + float x, y; + float dx; + int pada; +} Boss; + +typedef struct { + Palka palka; + Lopta lopty[MAX_LOPT]; + int pocet_lopt; + Tehla tehly[MAX_RIADKOV][MAX_STLPCOV]; + int zivoty; + int skore; + int zostatok_tehiel; + int level; + int riadkov; + float rychlost; + StavHry stav; + int sirka; + int vyska; + char meno[32]; + int meno_dlzka; + PowerUp powerupy[2]; + int hit_counter; + int sirka_timer; + int posledna_lopta; + int cas_tikov; + int special_dostupny; + Boss boss; +} Hra; + +void* init_game(); +int game_event(struct event* udalost, void* hra); +void destroy_game(void* hra); + +void pohni_palku(Hra* h, int smer); +void pohni_lopty(Hra* h); +void inicializuj_tehly(Hra* h); +void vykresli_hru(Hra* h); + +#endif diff --git a/final/main.c b/final/main.c new file mode 100644 index 0000000..1e0abf5 --- /dev/null +++ b/final/main.c @@ -0,0 +1,6 @@ +#include "world.h" +#include "game.h" + +int main() { + return start_world(init_game, game_event, destroy_game); +} diff --git a/final/scores.txt b/final/scores.txt new file mode 100644 index 0000000..ff64e89 --- /dev/null +++ b/final/scores.txt @@ -0,0 +1,27 @@ ++-------------------------------------------------+ +| Hrac : Filip | +| Skore : 20 | +| Level : 1 | +| Cas : 00:23 | +| Vysledok: GAME OVER | +| Datum : 09.05.2026 17:37:02 | ++-------------------------------------------------+ + ++-------------------------------------------------+ +| Hrac : Filip | +| Skore : 90 | +| Level : 1 | +| Cas : 00:15 | +| Vysledok: GAME OVER | +| Datum : 09.05.2026 17:43:40 | ++-------------------------------------------------+ + ++-------------------------------------------------+ +| Hrac : Filip | +| Skore : 620 | +| Level : 2 | +| Cas : 00:03 | +| Vysledok: GAME OVER | +| Datum : 10.05.2026 09:22:53 | ++-------------------------------------------------+ + diff --git a/final/world.c b/final/world.c new file mode 100644 index 0000000..a45c109 --- /dev/null +++ b/final/world.c @@ -0,0 +1,198 @@ +#include "world.h" +#include +#include +#include +#include +#include + +int TIMEOUT; + +void abort_game(const char* message){ + endwin(); + puts(message); + exit(1); +} + +void check_bounds(const char* source,int x, int y){ + char msg[200]; + if (x < 0 || x >= COLS){ + sprintf(msg,"%s:: width %d is out of bounds (0,%d)",source,x,COLS); + abort_game(msg); + } + if (y < 0 || y >= LINES){ + sprintf(msg,"%s:: height %d is out of bounds (0,%d)",source,y,LINES); + abort_game(msg); + } +} + +void clear_screen(){ + // Clear screen + mvaddch(0,0,' '); + int screenchars = LINES*COLS; + for (int j = 1; j < screenchars;j++ ){ + addch(' '); + } +} + +void game_speed(int value){ + if (value < 0){ + abort_game("world_seed:: cannot be negative\n"); + } + TIMEOUT =value; +} + +void set_message(const char* message,int x,int y) { + int l = strlen(message); + for (int i = 0; i < l; i++){ + check_bounds("set_message",x+i,y); + set_cell(message[i],x+i,y); + } +} + +void assert_message(int event,const char* message){ + if (event == 0){ + abort_game(message); + } +} + + +void set_cell(int character,int x,int y) { + check_bounds("set_cell",x,y); + set_color_cell(character,x,y,COLOR_WHITE,COLOR_BLACK); +} + +void set_color_cell(int character,int x,int y,short front_color,short back_color){ + check_bounds("set_color_cell",x,y); + if (has_colors()){ + int pair = COLOR_COUNT * front_color + back_color; + attron(COLOR_PAIR(pair)); + mvaddch(y,x,character); + attroff(COLOR_PAIR(pair)); + } + else{ + mvaddch(y,x,character); + } +} + +int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void*)){ + srand(time(NULL)); + int r = 1; + // Speed global variable + TIMEOUT = 100; + if (initscr() == NULL){ + // TODO Which Error? + puts("Curses Error."); + return -1; + } + noecho(); // Nevypisuj vstup na obrazovku + cbreak(); // Zabudni starý vstup + nodelay(stdscr,TRUE); // Nečakaj na stlačenie + keypad(stdscr,TRUE); // Aktivuje šípky + curs_set(FALSE); // Neviditeľný kurzor + /* Get all the mouse events */ + mousemask(ALL_MOUSE_EVENTS, NULL); + MEVENT mouse_event; + if (has_colors()){ // Zistenie či terminál podporuje farby + start_color(); + for (int i = 0; i < COLOR_COUNT;i++){ + for (int j = 0; j < COLOR_COUNT;j++){ + init_pair(i * COLOR_COUNT + j, i,j); + } + } + } + else { + puts("No colors!\n"); + } + void* game = NULL; + if (init_game != NULL){ + game = init_game(); + assert_message(game != NULL,"init_game:: should return non null pointer"); + } + timeout(TIMEOUT); + // Initial step + struct event event; + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.type = EVENT_START; + clock_t start_time = clock(); + clock_t last_timeout = start_time; + clock_t next_timeout = last_timeout + TIMEOUT; + event.time_ms = start_time; + // Start event + r = world_event(&event,game); + refresh(); + while (!r) { + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.key = getch(); + // No key was pressed + if (event.key == ERR){ + event.type = EVENT_TIMEOUT; + last_timeout = clock(); + next_timeout = last_timeout + TIMEOUT; + } + // Mouse event + else if (event.key == KEY_MOUSE ){ + event.type = EVENT_MOUSE; + if(getmouse(&mouse_event) == OK){ + event.mouse_x = mouse_event.x; + event.mouse_y = mouse_event.y; + if(mouse_event.bstate & BUTTON1_PRESSED){ + event.mouse_left = 1; + } + if(mouse_event.bstate & BUTTON2_PRESSED){ + event.mouse_middle = 1; + } + if(mouse_event.bstate & BUTTON3_PRESSED){ + event.mouse_right = 1; + } + } + } + else if (event.key == KEY_RESIZE) { + event.type = EVENT_RESIZE; + } + else{ + event.type = EVENT_KEY; + if (event.key == 27){ + int k = getch(); + if (k == -1){ + // Esc Was pressed + event.type = EVENT_ESC; + } + else { + // Alt was pressed + event.key = k; + event.alt_key = 1; + } + } + } + // Draw new world + event.time_ms = clock(); + r = world_event(&event,game); + refresh(); + event.time_ms = clock(); + // set new timeout + int nt = next_timeout - event.time_ms; + //printf("%d\n",nt); + if (nt > 0){ + timeout(nt); + } + else { + timeout(TIMEOUT); + next_timeout = event.time_ms + TIMEOUT; + } + } + memset(&event,0,sizeof(struct event)); + event.height = LINES; + event.width = COLS; + event.type = EVENT_END; + event.time_ms = clock(); + world_event(&event,game); + if (destroy_game != NULL){ + destroy_game(game); + } + endwin(); + return r; +}; diff --git a/final/world.h b/final/world.h new file mode 100644 index 0000000..73be057 --- /dev/null +++ b/final/world.h @@ -0,0 +1,113 @@ +#ifndef _WORLD_H_ +#define _WORLD_H_ + +#include + +/** + * World represented as a rectangular matrix of colorful characters. + * + * Point [0,0] is displayed the upper left corner of the screen. + * + */ + +enum event_type { + EVENT_START, + EVENT_TIMEOUT, + EVENT_KEY, + EVENT_MOUSE, + EVENT_RESIZE, + EVENT_ESC, + EVENT_END, +}; + +struct event { + /** + * Last width of the screen. + */ + int width; + /** + * Last height of the screen. + */ + int height; + /** + * Last pressed key or Curses event. + * + * Special event values: + * ERR if timeout, + * KEY_RESIZE if screen resize + * KEY_EVENT, other event, + * KEY_MOUSE, mouse clicked + * + * Key values: + * + * ' ' Space + * KEY_DOWN Arrow down + * KEY_UP Arrow up + * KEY_LEFT Arrow left + * KEY_RIGHT Arrow right + * KEY_A1 Upper left of keypad + * KEY_A3 Upper right of keypad + * KEY_B2 Center of keypad + * KEY_C1 Lower left of keypad + * KEY_C3 Lower right of keypad + * + * KEY_ENTER + * KEY_BACKSPACE + */ + int key; + int alt_key; + enum event_type type; + int mouse_x; + int mouse_y; + int mouse_left; + int mouse_right; + int mouse_middle; + long int time_ms; +}; + +/** + * Sets cell to a state. + * @param event + * @param x coordinate of cell + * @param y coordinate of cell + * @param new state of the cell + */ +void set_cell(int character,int x,int y); + +/** + * COLOR_BLACK 0 + * COLOR_RED 1 + * COLOR_GREEN 2 + * COLOR_YELLOW 3 + * COLOR_BLUE 4 + * COLOR_MAGENTA 5 + * COLOR_CYAN 6 + * COLOR_WHITE 7 + */ + +#define COLOR_COUNT 8 + +void set_color_cell(int character,int x,int y,short front_color,short back_color); + + +/** + * + * @param event + * @param number of commandline arguments + * @param init_world + * @param destroy_world + * + * void init_world(struct event* w); + * Initializes user state. + * Free user state. + * @param event + */ + +int start_world(void* (*init_game)(),int (*world_event)(struct event* event,void* game),void (*destroy_game)(void* game)); + +void game_speed(int value); + +void set_message(const char* message,int x,int y); +void clear_screen(); + +#endif