I'm new to ncurses and we have a project to create a game of our choice.
The idea of my game is to have a spaceship and have enemies attack from the top.
I only started the code and I already ran into a problem, when i use the space bar to shoot a bullet, the bullet will travel, however i am unable to move my ship at the same time the bullet is moving.
#include<ncurses.h>
typedef struct
{/*define a structure for player information*/
int row;
int col;
}playerinfo;
int changeRow(int x,int m, int c)
{
if(x+m==22 || x+m==0 )
{
beep();
return x;
}
return x+m;
}
int changeColumn(int y,int n, int r)
{
if(y+n==72 || y+n==0 )
{
beep();
return y;
}
return y+n;
}
int main(){
initscr();
start_color();
assume_default_colors(COLOR_GREEN, COLOR_BLACK);
noecho();
cbreak();
curs_set(0); /* turn cursor display off */
timeout(0);
keypad(stdscr,TRUE); /* allow keypad keys to be used */
playerinfo playership;
playership.row= 10;
playership.col= 15;
char player[] =" X ";
char player2[]=" |o| ";
char player3[]=" xX| |Xx ";
char player4[]=" X | | X ";
char player5[]=" X__-|-__X ";
char bullet = '.';
int key = 0;
int i=0;
bool moving= false;
mvprintw(playership.row,playership.col,player);
mvprintw(playership.row+1,playership.col,player2);
mvprintw(playership.row+2,playership.col,player3);
mvprintw(playership.row+3,playership.col,player4);
mvprintw(playership.row+4,playership.col,player5);
timeout(0);
while(key!='q'){
usleep(17000);
mvprintw(playership.row,playership.col," ");
mvprintw(playership.row+5,playership.col," ");
key = getch ();
switch(key){
case KEY_UP: playership.row=changeRow(playership.row,-1,playership.col); /* move up */
break;
case KEY_DOWN: playership.row=changeRow(playership.row,+1,playership.col); /* move down */
break;
case KEY_LEFT:playership.col=changeColumn(playership.col,-1,playership.row); /* move left */
break;
case KEY_RIGHT:playership.col=changeColumn(playership.col,+1,playership.row); /* move right */
break;
case ' ': moving=true; break;
default: break; /* do nothing if other keys */
}
mvprintw(playership.row,playership.col,player);
mvprintw(playership.row+1,playership.col,player2);
mvprintw(playership.row+2,playership.col,player3);
mvprintw(playership.row+3,playership.col,player4);
mvprintw(playership.row+4,playership.col,player5);
if (moving==true){
for( i=0; i <24; i++){
refresh; mvprintw(playership.row-i-2,playership.col+5,"%c", bullet); mvprintw(playership.row,playership.col,player);
refresh();usleep(12000); mvprintw(playership.row-i-1,playership.col+5," ");} moving=false; }
refresh();
}
echo(); /* turn echo back on */
endwin(); /* End curses mode */
return 0;
}
That is because you nested the "firing" code inside your game loop, so all input stops while you do that animation. You need to change your logic to a kind of state machine:
switch(key)
{
// ...
case ' ':
if( !bullet_active )
{
bullet_active = true;
bullet_pos = 0;
}
break;
}
if( bullet_active ) {
// TODO: Draw bullet at bullet_pos
if( ++bullet_pos == 24 ) bullet_active = false;
}
Related
I am pretty new to c++ and ncurses, so sorry if my question is weird or obvious.
I managed to make a simple tui program that will do various tasks, my problem is that I have no back functionality or something that would loop the program.
I want to be able to go back to the main menu after accessing one of the submenus and I want to make the program repeat after it's done so I won't have to keep opening it over and over, so do you have any solutions that I can apply?
Thank you in advance
This is my code:
int main(int argc, char ** argv)
{
int i=0;
string input;
initscr();
noecho();
cbreak();
curs_set(0);
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
WINDOW * menuwin = newwin (0, xMax, 0, 0);
leaveok(menuwin, true);
refresh();
wrefresh(menuwin);
keypad(menuwin, true);
string choices[4] = {"Youtube","Music","Anime","Games"};
int choice;
int highlight = 0;
int option = 0;
while(1)
{
for (i=0;i < 4; i++)
{
if(i == highlight)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, choices[i].c_str());
wattroff(menuwin, A_REVERSE);
}
choice = wgetch(menuwin);
switch(choice)
{
case KEY_UP:
highlight--;
if(highlight == -1)
highlight = 3;
break;
case KEY_DOWN:
highlight++;
if(highlight == 4)
highlight = 0;
break;
default:
break;
}
if(choice == 10)
{
break;
}
}
werase(menuwin);
switch(highlight)
{
case 0:
{
menu2();
break;
}
case 1:
{
menu3();
break;
}
case 2:
{
menu4();
break;
}
case 3:
{
menu2();
}
default:
break;
}
endwin();
return 0;
}
I am trying to create a program that response to keyboard input. At the moment the menu below functions correctly but upon pressing enter I am running into issues. When I press enter nothing happens. I am just wondering why this is happening?
Many thanks!
#include <ncurses.h>
#define MENUMAX 6
void drawmenu(int item)
{
int c;
char mainmenu[] = "Menu";
char menu[MENUMAX] [10] = {
"1",
"2",
"3",
"4",
"5",
"6"
};
clear();
attron(A_BOLD | A_UNDERLINE);
addstr(mainmenu);
attroff(A_BOLD | A_UNDERLINE);
for( c = 0; c < MENUMAX; c++)
{
if( c == item)
attron(A_REVERSE);
attron(A_BOLD);
mvaddstr(3 + (c * 2), 20, menu[c]);
attroff(A_BOLD);
attroff(A_REVERSE);
}
refresh();
}
int main(int argc, char *argv[])
{
int key, menuitem;
menuitem = 0;
initscr();
drawmenu(menuitem);
keypad(stdscr, TRUE);
noecho();
do
{
raw();
nonl();
key = getch();
switch(key)
{
case KEY_DOWN:
menuitem++;
if(menuitem > MENUMAX - 1) menuitem = 0;
break;
case KEY_UP:
menuitem--;
if(menuitem < 0) menuitem = MENUMAX - 1;
break;
case KEY_ENTER:
mvaddstr(17, 25, "Test mesage!");
refresh();
break;
default:
break;
}
drawmenu(menuitem);
} while(key != '~');
echo();
endwin();
return 0;
}
The program has not provided for character-at-a-time (unbuffered) input, as described in the Initialization section of the ncurses manual page. Since it looks for special keys such as KEY_UP, that means it should use cbreak (rather than raw, which prevents ncurses from decoding special keys).
So I have most of my game finish up however, I want to save the score and name of the player to a text file and display it. So far the user input works and does save to a text file named score.txt however the input is invisible and you can't see what you are typing. Does anyone know why this is happening?
Main
int main()
{
DWORD mode; /* Preserved console mode */
INPUT_RECORD event; /* Input event */
BOOL EXITGAME = FALSE; /* Program termination flag */
unsigned int counter = 0; /* The number of times 'Esc' is pressed */
/* Get the console input handle */
HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );
/* Preserve the original console mode */
GetConsoleMode( hstdin, &mode );
/* Set to no line-buffering, no echo, no special-key-processing */
SetConsoleMode( hstdin, 0 );
srand ( time(NULL) ); //initialize the random seed
// Variables
int health = 2;
// Declare variable positions
player.x=1;
player.y=1;
treasure.x = 20;
treasure.y = 5;
treasure.z= 0;
traps.x = 1;
traps.y = 7;
traps.z = 0;
lives.x = 1;
lives.y = 9;
ofstream file;
int score = 0;
string name;
string line;
/*
while(treasure.z < 2)
{
treasure.x = (rand() % 24);
treasure.y = (rand() % 16);
if(treasure.x == 0 && treasure.y == 0)
{
treasure.z++;
}
}
while(traps.z < 3)
{
traps.x = (rand() % 24);
traps.y = (rand() % 16);
if(traps.x == 0 && traps.y == 0)
{
traps.z++;
}
}
while(lives.z < 2)
{
lives.x = (rand() % 24);
lives.y = (rand() % 16);
}
*/
clrscr();
setcolor(15);
while (!EXITGAME)
{
if (WaitForSingleObject( hstdin, 0 ) == WAIT_OBJECT_0) /* if kbhit */
{
DWORD count; /* ignored */
/* Get the input event */
ReadConsoleInput( hstdin, &event, 1, &count );
/* Only respond to key release events */
if ((event.EventType == KEY_EVENT)
&& !event.Event.KeyEvent.bKeyDown)
clrscr();
putmenu();
gotoxy(6,20);
cout<<"Lives: " << health;
gotoxy(6,22);
cout<<"Score: " << score;
Sleep(50);
switch (event.Event.KeyEvent.wVirtualKeyCode)
{
case VK_ESCAPE:
clrscr();
putend();
EXITGAME = TRUE;
break;
case VK_LEFT:
// left key move player left
moveleft();
break;
case VK_RIGHT:
// right key move player right
moveright();
break;
case VK_UP:
// up key move player up
moveup();
break;
case VK_DOWN:
// down key move player down
movedown();
break;
case VK_A:
// left key move player left
moveleft();
break;
case VK_D:
// right key move player right
moveright();
break;
case VK_W:
// up key move player up
moveup();
break;
case VK_S:
// down key move player down
movedown();
break;
}//switch
puttreasure();
puttraps();
putlives();
putplayer();
if((player.x == lives.x) && (player.y == lives.y))
{
health++;
lives.x = 0;
lives.y = 0;
}
if((player.x == traps.x) && (player.y == traps.y))
{
health--;
traps.x = 0;
traps.y = 0;
}
if((player.x == treasure.x)&& (player.y == treasure.y))
{
score += 100;
EXITGAME = true;
}
else if(health == 0)
{
EXITGAME = true;
}
if(EXITGAME == true)
{
score = score + (health * 100);
}
}
}
if(EXITGAME == true)
{
// clear screen
clrscr();
}
setcolor(10);
cout << "Enter your name ";
cin >> name;
ofstream out("score.txt");
out << name;
out << "\n";
out << score;
out.close();
/* if(file.is_open())
{
while( getline (file, line))
cout << line << '\n';
}*/
gotoxy(1,23);cout<<" ";
SetConsoleMode( hstdin, mode );
return 0;
}
/* Set to no line-buffering, no echo, no special-key-processing */
You turned off echo - means that the user wont see what they type
So I have a 2d map array printed out. I have 0's set as a space and 1 set as a wall. I have variables such as traps and health as well as the player. However, I am having trouble implementing a score as the two collide. I was wondering how I can detect when a player and life collide which would increment the health variable by 1. Right now it increments every time I move.
Thank you!
These are my variable functions
void putplayer()
{
setcolor(9);
gotoxy( player.x +mazex, player.y +mazey);
cout<<playersymbol;
setcolor(7);
}
void puttreasure()
{
setcolor(14);
gotoxy(treasure.x +mazex, treasure.y +mazey);
cout<<treasuresymbol;
setcolor(7);
}
void puttraps()
{
setcolor(14);
gotoxy( traps.x +mazex, traps.y +mazey);
cout<<trapssymbol;
setcolor(7);
}
void putlives()
{
setcolor(14);
gotoxy( lives.x +mazex, lives.y +mazey);
cout<<livessymbol;
setcolor(7);
}
void moveleft()
{
gotoxy(31,7);
cout<<"left key move player left \n\n";
if (maze[player.y][player.x-1]==0) player.x = player.x -1;
}
Then my Main, however some code I took out as it might not be relevant
int main()
{
DWORD mode; /* Preserved console mode */
INPUT_RECORD event; /* Input event */
BOOL EXITGAME = FALSE; /* Program termination flag */
// unsigned int counter = 0; /* The number of times 'Esc' is pressed */
/* Get the console input handle */
HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );
/* Preserve the original console mode */
GetConsoleMode( hstdin, &mode );
/* Set to no line-buffering, no echo, no special-key-processing */
SetConsoleMode( hstdin, 0 );
srand ( time(NULL) ); //initialize the random seed
// Variables
int health = 0;
// Declare variable positions
player.x=1;
player.y=1;
treasure.x = 1;
treasure.y = 3;
traps.x = 1;
traps.y = 7;
lives.x = 1;
lives.y = 9;
clrscr();
setcolor(15);
while (!EXITGAME)
{
if (WaitForSingleObject( hstdin, 0 ) == WAIT_OBJECT_0) /* if kbhit */
{
DWORD count; /* ignored */
/* Get the input event */
ReadConsoleInput( hstdin, &event, 1, &count );
/* Only respond to key release events */
if ((event.EventType == KEY_EVENT)
&& !event.Event.KeyEvent.bKeyDown)
clrscr();
putmenu();
gotoxy(6,20);
cout<<"Lives: " << health;
Sleep(100);
switch (event.Event.KeyEvent.wVirtualKeyCode)
{
case VK_ESCAPE:
EXITGAME = TRUE;
break;
case VK_LEFT:
// left key move player left
moveleft();
break;
case VK_RIGHT:
// right key move player right
moveright();
break;
case VK_UP:
// up key move player up
moveup();
break;
case VK_DOWN:
// down key move player down
movedown();
break;
case VK_A:
// left key move player left
moveleft();
break;
case VK_D:
// right key move player right
moveright();
break;
case VK_W:
// up key move player up
moveup();
break;
case VK_S:
// down key move player down
movedown();
break;
}//switch
putplayer();
puttreasure();
puttraps();
putlives();
}
if(maze[player.x][player.y] = maze[lives.x][lives.y]){
health++;
}
else if(maze[player.x][player.y] = maze[traps.x][traps.y]){
health--;
}
}
gotoxy(1,23);cout<<" ";
SetConsoleMode( hstdin, mode );
return 0;
}
You have the code
if(maze[player.x][player.y] = maze[lives.x][lives.y])
which sets the maze[player.x][player.y] to the value at maze[lives.x][lives.y], and then evaluates it (it will evaluate to true if 1 (wall), false if 0 (space)). I think you meant to have something like
if((player.x == lives.x) && (player.y == lives.y))
which will (as far as I can tell) evaluate if the player and the lives are at the same location.
I want to print an array of characters, these characters are underscores first.
Then the user can write characters on these underscores.I used gotoxy() but it doesn't work properly.
That is what i wrote:
int main(void)
{
char arr[20];
int i;
char ch;
clrscr();
for(i=0;i<=20;i++)
{
textattr(0x07);
cprintf("_");
}
do
{
for(i=0;i<=20;i++)
{
//gotoxy(i,0);
//ch = getche();
if( isprint(ch) == 1)
{
arr[i] = ch;
gotoxy(i,0);
//printf("%c",ch);
}
}
} while(i == 20);
getch();
return 0;
}
The first thing is this: You probably don't want to have all those calls to gotoxy, textattr and cprintf in your main function, since that is not what the main function is supposed to do.
It is much more likely that the main function's purpose is "to read some text from the user, presented nicely in an input field". So you should make this a function:
static int
nice_input_field(char *buf, size_t bufsize, int x, int y) {
int i, ch;
gotoxy(x, y);
for (i = 0; i < bufsize - 1; i++) {
cprintf("_");
}
i = 0;
gotoxy(x, y);
while ((ch = readkey()) != EOF) {
switch (ch) {
case '...': /* ... */
break;
case '\b': /* backspace */
cprintf("_");
i--;
gotoxy(x + i, y);
break;
case '\t': /* tabulator */
case '\n': /* enter, return */
buf[i] = '\0';
return 0; /* ok */
default: /* some hopefully printable character */
if (i == bufsize - 1) {
cprintf("\a"); /* beep */
} else {
buf[i++] = ch;
gotoxy(x + i, y);
cprintf("%c", buf[i]);
}
}
}
/* TODO: null-terminate the buffer */
return 0;
}
Printing an array of characters is fairly easy:
char* str = your_array;
while(*str) {
putc(*str++);
}
From memory that should print a string out to the screen.
Your code is very DOS-specific. There is not a good general solution to the problem of reading immediate input in a portable way. It does get asked quite often, so I think the C FAQ broke down and included an answer which you might want to seek out.
That said, I think your bug is that gotoxy(1, 1) is the upper corner of the screen, not 0,0. So you want gotoxy(i, 1)