388 lines
10 KiB
C
388 lines
10 KiB
C
// This file is part of Snownews - A lightweight console RSS newsreader
|
|
//
|
|
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
|
|
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
|
|
//
|
|
// Snownews is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 3
|
|
// as published by the Free Software Foundation.
|
|
//
|
|
// Snownews is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Snownews. If not, see http://www.gnu.org/licenses/.
|
|
|
|
#include "main.h"
|
|
#include "about.h"
|
|
#include "uiutil.h"
|
|
#include <ncurses.h>
|
|
#include <sys/time.h>
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Santa Hunta
|
|
struct shot {
|
|
unsigned x;
|
|
unsigned y;
|
|
bool fired;
|
|
};
|
|
struct santa {
|
|
int x;
|
|
unsigned y;
|
|
unsigned height;
|
|
unsigned length;
|
|
unsigned speed;
|
|
unsigned anim;
|
|
const char* gfx;
|
|
const char* gfx_line2;
|
|
const char* altgfx;
|
|
const char* altgfx_line2;
|
|
};
|
|
struct scoreDisplay {
|
|
int score;
|
|
int x;
|
|
int y;
|
|
int rounds;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Generate a random number in given range
|
|
static unsigned nrand (unsigned r)
|
|
{
|
|
return rand() % r;
|
|
}
|
|
|
|
static void mvadd_center_utf8 (unsigned line, const char* text)
|
|
{
|
|
mvadd_utf8 (line, (COLS - utf8_length (text)) / 2u, text);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Santa Hunta
|
|
|
|
static void SHDrawGun (unsigned gun_pos)
|
|
{
|
|
move (LINES - 3, 0);
|
|
clrtoeol();
|
|
move (LINES - 2, 0);
|
|
clrtoeol();
|
|
|
|
attron (WA_BOLD);
|
|
mvaddstr (LINES - 3, gun_pos - 3, "___/\\___");
|
|
mvaddstr (LINES - 2, gun_pos - 3, "|######|");
|
|
attroff (WA_BOLD);
|
|
}
|
|
|
|
static void SHDrawStatus (void)
|
|
{
|
|
attron (WA_BOLD);
|
|
char helpstr[128];
|
|
snprintf (helpstr, sizeof (helpstr), _("Move: cursor or %c/%c; shoot: space; quit: %c"), _settings.keybindings.prev, _settings.keybindings.next, _settings.keybindings.quit);
|
|
mvadd_utf8 (LINES - 1, 1, helpstr);
|
|
move (LINES - 1, COLS - 1);
|
|
attroff (WA_BOLD);
|
|
}
|
|
|
|
static void SHDrawScore (int score, int level)
|
|
{
|
|
attron (WA_REVERSE);
|
|
mvhline (0, 0, ' ', COLS);
|
|
mvadd_utf8 (0, 1, _("Santa Hunta!"));
|
|
|
|
char scorestr[16];
|
|
snprintf (scorestr, sizeof (scorestr), _("Score: %d"), score);
|
|
mvadd_utf8 (0, COLS - utf8_length (scorestr) - 1, scorestr);
|
|
|
|
char levelstr[16];
|
|
snprintf (levelstr, sizeof (levelstr), _("Level: %d"), level);
|
|
mvadd_center_utf8 (0, levelstr);
|
|
|
|
attroff (WA_REVERSE);
|
|
}
|
|
|
|
static void SHDrawProjectile (struct shot shot)
|
|
{
|
|
attron (WA_BOLD);
|
|
mvaddstr (shot.y, shot.x, "|");
|
|
attroff (WA_BOLD);
|
|
}
|
|
|
|
static void SHDrawSanta (struct santa santa)
|
|
{
|
|
int len = COLS - santa.x;
|
|
if (santa.anim == 0) {
|
|
if (santa.x < 0)
|
|
mvaddnstr (santa.y, 0, santa.gfx + abs (santa.x), len);
|
|
else
|
|
mvaddnstr (santa.y, santa.x, santa.gfx, len);
|
|
if (santa.x < 0)
|
|
mvaddnstr (santa.y + 1, 0, santa.gfx_line2 + abs (santa.x), len);
|
|
else
|
|
mvaddnstr (santa.y + 1, santa.x, santa.gfx_line2, len);
|
|
} else {
|
|
if (santa.x < 0)
|
|
mvaddnstr (santa.y, 0, santa.altgfx + abs (santa.x), len);
|
|
else
|
|
mvaddnstr (santa.y, santa.x, santa.altgfx, len);
|
|
if (santa.x < 0)
|
|
mvaddnstr (santa.y + 1, 0, santa.altgfx_line2 + abs (santa.x), len);
|
|
else
|
|
mvaddnstr (santa.y + 1, santa.x, santa.altgfx_line2, len);
|
|
}
|
|
attron (COLOR_PAIR (10));
|
|
mvaddch (santa.y, santa.x + santa.length - 1, '*');
|
|
attroff (COLOR_PAIR (10));
|
|
}
|
|
|
|
static void newSanta (struct santa* santa, unsigned level)
|
|
{
|
|
santa->x = -27;
|
|
santa->y = 1 + nrand (LINES - 7);
|
|
santa->speed = level + nrand (3);
|
|
santa->anim = 0;
|
|
santa->height = 2;
|
|
santa->length = 27;
|
|
santa->gfx = "##___ __-+---+/*__-+---+/*";
|
|
santa->gfx_line2 = "_|__|_) /\\ /\\ /\\ /\\ "; // Append 2 spaces!
|
|
santa->altgfx = "##___ __-+---+/*__-+---+/*";
|
|
santa->altgfx_line2 = "_|__|_) /| |\\ /| |\\ "; // Append 1 space!
|
|
}
|
|
|
|
static bool SHHit (const struct shot shot, const struct santa santa)
|
|
{
|
|
return ((int) shot.x >= santa.x && shot.x <= santa.x + santa.length && (shot.y == santa.y || shot.y == santa.y + 1));
|
|
}
|
|
|
|
static unsigned SHAddScore (const struct santa santa)
|
|
{
|
|
return 100 / santa.y * santa.speed;
|
|
}
|
|
|
|
static void SHDrawHitScore (struct scoreDisplay score)
|
|
{
|
|
attron (WA_BOLD);
|
|
unsigned rand_color = 10 + nrand (6);
|
|
attron (COLOR_PAIR (rand_color));
|
|
mvprintw (score.y, score.x, "%d", score.score);
|
|
attroff (COLOR_PAIR (rand_color));
|
|
attroff (WA_BOLD);
|
|
}
|
|
|
|
static void printFinalScore (unsigned score)
|
|
{
|
|
//{{{ numbers ASCII art
|
|
static const char numbers[10][5][8] = {
|
|
{ "_______",
|
|
"| _ |",
|
|
"| | | |",
|
|
"| |_| |",
|
|
"|_____|" }, // 0
|
|
{ " ____ ",
|
|
" / | ",
|
|
"/_/| | ",
|
|
" | | ",
|
|
" |_| " }, // 1
|
|
{ "_______",
|
|
"|___ |",
|
|
"| ____|",
|
|
"| |____",
|
|
"|_____|" }, // 2
|
|
{ "_______",
|
|
"|___ |",
|
|
" ___ |",
|
|
"___| |",
|
|
"|_____|" }, // 3
|
|
{ "___ ___",
|
|
"| |_| |",
|
|
"|____ |",
|
|
" | |",
|
|
" |_|" }, // 4
|
|
{ "_______",
|
|
"| ___|",
|
|
"|___ |",
|
|
"____| |",
|
|
"|_____|" }, // 5
|
|
{ " __ ",
|
|
" / / ",
|
|
" / \\",
|
|
"| O /",
|
|
" \\___/ " }, // 6
|
|
{ "_______",
|
|
"|___ |",
|
|
" | |",
|
|
" | |",
|
|
" |_|" }, // 7
|
|
{ "_______",
|
|
"| _ |",
|
|
"| |_| |",
|
|
"| |_| |",
|
|
"|_____|" }, // 8
|
|
{ " ___ ",
|
|
" / \\ ",
|
|
"| O |",
|
|
" \\ / ",
|
|
" /__/ " } // 9
|
|
};
|
|
//}}}
|
|
char numtxt[12];
|
|
unsigned ndigits = snprintf (numtxt, 12, "%u", score);
|
|
const int sx = (COLS - ndigits * 8) / 2u, sy = (LINES - 5) / 2u;
|
|
for (unsigned i = 0; i < ndigits; ++i)
|
|
for (unsigned j = 0; j < 5; ++j)
|
|
mvaddstr (sy + j, sx + i * 8, numbers[numtxt[i] - '0'][j]);
|
|
refresh();
|
|
}
|
|
|
|
static void SHFinalScore (int score)
|
|
{
|
|
attron (WA_BOLD);
|
|
mvadd_center_utf8 ((LINES - 5) / 2u - 2, _("Final score:"));
|
|
for (int k; (k = getch()) < ' ' || k > '~';) {
|
|
unsigned rand_color = 10 + nrand (6);
|
|
attron (COLOR_PAIR (rand_color));
|
|
printFinalScore (score);
|
|
attroff (COLOR_PAIR (rand_color));
|
|
refresh();
|
|
}
|
|
attroff (WA_BOLD);
|
|
}
|
|
|
|
static void santaHunta (void)
|
|
{
|
|
// Set ncurses halfdelay mode.
|
|
// Max resolution is 1/10sec
|
|
halfdelay (1);
|
|
|
|
struct scoreDisplay scoreDisplay = { };
|
|
struct shot shot = { 0, 0, false };
|
|
bool santalives = false;
|
|
struct santa santa = { };
|
|
|
|
for (unsigned score = 0, level = 1, gun_pos = COLS / 2u, hitcount = 0, draw_delay = 0;;) {
|
|
struct timeval before, after;
|
|
gettimeofday (&before, NULL);
|
|
int input = getch();
|
|
gettimeofday (&after, NULL);
|
|
|
|
if (after.tv_sec > before.tv_sec)
|
|
after.tv_usec += 1000000;
|
|
|
|
if (!santalives) {
|
|
newSanta (&santa, level);
|
|
santalives = true;
|
|
}
|
|
// Pad drawing resolution to 1/10sec
|
|
draw_delay += after.tv_usec - before.tv_usec;
|
|
if (draw_delay > 100000) {
|
|
draw_delay = 0;
|
|
erase();
|
|
if (santalives)
|
|
SHDrawSanta (santa);
|
|
santa.anim = !santa.anim;
|
|
if (santa.x >= COLS)
|
|
santalives = false;
|
|
if (shot.fired)
|
|
SHDrawProjectile (shot);
|
|
if (scoreDisplay.rounds > 0)
|
|
SHDrawHitScore (scoreDisplay);
|
|
if (SHHit (shot, santa)) {
|
|
if (++hitcount >= 10) {
|
|
hitcount = 0;
|
|
++level;
|
|
}
|
|
santalives = false;
|
|
scoreDisplay.x = shot.x;
|
|
scoreDisplay.y = shot.y;
|
|
scoreDisplay.score = SHAddScore (santa);
|
|
scoreDisplay.rounds = 20;
|
|
score += scoreDisplay.score;
|
|
}
|
|
santa.x += santa.speed;
|
|
--scoreDisplay.rounds;
|
|
if (!--shot.y)
|
|
shot.fired = false;
|
|
}
|
|
|
|
if ((input == KEY_RIGHT) || (input == _settings.keybindings.next)) {
|
|
if (gun_pos < COLS - 5u)
|
|
++gun_pos;
|
|
} else if ((input == KEY_LEFT) || (input == _settings.keybindings.prev)) {
|
|
if (gun_pos > 3)
|
|
--gun_pos;
|
|
} else if (input == ' ') {
|
|
if (!shot.fired) {
|
|
attron (WA_BOLD | COLOR_PAIR (12));
|
|
mvaddstr (LINES - 4, gun_pos - 2, "\\***/");
|
|
attroff (WA_BOLD | COLOR_PAIR (12));
|
|
|
|
shot.x = gun_pos;
|
|
shot.y = LINES - 4;
|
|
shot.fired = true;
|
|
}
|
|
} else if (input == _settings.keybindings.quit) {
|
|
if (score)
|
|
SHFinalScore (score);
|
|
break;
|
|
}
|
|
|
|
SHDrawGun (gun_pos);
|
|
SHDrawScore (score, level);
|
|
SHDrawStatus();
|
|
|
|
refresh();
|
|
}
|
|
// Leave halfdelay mode.
|
|
cbreak();
|
|
}
|
|
|
|
void UIAbout (void)
|
|
{
|
|
if (COLS < 80) {
|
|
erase();
|
|
mvadd_utf8 (0, 0, _("Need at least 80 COLS terminal, sorry!"));
|
|
getch();
|
|
return;
|
|
}
|
|
int key = 'S';
|
|
if (!easterEgg()) {
|
|
unsigned xpos = COLS / 2u - 40;
|
|
|
|
erase();
|
|
mvaddstr (2, xpos, " ________ _______ ____ __________ _______ ______ __________ ________");
|
|
mvaddstr (3, xpos, " / _____/ \\ \\ / \\\\ | | \\\\ \\ / ___/ \\ | | \\ / ______/");
|
|
mvaddstr (4, xpos, " \\____ \\ \\ | \\ / /\\ \\\\ | / \\ | \\ / / \\ | / \\____ \\");
|
|
mvaddstr (5, xpos, " / \\ / | \\\\ \\/ / \\ | / / | \\\\ ___\\ \\ | / / \\");
|
|
mvaddstr (6, xpos, "/______ / / ___|___/ \\____/ /__|__/ / ___|___/ \\____ \\ /__|__/ /______ /");
|
|
mvaddstr (7, xpos, " \\/ \\/ \\/ \\/ \\/");
|
|
char verstrbuf [128];
|
|
snprintf (verstrbuf, sizeof(verstrbuf), _("Version %s"), SNOWNEWS_VERSTRING);
|
|
mvadd_center_utf8 (9, verstrbuf);
|
|
|
|
mvadd_center_utf8 (11, _("Brought to you by:"));
|
|
mvadd_center_utf8 (13, _("Main code"));
|
|
mvadd_center_utf8 (14, "Oliver Feiler");
|
|
mvadd_center_utf8 (16, _("Additional code"));
|
|
mvadd_center_utf8 (17, "Mike Sharov, Rene Puls");
|
|
mvadd_center_utf8 (19, _("Translation team"));
|
|
mvadd_center_utf8 (20, "Frank van der Loo, Pascal Varet, Simon Isakovic, Страхиња Радић");
|
|
mvadd_center_utf8 (21, "Fernando J. Pereda, Marco Cova, Cheng-Lung Sung, Dmitry Petukhov");
|
|
mvadd_center_utf8 (22, "Douglas Campos, Ray Iwata, Piotr Ozarowski, Yang Huan");
|
|
mvadd_center_utf8 (23, "Ihar Hrachyshka, Mats Berglund, Johannes Hove-Henriksen");
|
|
|
|
key = getch();
|
|
}
|
|
if (key == 'S')
|
|
santaHunta();
|
|
}
|
|
|
|
bool easterEgg (void)
|
|
{
|
|
time_t tunix = time (NULL);
|
|
struct tm* t = localtime (&tunix);
|
|
return t->tm_mon == 11 && t->tm_mday >= 24 && t->tm_mday <= 26;
|
|
}
|