pvjc25/du8/game.c
2025-06-12 13:00:33 +02:00

564 lines
19 KiB
C

#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);
}
}