This commit is contained in:
Filip Chochol 2026-05-10 10:44:22 +02:00
parent 8d0f3c5047
commit 700d025fe4
8 changed files with 1593 additions and 0 deletions

24
final/Makefile Normal file
View File

@ -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

321
final/README.md Normal file
View File

@ -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) |
---

814
final/game.c Normal file
View File

@ -0,0 +1,814 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#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;
}

90
final/game.h Normal file
View File

@ -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

6
final/main.c Normal file
View File

@ -0,0 +1,6 @@
#include "world.h"
#include "game.h"
int main() {
return start_world(init_game, game_event, destroy_game);
}

27
final/scores.txt Normal file
View File

@ -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 |
+-------------------------------------------------+

198
final/world.c Normal file
View File

@ -0,0 +1,198 @@
#include "world.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
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;
};

113
final/world.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef _WORLD_H_
#define _WORLD_H_
#include <curses.h>
/**
* 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