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