mirror of
https://github.com/abakh/nbsdgames.git
synced 2024-11-02 16:27:18 -04:00
495 lines
10 KiB
C
495 lines
10 KiB
C
/*
|
|
_
|
|
| '.
|
|
| :
|
|
|.' ARRT
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
|
|
#include <curses.h>
|
|
#include <unistd.h>
|
|
#include "config.h"
|
|
#define SAVE_TO_NUM 11
|
|
#define LEN 24
|
|
#define HLEN LEN/2
|
|
#define WID 80
|
|
#define HWID WID/2
|
|
#define SHOTS_WHEN_STARTING 10
|
|
|
|
#define randint(a,b) ((a)+(rand()%((b+1)-(a))))
|
|
typedef signed char byte;
|
|
#ifdef Plan9
|
|
int usleep(long usec) {
|
|
int second = usec/1000000;
|
|
long nano = usec*1000 - second*1000000;
|
|
struct timespec sleepy = {0};
|
|
sleepy.tv_sec = second;
|
|
sleepy.tv_nsec = nano;
|
|
nanosleep(&sleepy, (struct timespec *) NULL);
|
|
return 0;
|
|
}
|
|
#endif
|
|
// 12 lines of water
|
|
// 80 columns
|
|
|
|
chtype colors[3]={0};
|
|
long score=0;
|
|
char error [150]={0};
|
|
FILE* scorefile;
|
|
|
|
chtype background[LEN][WID];
|
|
|
|
int input;
|
|
typedef struct aim{
|
|
char sign;
|
|
float y,x;
|
|
float angle;
|
|
float v;
|
|
byte brake;
|
|
bool visible;
|
|
}aim;
|
|
|
|
aim aims[26];
|
|
aim landed_aims[SHOTS_WHEN_STARTING];//so an aim couldn't pass below one that has already landed, doesn't make sense visually.
|
|
|
|
byte shots,aims_to_stop;
|
|
|
|
char msg[150]={0};
|
|
byte msg_show=0;
|
|
byte digit_count(int num){
|
|
byte ret=0;
|
|
do{
|
|
++ret;
|
|
num/=10;
|
|
}while(num);
|
|
return ret;
|
|
}
|
|
void filled_rect(byte sy,byte sx,byte ey,byte ex){
|
|
byte y,x;
|
|
for(y=sy;y<ey;++y)
|
|
for(x=sx;x<ex;++x)
|
|
mvaddch(y,x,' ');
|
|
}
|
|
void red_border(void){
|
|
byte y,x;
|
|
for(y=0;y<LEN;++y){
|
|
mvaddch(y,WID-1,' '|A_STANDOUT|colors[0]);
|
|
mvaddch(y,0,' '|A_STANDOUT|colors[0]);
|
|
}
|
|
for(x=0;x<WID;++x){
|
|
mvaddch(LEN-1,x,' '|A_STANDOUT|colors[0]);
|
|
mvaddch(0,x,' '|A_STANDOUT|colors[0]);
|
|
}
|
|
|
|
}
|
|
|
|
void fill_aims(){
|
|
for(byte i=0;i<26;++i){
|
|
aims[i].y= randint(8,HLEN);
|
|
aims[i].x= randint(0,HWID);
|
|
aims[i].angle=randint(0,628)/100;
|
|
aims[i].v=1;
|
|
aims[i].sign='A'+i;
|
|
aims[i].brake=0;
|
|
aims[i].visible=1;
|
|
}
|
|
}
|
|
|
|
float center_distance(byte y,byte x){
|
|
//y distance is twice accounted for. visual reasons
|
|
return sqrt( (y-HLEN)*(y-HLEN)+0.25*(x-HWID)*(x-HWID) );
|
|
}
|
|
long calculate_points(aim *a){
|
|
int distance=center_distance((byte)a->y,(byte)a->x);
|
|
long points;
|
|
if(distance>HLEN){
|
|
points=-2*pow(2,distance-HLEN);
|
|
}
|
|
else if((byte) a->y == HLEN && (byte) a->x == HWID){
|
|
points=1000000;
|
|
}
|
|
else{
|
|
points=pow(2,HLEN-distance);
|
|
}
|
|
return points;
|
|
}
|
|
void aim_lands(aim *a){
|
|
landed_aims[SHOTS_WHEN_STARTING-aims_to_stop]=*a;
|
|
--aims_to_stop;
|
|
score+=calculate_points(a);
|
|
a->visible=0;
|
|
|
|
float distance= center_distance((byte)a->y,(byte)a->x);
|
|
if((byte)a->y==HLEN && (byte)a->x==HWID){
|
|
strcpy(msg,"Bravo!");
|
|
}
|
|
else if(distance<2){
|
|
strcpy(msg,"Very close...");
|
|
}
|
|
else{
|
|
goto NoMessage;
|
|
}
|
|
msg_show=30;
|
|
NoMessage: return;
|
|
}
|
|
void move_aim(aim *a){
|
|
if(a->brake==1){
|
|
return;
|
|
}
|
|
else if(a->brake>0){
|
|
--a->brake;
|
|
}
|
|
bool bounce;
|
|
bounce=0;
|
|
|
|
//bounce when hitting the borders, and don't get stuck there
|
|
if(a->x<0 || (int)a->x>=WID-1 || ((int)a->x==13 && a->y<=7 ) ){
|
|
a->angle =M_PI- a->angle;
|
|
bounce=1;
|
|
}
|
|
if(a->y <0 || (int)a->y >= LEN-1 || (a->x<=13 && (int)a->y==7)){
|
|
a->angle =0- a->angle;
|
|
bounce=1;
|
|
}
|
|
if(a->x<0)//these are for getting unstuck
|
|
a->x=1;
|
|
if(a->y<0)
|
|
a->y=1;
|
|
if(a->x>=WID)
|
|
a->x=WID-1;
|
|
if(a->y>=LEN)
|
|
a->y=LEN-1;
|
|
|
|
if((int)a->x==13 && a->y<7)
|
|
a->x=14;
|
|
|
|
if(a->x<=13 && (int)a->y==7)
|
|
a->y=8;
|
|
|
|
while(a->angle<0){//preventing overflow
|
|
a->angle +=M_PI*2;
|
|
}
|
|
|
|
//move
|
|
a->x+=cos(a->angle)*a->v;
|
|
a->y+=sin(a->angle)*a->v;
|
|
|
|
|
|
if(bounce && a->x>=WID-1)//getting unstuck
|
|
a->x=WID-1;
|
|
if(bounce && a->y>=LEN-1)
|
|
a->y=LEN-1;
|
|
|
|
if(bounce){//bounce in a slightly different direction than it should be
|
|
a->angle +=randint(-1,1)*0.1;
|
|
}
|
|
if(a->x<13 && a->y<7){// don't go into the logo area
|
|
if(13 - a->x < 7 - a->y){
|
|
a->y=8;
|
|
}
|
|
else{
|
|
a->x=14;
|
|
}
|
|
}
|
|
|
|
if(a->brake==1){//the aim has just been stopped
|
|
aim_lands(a);
|
|
}
|
|
|
|
}
|
|
void star_line(byte y){
|
|
for(byte x=1;x<WID-1;++x)
|
|
mvaddch(y,x,'.');
|
|
}
|
|
void make_background(){
|
|
float d;
|
|
for(byte y=0;y<LEN;++y){
|
|
for(byte x=0;x<WID;++x){
|
|
d=center_distance(y,x)/(HLEN/4);
|
|
if(d<4){
|
|
if( ((int)d) %2){
|
|
background[y][x]='#';
|
|
}
|
|
else{
|
|
background[y][x]='$'|colors[0];
|
|
}
|
|
}
|
|
else{
|
|
background[y][x]=' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void draw_aim(aim a){
|
|
if(!a.visible)
|
|
return;
|
|
|
|
chtype color;
|
|
if(a.brake)
|
|
color=colors[2]|A_BOLD;
|
|
else
|
|
color=colors[2];
|
|
mvaddch((int) a.y,(int)a.x,a.sign|color);
|
|
}
|
|
void logo(){
|
|
mvaddstr(0,0," _ ");
|
|
mvaddstr(1,0,"| '. ");
|
|
mvaddstr(2,0,"| : ");
|
|
mvaddstr(3,0,"|.' ARRT ");
|
|
}
|
|
void draw(){
|
|
for(byte y=0;y<LEN;++y){
|
|
for(byte x=0;x<WID;++x){
|
|
mvaddch(y,x,background[y][x]);
|
|
}
|
|
}
|
|
|
|
logo();
|
|
mvprintw(5,0,"Score: %d",score);
|
|
mvprintw(6,0,"Shots: %d",shots);
|
|
for(byte i=0;i<SHOTS_WHEN_STARTING-aims_to_stop;++i){
|
|
draw_aim(landed_aims[i]);
|
|
}
|
|
for(byte i=0;i<26;++i){
|
|
draw_aim(aims[i]);
|
|
}
|
|
if(msg_show){
|
|
--msg_show;
|
|
mvaddstr(LEN-1,0,msg);
|
|
}
|
|
}
|
|
|
|
void end_scene(){
|
|
for(byte y=0;y<LEN;++y){
|
|
for(byte x=0;x<WID;++x){
|
|
mvaddch(y,x,background[y][x]);
|
|
}
|
|
}
|
|
|
|
logo();
|
|
mvprintw(5,0,"Score: %d",score);
|
|
for(byte i=0;i<SHOTS_WHEN_STARTING-aims_to_stop;++i){
|
|
draw_aim(landed_aims[i]);
|
|
}
|
|
mvaddstr(LEN-1,0,"Press any key to continue:");
|
|
getch();
|
|
refresh();
|
|
}
|
|
|
|
byte scorewrite(void){// only saves the top 10, returns the place in the chart
|
|
bool deforno;
|
|
if( !getenv("DRT_SCORES") && (scorefile= fopen(DRT_SCORES,"r")) ){
|
|
deforno=1;
|
|
}
|
|
else{
|
|
deforno=0;
|
|
if( !(scorefile = fopen(getenv("DRT_SCORES"),"r")) ){
|
|
sprintf(error,"No accessible score files found. You can make an empty text file in %s or set DRT_SCORES to such a file to solve this.",DRT_SCORES);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
char namebuff[SAVE_TO_NUM][60];
|
|
long scorebuff[SAVE_TO_NUM];
|
|
|
|
memset(namebuff,0,SAVE_TO_NUM*60*sizeof(char) );
|
|
memset(scorebuff,0,SAVE_TO_NUM*sizeof(long) );
|
|
|
|
long fuckingscore =0;
|
|
char fuckingname[60]={0};
|
|
byte location=0;
|
|
|
|
while( fscanf(scorefile,"%59s : %ld\n",fuckingname,&fuckingscore) == 2 && location<SAVE_TO_NUM ){
|
|
strcpy(namebuff[location],fuckingname);
|
|
scorebuff[location] = fuckingscore;
|
|
++location;
|
|
|
|
memset(fuckingname,0,60);
|
|
fuckingscore=0;
|
|
}
|
|
if(deforno)
|
|
scorefile = fopen(DRT_SCORES,"w+");//get rid of the previous text first
|
|
else
|
|
scorefile = fopen(getenv("DRT_SCORES"), "w+") ;
|
|
if(!scorefile){
|
|
strcpy(error, "The file cannot be opened in w+. ");
|
|
return -1;
|
|
}
|
|
|
|
byte itreached=location;
|
|
byte ret = -1;
|
|
bool wroteit=0;
|
|
for(location=0;location<=itreached && location<SAVE_TO_NUM-wroteit;++location){
|
|
if(!wroteit && score>=scorebuff[location] ){
|
|
fprintf(scorefile,"%s : %ld\n",getenv("USER"),score);
|
|
ret=location;
|
|
wroteit=1;
|
|
}
|
|
if(location<SAVE_TO_NUM-wroteit && location<itreached)
|
|
fprintf(scorefile,"%s : %ld\n",namebuff[location],scorebuff[location]);
|
|
}
|
|
fflush(scorefile);
|
|
return ret;
|
|
}
|
|
void showscores(byte playerrank){
|
|
byte y,x;
|
|
attron(colors[3]);
|
|
filled_rect(0,0,LEN,WID);
|
|
red_border();
|
|
if(*error){
|
|
mvaddstr(1,0,error);
|
|
mvprintw(2,0,"However, your score is %ld.",score);
|
|
refresh();
|
|
return;
|
|
}
|
|
if(playerrank == 0){
|
|
char formername[60]={0};
|
|
long formerscore=0;
|
|
rewind(scorefile);
|
|
fscanf(scorefile,"%*s : %*d");
|
|
if ( fscanf(scorefile,"%s : %ld",formername,&formerscore)==2 && formerscore>0){
|
|
byte a = (LEN-9)/2;
|
|
star_line(1);
|
|
star_line(LEN-2);
|
|
mvaddstr(1,WID/2-8,"CONGRATULATIONS!!");
|
|
mvprintw(a+1,HWID-10," _____ You bet the");
|
|
mvprintw(a+2,HWID-10," .' | previous");
|
|
mvprintw(a+3,HWID-10," .' | record");
|
|
mvprintw(a+4,HWID-10," | .| | of");
|
|
mvprintw(a+5,HWID-10," |.' | |%11ld",formerscore);
|
|
mvprintw(a+6,HWID-10," | | held by");
|
|
mvprintw(a+7,HWID-10," ___| |___%7s!",formername);
|
|
mvprintw(a+8,HWID-10," | |");
|
|
mvprintw(a+9,HWID-10," |____________|");
|
|
mvprintw(LEN-3,HWID-11,"Press a key to continue");
|
|
refresh();
|
|
do{
|
|
input=getch();
|
|
}while(input==KEY_UP || input==KEY_DOWN);
|
|
filled_rect(0,0,LEN,WID);
|
|
red_border();
|
|
}
|
|
|
|
}
|
|
//scorefile is still open with w+
|
|
char pname[60] = {0};
|
|
long pscore=0;
|
|
byte rank=0;
|
|
rewind(scorefile);
|
|
mvaddstr(1,WID/2-4,"HIGH SCORES");
|
|
attron(colors[3]);
|
|
while( rank<SAVE_TO_NUM && fscanf(scorefile,"%s : %ld\n",pname,&pscore) == 2){
|
|
star_line(2+2*rank);
|
|
move(2+2*rank,1);
|
|
if(rank == playerrank)
|
|
printw(">>>");
|
|
printw("%s",pname);
|
|
mvprintw(2+2*rank,WID-1-digit_count(pscore),"%d",pscore);
|
|
++rank;
|
|
}
|
|
attroff(colors[3]);
|
|
refresh();
|
|
}
|
|
void help(void){
|
|
nocbreak();
|
|
cbreak();
|
|
attron(colors[3]);
|
|
filled_rect(0,0,LEN,WID);
|
|
red_border();
|
|
mvprintw(1,HWID-4,"GAME PLAY");
|
|
mvprintw(3,1,"If you hit a letter on keyboard, the letter on the");
|
|
mvprintw(4,1,"screen will soon stop. You have to aim for the");
|
|
mvprintw(5,1,"center of the target using the moving letters.");
|
|
attroff(colors[3]);
|
|
refresh();
|
|
getch();
|
|
halfdelay(1);
|
|
}
|
|
void sigint_handler(int x){
|
|
endwin();
|
|
puts("Quit.");
|
|
exit(x);
|
|
}
|
|
int main(void){
|
|
signal(SIGINT,sigint_handler);
|
|
initscr();
|
|
noecho();
|
|
cbreak();
|
|
keypad(stdscr,1);
|
|
srand(time(NULL)%UINT_MAX);
|
|
if(has_colors()){
|
|
start_color();
|
|
init_pair(1,COLOR_RED,COLOR_BLACK);
|
|
init_pair(2,COLOR_YELLOW,COLOR_BLACK);
|
|
init_pair(3,COLOR_GREEN,COLOR_BLACK);
|
|
for(byte b=0;b<3;++b)
|
|
colors[b]=COLOR_PAIR(b+1);
|
|
}
|
|
byte n;
|
|
|
|
make_background();
|
|
Start:
|
|
erase();
|
|
halfdelay(1);
|
|
curs_set(0);
|
|
score=0;
|
|
msg_show=0;
|
|
aims_to_stop=shots=SHOTS_WHEN_STARTING;
|
|
fill_aims();
|
|
while(1){
|
|
draw();
|
|
refresh();
|
|
input=getch();
|
|
|
|
if(input=='?' || input==KEY_F(1))
|
|
help();
|
|
if(input>='a' && input<='z'){
|
|
input=input-'a'+'A';
|
|
}
|
|
if(input>='A' && input<='Z' && shots){
|
|
if(!aims[input-'A'].brake){
|
|
aims[input-'A'].brake=15;
|
|
--shots;
|
|
}
|
|
}
|
|
if(input=='Q'){
|
|
strcpy(msg,"Ctrl-C to quit.");
|
|
msg_show=50;
|
|
}
|
|
if(input!=ERR){
|
|
usleep(100000);
|
|
flushinp();
|
|
}
|
|
if(!aims_to_stop){
|
|
break;
|
|
}
|
|
for(int i=0;i<26;++i){
|
|
move_aim(aims+i);
|
|
}
|
|
}
|
|
End:
|
|
flushinp();
|
|
nocbreak();
|
|
cbreak();
|
|
curs_set(1);
|
|
end_scene();
|
|
showscores(scorewrite());
|
|
attron(colors[0]|A_STANDOUT);
|
|
mvprintw(LEN-1,HWID-11,"Wanna play again? (y/n)");
|
|
attroff(colors[0]|A_STANDOUT);
|
|
do{
|
|
input=getch();
|
|
}while(input==KEY_UP || input==KEY_DOWN);
|
|
if(input!='q' && input!='n' && input!='N')
|
|
goto Start;
|
|
endwin();
|
|
return 0;
|
|
}
|