aaaauto
This commit is contained in:
parent
d4d57c0344
commit
9a4e7c44d7
22
du8/Makefile
Normal file
22
du8/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -pedantic -std=c99
|
||||
LIBS = -lncurses -lm
|
||||
TARGET = asteroids
|
||||
OBJS = main.o game.o world.o
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
|
||||
|
||||
main.o: main.c game.h world.h
|
||||
$(CC) $(CFLAGS) -c main.c
|
||||
|
||||
game.o: game.c game.h world.h
|
||||
$(CC) $(CFLAGS) -c game.c
|
||||
|
||||
world.o: world.c world.h
|
||||
$(CC) $(CFLAGS) -c world.c
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(OBJS)
|
106
du8/README.md
Normal file
106
du8/README.md
Normal file
@ -0,0 +1,106 @@
|
||||
=======================================
|
||||
Dokumentácia hry: Ultimate Asteroids
|
||||
=======================================
|
||||
|
||||
|
||||
Predstavenie Hry
|
||||
------------------
|
||||
|
||||
Ultimate Asteroids je dynamická 2D vesmírna strieľačka, ktorá berie to najlepšie z klasických arkádových hier ako Space Invaders. Ako pilot osamelej vesmírnej lode je vašou úlohou prežiť nápor neustále prichádzajúcich vĺn nepriateľov a nakoniec sa postaviť tvárou v tvár mocnej Materskej lodi vo finálnom súboji.
|
||||
|
||||
Hra ponúka svieži pohľad na známy žáner a obsahuje:
|
||||
|
||||
* Menu s voľbou obtiažnosti, ktorá priamo ovplyvňuje rýchlosť a agresivitu nepriateľov.
|
||||
* Štyri unikátne typy nepriateľov s odlišným správaním a odolnosťou.
|
||||
* Dynamický combo systém, ktorý vás odmení za rýchle a presné zničenie protivníkov.
|
||||
* Taktické bariéry, ktoré dočasne blokujú strely a menia dynamiku boja.
|
||||
* Epický súboj s finálnym bossom, ktorý preverí všetky vaše schopnosti svojimi unikátnymi útočnými vzormi.
|
||||
|
||||
|
||||
Preklad a Spustenie
|
||||
---------------------
|
||||
|
||||
Hra je napísaná v jazyku C a pre zobrazenie a interakciu využíva knižnicu 'ncurses' (prostredníctvom dodanej knižnice 'world').
|
||||
|
||||
Požiadavky:
|
||||
* Prekladač 'gcc'
|
||||
* Nástroj 'make'
|
||||
* Knižnica 'ncurses' (na serveri sigma je štandardne dostupná)
|
||||
|
||||
Postup prekladu:
|
||||
1. Umiestnite všetky súbory ('Makefile', 'main.c', 'game.c', 'game.h', 'world.c', 'world.h') do jedného adresára.
|
||||
2. V termináli prejdite do tohto adresára.
|
||||
3. Spustite príkaz 'make'. Tento príkaz automaticky skompiluje všetky súbory a vytvorí finálny spustiteľný súbor s názvom 'asteroids'.
|
||||
|
||||
|
||||
Spustenie hry:
|
||||
Hru spustíte z terminálu nasledujúcim príkazom:
|
||||
|
||||
./asteroids
|
||||
|
||||
|
||||
Návod na Hranie
|
||||
-----------------
|
||||
|
||||
Cieľ hry:
|
||||
Vaším hlavným cieľom je prežiť čo najdlhšie, nahrať čo najvyššie skóre a po uplynutí približne jednej minúty poraziť finálneho bossa – Materskú loď.
|
||||
|
||||
Ovládanie:
|
||||
* Šípka vľavo: Pohyb lode doľava.
|
||||
* Šípka vpravo: Pohyb lode doprava.
|
||||
* Medzerník: Vystrelenie projektilu.
|
||||
* q: Okamžité ukončenie hry.
|
||||
|
||||
Herné mechaniky:
|
||||
|
||||
Životy: Začínate s 5 životmi. Život stratíte, ak sa vaša loď zrazí s nepriateľom, alebo ak nepriateľ preletí cez spodný okraj obrazovky a unikne vám.
|
||||
|
||||
Nepriatelia: V hre narazíte na 4 typy nepriateľov:
|
||||
* 'v' Asteroid: Základný nepriateľ. Letí priamo dole a nepredstavuje veľkú hrozbu osamote.
|
||||
* 'S' Stíhač: Nevyspytateľný protivník, ktorý sa pohybuje v sínusových vlnách, čo ho robí ťažkým cieľom.
|
||||
* 'O' Tank: Odolný nepriateľ, ktorý vydrží až dva zásahy, kým ho zničíte.
|
||||
* '*' Mína: Pasívna hrozba, ktorá po určitom čase sama zmizne.
|
||||
|
||||
Bariéry <===>: V náhodných intervaloch sa na obrazovke objavujú dočasné bariéry. Slúžia ako štít, ktorý blokuje vaše strely, ale pozor – nepriatelia cez ne môžu bez problémov prejsť!
|
||||
|
||||
Combo systém: Za každého nepriateľa zničeného v rýchlom slede sa vám zvyšuje násobič skóre (Combo: x2, x3, ...). Ako špeciálny bonus, po dosiahnutí COMBA x10, vaša loď získa dočasný ŠTÍT, ktorý ju ochráni pred jedným zásahom alebo koliziou.
|
||||
|
||||
Boss fight: Po uplynutí stanoveného času sa na obrazovke zobrazí varovanie a začne súboj s Materskou loďou. Boss sa pohybuje zo strany na stranu a strieda dva útočné vzory: streľbu a vypúšťanie nepriateľov.
|
||||
|
||||
Koniec hry:
|
||||
* Prehra: Hra končí, keď počet vašich životov klesne na nulu.
|
||||
* Výhra: Hra končí vaším triumfom po úspešnom zničení Materskej lode.
|
||||
|
||||
|
||||
Popis Implementácie
|
||||
----------------------
|
||||
|
||||
|
||||
Najdôležitejšie štruktúry ('game.h'):
|
||||
* 'StavHry': Centrálna štruktúra, ktorá drží kompletný stav celej hry – obsahuje polia pre všetky dynamické objekty, informácie o hráčovi a bossovi, a tiež premenné riadiace stav hry ('stav_programu'), skóre, životy a rôzne časovače.
|
||||
* 'Nepriatel': Reprezentuje jedného nepriateľa. Okrem pozície obsahuje aj 'typ', 'hp' (životy) a 'farbu'.
|
||||
* 'Boss': Unikátna štruktúra pre finálneho bossa, ktorá si pamätá jeho životy, aktuálny útočný vzor ('aktualny_vzor') a časovače.
|
||||
|
||||
Kľúčové funkcie:
|
||||
* 'main()' ('main.c'): Vstupný bod programu. Jeho jediná úloha je odovzdať riadenie knižnici 'world' a poskytnúť jej tri kľúčové "callback" funkcie.
|
||||
* 'world_event()' ('main.c'): Centrálny dispečer udalostí, ktorý reaguje na stlačenie klávesy alebo uplynutie času a volá ďalšie funkcie.
|
||||
* 'aktualizuj_stav()' ('game.c'): "Srdce" hernej slučky. Volá sa v pravidelných intervaloch a zabezpečuje všetku hernú logiku.
|
||||
* 'vykresli_stav()' ('game.c'): Zodpovedá za kompletné vykreslenie všetkého na obrazovku.
|
||||
|
||||
|
||||
|
||||
|
||||
Modifikácia knižnice 'world'
|
||||
------------------------------
|
||||
|
||||
Pôvodná knižnica 'world' mala definície funkcií umiestnené priamo v hlavičkovom súbore 'world.h', čo spôsobovalo chyby pri kompilácii ("redefinition of function").
|
||||
|
||||
Riešenie:
|
||||
Knižnica bola vylepšená tak, aby hlavičkový súbor 'world.h' obsahoval iba DEKLARÁCIE funkcií a zdrojový súbor 'world.c' obsahoval ich IMPLEMENTÁCIU .Týmto sa zabezpečila bezchybná kompilácia.
|
||||
|
||||
|
||||
Použité Zdroje
|
||||
-----------------
|
||||
|
||||
* Základná knižnica 'world': https://github.com/hladek/world (poskytnutá v rámci zadania)
|
||||
* Inšpirácia: žáner "shoot 'em up" ako Space Invaders a Galaga.
|
BIN
du8/asteroids
Executable file
BIN
du8/asteroids
Executable file
Binary file not shown.
563
du8/game.c
Normal file
563
du8/game.c
Normal file
@ -0,0 +1,563 @@
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "game.h"
|
||||
|
||||
// --- Deklaracie funkcii ---
|
||||
void spusti_hru(StavHry *stav);
|
||||
void vytvor_nepriatela(StavHry *stav);
|
||||
void vytvor_barieru(StavHry *stav);
|
||||
void vystrel(StavHry *stav);
|
||||
void inicializuj_bossa(StavHry *stav);
|
||||
void spracuj_logiku_bossa(StavHry *stav);
|
||||
|
||||
// Grafika pre bossa
|
||||
const char *boss_logo[] = { "<[O|O|O]>", "//---\\\\" };
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* 1. CAST : VSTUP A ZACIATOCNE FUNKCIE/PARAMETRE ATD.
|
||||
**********************************************************/
|
||||
|
||||
/*
|
||||
* Spracovanie vstupu - stlacenie klaves
|
||||
* stav -> Ukazovatel na aktualny stav hry
|
||||
* klavesa -> cod stlacenej klavesy
|
||||
*/
|
||||
void spracuj_vstup(StavHry *stav, int klavesa)
|
||||
{
|
||||
// ovladanie v hlavnom menu
|
||||
if (stav->stav_programu == V_MENU) {
|
||||
switch (klavesa) {
|
||||
case KEY_UP:
|
||||
stav->menu_vyber--;
|
||||
if (stav->menu_vyber < 0) stav->menu_vyber = 2;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
stav->menu_vyber++;
|
||||
if (stav->menu_vyber > 2) stav->menu_vyber = 0;
|
||||
break;
|
||||
case KEY_ENTER:
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ' ':
|
||||
stav->obtiaznost = (Obtiaznost)stav->menu_vyber;
|
||||
spusti_hru(stav);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ovladanie pocas hry a BOSS fightu
|
||||
else if (stav->stav_programu == HRAJE_SA || stav->stav_programu == BOSS_SOUBOJ) {
|
||||
switch (klavesa) {
|
||||
case KEY_LEFT:
|
||||
stav->hrac.data.x -= 2;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
stav->hrac.data.x += 2;
|
||||
break;
|
||||
case ' ':
|
||||
vystrel(stav);
|
||||
break;
|
||||
}
|
||||
// teleport na druhu sstranu
|
||||
if (stav->hrac.data.x < 0) {
|
||||
stav->hrac.data.x = stav->sirka_obrazovky - 1;
|
||||
}
|
||||
if (stav->hrac.data.x >= stav->sirka_obrazovky) {
|
||||
stav->hrac.data.x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rozbehnutie zakladnych veci pri prvom spusteni
|
||||
*/
|
||||
void inicializuj_hru(StavHry *stav, int sirka, int vyska) {
|
||||
stav->sirka_obrazovky = sirka;
|
||||
stav->vyska_obrazovky = vyska;
|
||||
stav->stav_programu = V_MENU;
|
||||
stav->menu_vyber = 0;
|
||||
game_speed(80); // rychlost hry (menej je rychlejsie)
|
||||
}
|
||||
|
||||
/*
|
||||
* Spusti novu hru a resetuje udaje zo starej
|
||||
*/
|
||||
void spusti_hru(StavHry *stav) {
|
||||
// Ulozenie nastaveni, ktore sa maju zachovat (sirka, vyska, obtiaznost)
|
||||
int sirka = stav->sirka_obrazovky;
|
||||
int vyska = stav->vyska_obrazovky;
|
||||
Obtiaznost obt = stav->obtiaznost;
|
||||
|
||||
// odstranenie starych udajov z memory
|
||||
memset(stav, 0, sizeof(StavHry));
|
||||
|
||||
// zakladne veci pre hru
|
||||
stav->sirka_obrazovky = sirka;
|
||||
stav->vyska_obrazovky = vyska;
|
||||
stav->obtiaznost = obt;
|
||||
stav->stav_programu = HRAJE_SA;
|
||||
stav->zivoty = 5;
|
||||
|
||||
// startovna pozicia mojej lode
|
||||
stav->hrac.data.x = stav->sirka_obrazovky / 2.0;
|
||||
stav->hrac.data.y = stav->vyska_obrazovky - 2;
|
||||
stav->hrac.data.aktivny = 1;
|
||||
|
||||
// spawner barier
|
||||
switch (stav->obtiaznost) {
|
||||
case LAHKA: stav->bariera_spawn_casovac = 60; break;
|
||||
case STREDNA: stav->bariera_spawn_casovac = 50; break;
|
||||
case TAZKA: stav->bariera_spawn_casovac = 40; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* 2. CAST : VYTVARANIE LODI
|
||||
**********************************************************/
|
||||
|
||||
//vytvaranie enemy ldoi
|
||||
void vytvor_nepriatela(StavHry *stav) {
|
||||
for (int i = 0; i < MAX_NEPRIATELIA; i++) {
|
||||
if (!stav->nepriatelia[i].data.aktivny) {
|
||||
Nepriatel *n = &stav->nepriatelia[i];
|
||||
n->data.aktivny = 1;
|
||||
n->data.x = rand() % stav->sirka_obrazovky;
|
||||
n->data.y = 0;
|
||||
n->pociatocne_x = n->data.x; // Pre sinusovy pohyb
|
||||
|
||||
// randomne urcovanie typu lode
|
||||
int typ_roll = rand() % 100;
|
||||
if (typ_roll < 60) n->typ = TYP_ASTEROID;
|
||||
else if (typ_roll < 85) n->typ = TYP_STIHAC;
|
||||
else if (typ_roll < 95) n->typ = TYP_TANK;
|
||||
else n->typ = TYP_MINA;
|
||||
|
||||
// Nastavenie vlastnosti podla typu
|
||||
switch (n->typ) {
|
||||
case TYP_ASTEROID: n->hp = 1; n->farba = COLOR_WHITE; break;
|
||||
case TYP_STIHAC: n->hp = 2; n->farba = COLOR_CYAN; break;
|
||||
case TYP_TANK: n->hp = 5; n->farba = COLOR_GREEN; break;
|
||||
case TYP_MINA: n->hp = 3; n->farba = COLOR_RED; n->casovac = 50; break;
|
||||
}
|
||||
|
||||
// rychlost podla obtiaznosti
|
||||
float rychlost_mod = 1.0f;
|
||||
if (stav->obtiaznost == STREDNA) rychlost_mod = 1.2f;
|
||||
if (stav->obtiaznost == TAZKA) rychlost_mod = 1.6f;
|
||||
n->data.rychlost_y = (0.08 + (rand() % 10) / 80.0) * rychlost_mod;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Vytvaranie barier
|
||||
*/
|
||||
void vytvor_barieru(StavHry *stav) {
|
||||
for (int i = 0; i < MAX_BARIERY; i++) {
|
||||
if (!stav->bariery[i].data.aktivny) {
|
||||
Bariera *b = &stav->bariery[i];
|
||||
b->data.aktivny = 1;
|
||||
b->data.x = rand() % (stav->sirka_obrazovky - 5); // 5 je sirka baqriery, toto nemenit
|
||||
b->data.y = rand() % (stav->vyska_obrazovky - 10) + 5; // na y v strede
|
||||
b->zivotnost = 25;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Vytvaranie strely
|
||||
*/
|
||||
void vystrel(StavHry *stav) {
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (!stav->strely_hrac[i].data.aktivny) {
|
||||
Strela *s = &stav->strely_hrac[i];
|
||||
s->data.aktivny = 1;
|
||||
s->data.x = stav->hrac.data.x;
|
||||
s->data.y = stav->hrac.data.y - 1;
|
||||
s->data.rychlost_y = -1; // ide hore :)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* spawn bossa a priprava na bossfight
|
||||
*/
|
||||
void inicializuj_bossa(StavHry *stav) {
|
||||
stav->stav_programu = BOSS_SOUBOJ;
|
||||
// despawn enemy lodi
|
||||
for (int i = 0; i < MAX_NEPRIATELIA; i++) {
|
||||
stav->nepriatelia[i].data.aktivny = 0;
|
||||
}
|
||||
Boss *b = &stav->boss;
|
||||
b->aktivny = 1;
|
||||
b->hp = BOSS_HP;
|
||||
b->data.x = stav->sirka_obrazovky / 2;
|
||||
b->data.y = 5;
|
||||
b->data.rychlost_x = 0.15;
|
||||
b->smer_pohybu = 1; // 1 = doprava, -1 = dolava
|
||||
b->aktualny_vzor = VZOR_SWEEP;
|
||||
b->casovac_fazy = 400; // zmena utocneho vzoru
|
||||
b->casovac_utoku = 30; // rychlost strielanie (mensie je rychlejsie)
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* 3. CAST : AKTUALIZACIA LOGIKY
|
||||
**********************************************************/
|
||||
|
||||
/**
|
||||
* Aktualizovanie pozicie vsetkych veci na obrazovke
|
||||
*/
|
||||
void pohybuj_objektami(StavHry *stav)
|
||||
{
|
||||
// Pohyb nepriatelov
|
||||
for (int i = 0; i < MAX_NEPRIATELIA; i++) {
|
||||
if (stav->nepriatelia[i].data.aktivny) {
|
||||
Nepriatel *n = &stav->nepriatelia[i];
|
||||
|
||||
// vsetky enemy lode letia dole
|
||||
n->data.y += n->data.rychlost_y;
|
||||
|
||||
// Stihac ('S') - uhybne manevre
|
||||
if (n->typ == TYP_STIHAC) {
|
||||
n->data.x = n->pociatocne_x + sin(n->data.y * 0.1) * 15;
|
||||
// teleport na druhu stranu ak pojde za okraj
|
||||
if (n->data.x < 0) n->data.x = stav->sirka_obrazovky - 1;
|
||||
if (n->data.x >= stav->sirka_obrazovky) n->data.x = 0;
|
||||
}
|
||||
|
||||
// Despawn ak prejde za spodny okraj
|
||||
if (n->data.y > stav->vyska_obrazovky) {
|
||||
n->data.aktivny = 0;
|
||||
if (stav->stit_casovac <= 0) { // odcitanie len pri neaktivnom stite
|
||||
stav->zivoty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pohyb striel hraca
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (stav->strely_hrac[i].data.aktivny) {
|
||||
stav->strely_hrac[i].data.y += stav->strely_hrac[i].data.rychlost_y;
|
||||
if (stav->strely_hrac[i].data.y < 0) {
|
||||
stav->strely_hrac[i].data.aktivny = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pohyb striel bossa
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (stav->strely_boss[i].data.aktivny) {
|
||||
stav->strely_boss[i].data.y += stav->strely_boss[i].data.rychlost_y;
|
||||
if (stav->strely_boss[i].data.y > stav->vyska_obrazovky) {
|
||||
stav->strely_boss[i].data.aktivny = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pohyb bossa
|
||||
if (stav->boss.aktivny) {
|
||||
stav->boss.data.x += stav->boss.data.rychlost_x * stav->boss.smer_pohybu;
|
||||
if (stav->boss.data.x < 5 || stav->boss.data.x > stav->sirka_obrazovky - 5) {
|
||||
stav->boss.smer_pohybu *= -1; // Zmeni smer na okraji
|
||||
}
|
||||
}
|
||||
|
||||
// casovac zmiznutia bariery
|
||||
for (int i = 0; i < MAX_BARIERY; i++) {
|
||||
if (stav->bariery[i].data.aktivny) {
|
||||
stav->bariery[i].zivotnost--;
|
||||
if (stav->bariery[i].zivotnost <= 0) {
|
||||
stav->bariery[i].data.aktivny = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spracovanie utokov bossa, meni jeho fazy a vytvara strely a nepriatelov.
|
||||
*/
|
||||
void spracuj_logiku_bossa(StavHry *stav) {
|
||||
if (!stav->boss.aktivny) return;
|
||||
|
||||
stav->boss.casovac_fazy--;
|
||||
stav->boss.casovac_utoku--;
|
||||
|
||||
// Zmena utocneho vzoru po uplynuti casu
|
||||
if (stav->boss.casovac_fazy <= 0) {
|
||||
stav->boss.aktualny_vzor = (BossAttackPattern)(rand() % 2); // random vyber
|
||||
stav->boss.casovac_fazy = 300 + rand() % 200;
|
||||
stav->boss.casovac_utoku = 0; // Okamzity utok po zmene fazy
|
||||
}
|
||||
|
||||
// zautocenie ak uplynul casovac utoku
|
||||
if (stav->boss.casovac_utoku <= 0) {
|
||||
switch (stav->boss.aktualny_vzor) {
|
||||
case VZOR_SWEEP: // strelba pri pohybe
|
||||
stav->boss.casovac_utoku = 60;
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (!stav->strely_boss[i].data.aktivny) {
|
||||
Strela *s = &stav->strely_boss[i];
|
||||
s->data.aktivny = 1;
|
||||
s->data.x = stav->boss.data.x;
|
||||
s->data.y = stav->boss.data.y + 2;
|
||||
s->data.rychlost_y = 0.4;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VZOR_MINIONS: // vytvaranie lodi
|
||||
stav->boss.casovac_utoku = 120;
|
||||
vytvor_nepriatela(stav);
|
||||
vytvor_nepriatela(stav);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oprava kolizii objektov
|
||||
*/
|
||||
void ries_kolizie(StavHry *stav) {
|
||||
// Strely hraca vs Bariera
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (!stav->strely_hrac[i].data.aktivny) continue;
|
||||
for (int j = 0; j < MAX_BARIERY; j++) {
|
||||
if (!stav->bariery[j].data.aktivny) continue;
|
||||
// zasiahla barieru?
|
||||
if (fabs(stav->strely_hrac[i].data.y - stav->bariery[j].data.y) < 1 &&
|
||||
stav->strely_hrac[i].data.x >= stav->bariery[j].data.x &&
|
||||
stav->strely_hrac[i].data.x <= stav->bariery[j].data.x + 5) {
|
||||
stav->strely_hrac[i].data.aktivny = 0; // Znicenie strely
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strely hraca vs Enemy
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (!stav->strely_hrac[i].data.aktivny) continue;
|
||||
for (int j = 0; j < MAX_NEPRIATELIA; j++) {
|
||||
if (!stav->nepriatelia[j].data.aktivny) continue;
|
||||
// Kontrola, ci strela zasiahla nepriatela
|
||||
if (fabs(stav->strely_hrac[i].data.x - stav->nepriatelia[j].data.x) < 2 &&
|
||||
fabs(stav->strely_hrac[i].data.y - stav->nepriatelia[j].data.y) < 2) {
|
||||
stav->strely_hrac[i].data.aktivny = 0;
|
||||
stav->nepriatelia[j].hp--;
|
||||
if (stav->nepriatelia[j].hp <= 0) { // Ak nepriatelovi klesne HP na 0
|
||||
stav->nepriatelia[j].data.aktivny = 0;
|
||||
stav->skore += 10 * (1 + stav->combo_pocitadlo);
|
||||
stav->combo_pocitadlo++;
|
||||
stav->combo_casovac = 100; // Reset casovaca pre combo
|
||||
} else {
|
||||
stav->nepriatelia[j].farba = COLOR_YELLOW; // Vizuálna odozva na zásah
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strely hraca vs Boss
|
||||
if (stav->boss.aktivny) {
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (!stav->strely_hrac[i].data.aktivny) continue;
|
||||
// kONTROLA ci strela hitla bossa
|
||||
if (fabs(stav->strely_hrac[i].data.x - stav->boss.data.x) < 5 &&
|
||||
fabs(stav->strely_hrac[i].data.y - stav->boss.data.y) < 2) {
|
||||
stav->strely_hrac[i].data.aktivny = 0;
|
||||
stav->boss.hp--;
|
||||
stav->skore += 50;
|
||||
if (stav->boss.hp <= 0) {
|
||||
stav->boss.aktivny = 0;
|
||||
stav->stav_programu = KONIEC_HRY_VYHRA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kolizie s hracom ak nema stit
|
||||
if (stav->stit_casovac <= 0) {
|
||||
// Hrac vs Enemy strewtnutie
|
||||
for (int i = 0; i < MAX_NEPRIATELIA; i++) {
|
||||
if (stav->nepriatelia[i].data.aktivny &&
|
||||
fabs(stav->hrac.data.x - stav->nepriatelia[i].data.x) < 2 &&
|
||||
fabs(stav->hrac.data.y - stav->nepriatelia[i].data.y) < 2) {
|
||||
stav->zivoty--;
|
||||
stav->nepriatelia[i].data.aktivny = 0;
|
||||
}
|
||||
}
|
||||
// Hrac vs strely bossa
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (stav->strely_boss[i].data.aktivny &&
|
||||
fabs(stav->hrac.data.x - stav->strely_boss[i].data.x) < 2 &&
|
||||
fabs(stav->hrac.data.y - stav->strely_boss[i].data.y) < 2) {
|
||||
stav->zivoty--;
|
||||
stav->strely_boss[i].data.aktivny = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* !!!!!!!!!!!!!! Hlavna funkcia pre aktualizaciu logiky hry, volana v kazdom cykle. !!!!!!!!!!!!!!!!!!!!!
|
||||
*/
|
||||
void aktualizuj_stav(StavHry *stav) {
|
||||
// V menu / na konci hry neaktualizovat
|
||||
if (stav->stav_programu == V_MENU || stav->stav_programu == KONIEC_HRY_VYHRA || stav->stav_programu == KONIEC_HRY_PREHRA) {
|
||||
return;
|
||||
}
|
||||
|
||||
// boss warning
|
||||
if (stav->stav_programu == BOSS_VAROVANIE) {
|
||||
stav->boss_varovanie_casovac--;
|
||||
if (stav->boss_varovanie_casovac <= 0) {
|
||||
inicializuj_bossa(stav);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stav->casovac_herneho_cyklu++;
|
||||
|
||||
// Aktualizacia casovacov pre combo a stit
|
||||
if (stav->combo_casovac > 0) stav->combo_casovac--; else stav->combo_pocitadlo = 0;
|
||||
if (stav->stit_casovac > 0) stav->stit_casovac--;
|
||||
|
||||
// Aktivacia stitu za combo
|
||||
if (stav->combo_pocitadlo > 0 && stav->combo_pocitadlo % 10 == 0) {
|
||||
stav->stit_casovac = 250;
|
||||
stav->combo_pocitadlo++; // aby sa stit neaktivoval pre to iste combo
|
||||
}
|
||||
|
||||
pohybuj_objektami(stav);
|
||||
ries_kolizie(stav);
|
||||
|
||||
if (stav->stav_programu == HRAJE_SA) {
|
||||
// spawn emeny teamu podla obtiaznosati
|
||||
int spawn_sanca = (stav->obtiaznost == LAHKA) ? 3 : ((stav->obtiaznost == STREDNA) ? 5 : 8);
|
||||
if (rand() % 100 < spawn_sanca) {
|
||||
vytvor_nepriatela(stav);
|
||||
}
|
||||
|
||||
// Vytvaranie novych barier
|
||||
stav->bariera_spawn_casovac--;
|
||||
if (stav->bariera_spawn_casovac <= 0) {
|
||||
vytvor_barieru(stav);
|
||||
switch (stav->obtiaznost) { // Reset casovaca
|
||||
case LAHKA: stav->bariera_spawn_casovac = 60; break;
|
||||
case STREDNA: stav->bariera_spawn_casovac = 50; break;
|
||||
case TAZKA: stav->bariera_spawn_casovac = 40; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prechod do fazy varovania pred bossom
|
||||
if (stav->casovac_herneho_cyklu >= CAS_DO_BOSSA && !stav->boss.aktivny) {
|
||||
stav->stav_programu = BOSS_VAROVANIE;
|
||||
stav->boss_varovanie_casovac = 150;
|
||||
}
|
||||
}
|
||||
else if (stav->stav_programu == BOSS_SOUBOJ) {
|
||||
spracuj_logiku_bossa(stav);
|
||||
}
|
||||
|
||||
// Kontrola prehry
|
||||
if (stav->zivoty <= 0) {
|
||||
stav->stav_programu = KONIEC_HRY_PREHRA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
* 4. CAST : VYKRESLENIE
|
||||
**********************************************************/
|
||||
|
||||
|
||||
void vykresli_stav(StavHry *stav) {
|
||||
clear_screen();
|
||||
|
||||
// Main menu
|
||||
if (stav->stav_programu == V_MENU) {
|
||||
set_message("ULTIMATE ASTEROIDS", stav->sirka_obrazovky / 2 - 10, stav->vyska_obrazovky / 2 - 4);
|
||||
set_message("Vyber si obtiaznost:", stav->sirka_obrazovky / 2 - 10, stav->vyska_obrazovky / 2 - 2);
|
||||
char t[3][20];
|
||||
sprintf(t[0], "%s Lahka", (stav->menu_vyber == 0) ? ">" : " ");
|
||||
sprintf(t[1], "%s Stredna", (stav->menu_vyber == 1) ? ">" : " ");
|
||||
sprintf(t[2], "%s Tazka", (stav->menu_vyber == 2) ? ">" : " ");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
set_message(t[i], stav->sirka_obrazovky / 2 - 10, stav->vyska_obrazovky / 2 + i);
|
||||
}
|
||||
set_message("Ovladanie: Sipky, Enter", 1, stav->vyska_obrazovky - 2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vyzor hraca
|
||||
int hrac_farba = (stav->stit_casovac > 0) ? COLOR_CYAN : COLOR_YELLOW;
|
||||
set_color_cell('^', (int)stav->hrac.data.x, (int)stav->hrac.data.y, hrac_farba, COLOR_BLACK);
|
||||
|
||||
// Vyzor enemy lodi
|
||||
for (int i = 0; i < MAX_NEPRIATELIA; i++) {
|
||||
if (stav->nepriatelia[i].data.aktivny) {
|
||||
char c = '?';
|
||||
switch (stav->nepriatelia[i].typ) {
|
||||
case TYP_ASTEROID: c = 'v'; break;
|
||||
case TYP_STIHAC: c = 'S'; break;
|
||||
case TYP_TANK: c = 'O'; break;
|
||||
case TYP_MINA: c = '*'; break;
|
||||
}
|
||||
set_color_cell(c, (int)stav->nepriatelia[i].data.x, (int)stav->nepriatelia[i].data.y, stav->nepriatelia[i].farba, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
// Vyzor striel hrac/boss
|
||||
for (int i = 0; i < MAX_STRELY; i++) {
|
||||
if (stav->strely_hrac[i].data.aktivny) {
|
||||
set_color_cell('|', (int)stav->strely_hrac[i].data.x, (int)stav->strely_hrac[i].data.y, COLOR_RED, COLOR_BLACK);
|
||||
}
|
||||
if (stav->strely_boss[i].data.aktivny) {
|
||||
set_color_cell('!', (int)stav->strely_boss[i].data.x, (int)stav->strely_boss[i].data.y, COLOR_MAGENTA, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
// Nakreslenie bariery
|
||||
for (int i = 0; i < MAX_BARIERY; i++) {
|
||||
if (stav->bariery[i].data.aktivny) {
|
||||
set_message("<===>", (int)stav->bariery[i].data.x, (int)stav->bariery[i].data.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Nakreslenie bossa
|
||||
if (stav->boss.aktivny) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
set_message(boss_logo[y], stav->boss.data.x - 5, stav->boss.data.y + y);
|
||||
}
|
||||
}
|
||||
|
||||
// HUD
|
||||
char ui_text[120];
|
||||
sprintf(ui_text, "Skore: %d | Zivoty: %d | Combo: x%d", stav->skore, stav->zivoty, 1 + stav->combo_pocitadlo);
|
||||
set_message(ui_text, 1, 1);
|
||||
|
||||
// Vypis HP pri BOSSFIGHTE
|
||||
if (stav->stav_programu == BOSS_SOUBOJ && stav->boss.hp > 0) {
|
||||
char boss_hp_text[20];
|
||||
sprintf(boss_hp_text, "BOSS HP: %d", stav->boss.hp);
|
||||
set_message(boss_hp_text, stav->sirka_obrazovky / 2 - 5, 4);
|
||||
}
|
||||
|
||||
// Vypis sprav
|
||||
if (stav->stav_programu == BOSS_VAROVANIE) {
|
||||
set_message("!!! MATERSKA LOD PRICHAZA !!!", stav->sirka_obrazovky / 2 - 15, stav->vyska_obrazovky / 2);
|
||||
}
|
||||
if (stav->stav_programu == KONIEC_HRY_PREHRA) {
|
||||
set_message("GAME OVER", stav->sirka_obrazovky / 2 - 5, stav->vyska_obrazovky / 2);
|
||||
}
|
||||
if (stav->stav_programu == KONIEC_HRY_VYHRA) {
|
||||
set_message("!!! VITAZSTVO !!!", stav->sirka_obrazovky / 2 - 8, stav->vyska_obrazovky / 2);
|
||||
}
|
||||
}
|
37
du8/game.h
Normal file
37
du8/game.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef GAME_H_
|
||||
#define GAME_H_
|
||||
|
||||
#include "world.h"
|
||||
|
||||
typedef enum { V_MENU, HRAJE_SA, BOSS_VAROVANIE, BOSS_SOUBOJ, KONIEC_HRY_PREHRA, KONIEC_HRY_VYHRA } StavProgramu;
|
||||
typedef enum { LAHKA, STREDNA, TAZKA } Obtiaznost;
|
||||
typedef enum { TYP_ASTEROID, TYP_STIHAC, TYP_TANK, TYP_MINA } TypNepriatela;
|
||||
typedef enum { VZOR_SWEEP, VZOR_MINIONS } BossAttackPattern;
|
||||
#define MAX_NEPRIATELIA 60
|
||||
#define MAX_STRELY 50
|
||||
#define MAX_BARIERY 15
|
||||
#define BOSS_HP 30
|
||||
#define CAS_DO_BOSSA 1200
|
||||
|
||||
typedef struct { double x, y, rychlost_x, rychlost_y; int aktivny; } PohybujuciObjekt;
|
||||
typedef struct { PohybujuciObjekt data; } Strela;
|
||||
typedef struct { PohybujuciObjekt data; TypNepriatela typ; int hp, farba, casovac; double pociatocne_x; } Nepriatel;
|
||||
typedef struct { PohybujuciObjekt data; int zivotnost; } Bariera;
|
||||
typedef struct { PohybujuciObjekt data; } Hrac;
|
||||
typedef struct { int aktivny; PohybujuciObjekt data; int hp, smer_pohybu, casovac_utoku, casovac_fazy; BossAttackPattern aktualny_vzor; } Boss;
|
||||
|
||||
typedef struct {
|
||||
Hrac hrac; Nepriatel nepriatelia[MAX_NEPRIATELIA]; Strela strely_hrac[MAX_STRELY];
|
||||
Strela strely_boss[MAX_STRELY]; Bariera bariery[MAX_BARIERY]; Boss boss;
|
||||
int skore, zivoty, casovac_herneho_cyklu, menu_vyber;
|
||||
StavProgramu stav_programu; Obtiaznost obtiaznost;
|
||||
int combo_pocitadlo, combo_casovac, stit_casovac, boss_varovanie_casovac, bariera_spawn_casovac;
|
||||
int sirka_obrazovky, vyska_obrazovky;
|
||||
} StavHry;
|
||||
|
||||
void inicializuj_hru(StavHry *stav, int sirka, int vyska);
|
||||
void spracuj_vstup(StavHry *stav, int klavesa);
|
||||
void aktualizuj_stav(StavHry *stav);
|
||||
void vykresli_stav(StavHry *stav);
|
||||
|
||||
#endif
|
BIN
du8/game.o
Normal file
BIN
du8/game.o
Normal file
Binary file not shown.
39
du8/main.c
Normal file
39
du8/main.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "world.h"
|
||||
#include "game.h"
|
||||
|
||||
void* init_game()
|
||||
{
|
||||
StavHry *stav = (StavHry*) malloc(sizeof(StavHry));
|
||||
if (stav == NULL) return NULL;
|
||||
srand(time(NULL));
|
||||
return stav;
|
||||
}
|
||||
|
||||
void destroy_game(void* game)
|
||||
{
|
||||
free(game);
|
||||
}
|
||||
|
||||
int world_event(struct event* event, void* game)
|
||||
{
|
||||
StavHry *stav = (StavHry*) game;
|
||||
|
||||
if (event->type == EVENT_START) {
|
||||
inicializuj_hru(stav, event->width, event->height);
|
||||
} else if (event->type == EVENT_KEY) {
|
||||
if (event->key == 'q' || event->type == EVENT_ESC) return 1;
|
||||
spracuj_vstup(stav, event->key);
|
||||
} else if (event->type == EVENT_TIMEOUT) {
|
||||
aktualizuj_stav(stav);
|
||||
}
|
||||
|
||||
vykresli_stav(stav);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
return start_world(init_game, world_event, destroy_game);
|
||||
}
|
BIN
du8/main.o
Normal file
BIN
du8/main.o
Normal file
Binary file not shown.
73
du8/world.c
Normal file
73
du8/world.c
Normal file
@ -0,0 +1,73 @@
|
||||
#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 out of bounds (0,%d)", source, x, COLS); abort_game(msg); }
|
||||
if (y < 0 || y >= LINES) { sprintf(msg, "%s: height %d out of bounds (0,%d)", source, y, LINES); abort_game(msg); }
|
||||
}
|
||||
|
||||
void clear_screen() { erase(); }
|
||||
void game_speed(int value) { if (value < 0) abort_game("game_speed: negative"); TIMEOUT = value; }
|
||||
void set_message(const char* message, int x, int y) { mvprintw(y, x, "%s", message); }
|
||||
void set_cell(int character, int x, int 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_id = front_color * 8 + back_color + 1;
|
||||
attron(COLOR_PAIR(pair_id));
|
||||
mvaddch(y, x, character);
|
||||
attroff(COLOR_PAIR(pair_id));
|
||||
} else {
|
||||
mvaddch(y, x, character);
|
||||
}
|
||||
}
|
||||
|
||||
int start_world(void* (*init_game)(), int (*world_event)(struct event* event, void* game), void (*destroy_game)(void* game)) {
|
||||
srand(time(NULL));
|
||||
int r = 0;
|
||||
TIMEOUT = 100;
|
||||
if (initscr() == NULL) { puts("Curses Error."); return -1; }
|
||||
noecho(); cbreak(); nodelay(stdscr, TRUE); keypad(stdscr, TRUE); curs_set(FALSE);
|
||||
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) init_pair(i * 8 + j + 1, i, j);
|
||||
}
|
||||
void* game_state = NULL;
|
||||
if (init_game != NULL) { game_state = init_game(); assert(game_state != NULL); }
|
||||
|
||||
timeout(TIMEOUT);
|
||||
struct event event;
|
||||
memset(&event, 0, sizeof(struct event));
|
||||
event.height = LINES; event.width = COLS; event.type = EVENT_START;
|
||||
|
||||
r = world_event(&event, game_state);
|
||||
refresh();
|
||||
while (!r) {
|
||||
memset(&event, 0, sizeof(struct event));
|
||||
event.height = LINES; event.width = COLS;
|
||||
event.key = getch();
|
||||
if (event.key == ERR) event.type = EVENT_TIMEOUT;
|
||||
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) event.type = EVENT_ESC; else { event.key = k; event.alt_key = 1; } }
|
||||
}
|
||||
r = world_event(&event, game_state);
|
||||
refresh();
|
||||
timeout(TIMEOUT);
|
||||
}
|
||||
if (destroy_game != NULL) destroy_game(game_state);
|
||||
endwin();
|
||||
return r;
|
||||
}
|
21
du8/world.h
Normal file
21
du8/world.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _WORLD_H_
|
||||
#define _WORLD_H_
|
||||
|
||||
#include <curses.h>
|
||||
|
||||
enum event_type { EVENT_START, EVENT_TIMEOUT, EVENT_KEY, EVENT_RESIZE, EVENT_ESC, EVENT_END };
|
||||
|
||||
struct event {
|
||||
int width, height, key, alt_key, mouse_x, mouse_y, mouse_left, mouse_right, mouse_middle;
|
||||
enum event_type type;
|
||||
long int time_ms;
|
||||
};
|
||||
|
||||
void set_cell(int character, int x, int y);
|
||||
void set_color_cell(int character, int x, int y, short front_color, short back_color);
|
||||
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
|
BIN
du8/world.o
Normal file
BIN
du8/world.o
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user