1
0
Fork 0
nbsdgames/darrt.c

491 lines
10 KiB
C
Raw Normal View History

2021-03-05 13:00:08 +00:00
/*
_
| '.
| :
|.' 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;
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(0,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;
}
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]);
}
}
void end_animation(){
for(byte y=0;y<LEN;++y){
for(byte x=0;x<WID;++x){
mvaddch(y,x,background[y][x]);
}
}
for(byte i=0;i<26;++i){
if(aims[i].brake){
draw_aim(aims[i]);
}
}
refresh();
long points;
float distance;
byte bold=0;
sleep(2);
for(byte i=0;i<26;++i){
if(aims[i].brake){
distance=(int)center_distance((byte)aims[i].y,(byte)aims[i].x);
if(distance>HLEN){
points=-2*pow(2,distance-HLEN);
}
else{
points=pow(2,HLEN-distance);
}
score+=points;
bold=(int)(aims[i].x/4+ i*distance)%2;//so numbers displayed don't mix
mvprintw(5,0,"Score: %d",score);
attron(colors[2]|(A_BOLD*bold));
mvprintw(aims[i].y,aims[i].x,"%d",points);
attroff(colors[2]|(A_BOLD*bold));
refresh();
usleep(500000);
}
}
}
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;
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!=ERR){
usleep(100000);
flushinp();
}
if(!aims_to_stop){
break;
}
for(int i=0;i<26;++i){
move_aim(aims+i);
}
}
End:
//end_animation();
flushinp();
nocbreak();
cbreak();
curs_set(1);
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;
}