You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
9.9 KiB
439 lines
9.9 KiB
#include <ncurses.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#define MAX_PROJECTILES 2
|
|
#define MAX_ZOMBIES 1000
|
|
#define MAX_ALIVE 10
|
|
|
|
int scrRows,scrCols;
|
|
int viewportWinRows,viewportWinCols;
|
|
int lastPressed;
|
|
|
|
WINDOW* viewportWin;
|
|
|
|
WINDOW* scoreWin;
|
|
|
|
struct Projectile{
|
|
int isAlive;
|
|
char symbol;
|
|
int curXpos;
|
|
int curYpos;
|
|
int prevYpos;
|
|
int prevXpos;
|
|
int nextYpos;
|
|
int nextXpos;
|
|
int range;
|
|
char direction;
|
|
int distanceTraveled;
|
|
};
|
|
struct Projectile projectiles[MAX_PROJECTILES];
|
|
|
|
struct Weapon{
|
|
char name[10];
|
|
int range;
|
|
int maxCapacity;
|
|
int curCapacity;
|
|
char projectileSymbol;
|
|
char symbols[8];
|
|
};
|
|
|
|
struct Entity {
|
|
char *type;
|
|
char symbol;
|
|
int curYpos, curXpos;
|
|
int nextYpos, nextXpos;
|
|
struct Weapon weapons[8];
|
|
int curWeapon;
|
|
int isAlive;
|
|
};
|
|
|
|
|
|
struct Entity player = {.type="player",.symbol='@', .curYpos=1, .curXpos=1, .nextYpos=1, .nextXpos=1 ,.isAlive=1};
|
|
struct Entity allEntities[MAX_ALIVE];
|
|
|
|
struct Weapon pistol = {.name="Pistol", .range=3, .maxCapacity=12, .curCapacity=12, .symbols="-\\|/-\\|/"};
|
|
|
|
|
|
struct Entity * collisionDetect(int objYpos, int objXpos){
|
|
int i;
|
|
for(i=1; i<MAX_ALIVE; i++){
|
|
if(allEntities[i].isAlive){
|
|
if(objYpos==allEntities[i].curYpos && objXpos==allEntities[i].curXpos){
|
|
return &allEntities[i];
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void moveProjectiles(){
|
|
int i;
|
|
for(i=0; i<MAX_PROJECTILES; i++){
|
|
if(projectiles[i].isAlive){
|
|
projectiles[i].prevYpos=projectiles[i].curYpos;
|
|
projectiles[i].prevXpos=projectiles[i].curXpos;
|
|
|
|
switch(projectiles[i].direction){
|
|
case 'w':{
|
|
projectiles[i].curYpos--;
|
|
break;
|
|
}
|
|
|
|
case 'a':{
|
|
projectiles[i].curXpos--;
|
|
break;
|
|
}
|
|
|
|
case 's':{
|
|
projectiles[i].curYpos++;
|
|
break;
|
|
}
|
|
|
|
case 'd':{
|
|
projectiles[i].curXpos++;
|
|
break;
|
|
}
|
|
|
|
}
|
|
projectiles[i].distanceTraveled++;
|
|
mvwaddch(viewportWin, projectiles[i].curYpos, projectiles[i].curXpos, projectiles[i].symbol);
|
|
struct Entity * hitEntity = collisionDetect(projectiles[i].curYpos, projectiles[i].curXpos);
|
|
if(hitEntity){
|
|
if(!strcmp(hitEntity->type,"zombie")){
|
|
projectiles[i].isAlive=0;
|
|
hitEntity->isAlive=0;
|
|
mvwaddch(viewportWin, allEntities[i].curYpos, allEntities[i].curXpos, ' ');
|
|
}
|
|
}
|
|
if(mvwinch(viewportWin,projectiles[i].prevYpos, projectiles[i].prevXpos)==projectiles[i].symbol){
|
|
mvwaddch(viewportWin, projectiles[i].prevYpos, projectiles[i].prevXpos, ' ');
|
|
}
|
|
if(projectiles[i].distanceTraveled>=projectiles[i].range || projectiles[i].curYpos > viewportWinRows || projectiles[i].curYpos < 0 || projectiles[i].curXpos > viewportWinCols || projectiles[i].curXpos < 0){
|
|
projectiles[i].isAlive=0;
|
|
mvwaddch(viewportWin, projectiles[i].curYpos, projectiles[i].curXpos, ' ');
|
|
}
|
|
usleep(30000);
|
|
}
|
|
else{
|
|
mvwaddch(viewportWin, projectiles[i].curYpos, projectiles[i].curXpos, ' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
void fireWeapon(struct Weapon *weaponRef, char *direction){
|
|
struct Projectile projectile;
|
|
if (!strcmp(weaponRef->name,"Pistol")){
|
|
projectile.range=20;
|
|
}
|
|
projectile.isAlive=1;
|
|
projectile.distanceTraveled=0;
|
|
projectile.curYpos=allEntities[0].curYpos;
|
|
projectile.curXpos=allEntities[0].curXpos;
|
|
if(!strcmp(direction,"up")){
|
|
projectile.range*=.4;
|
|
projectile.direction='w';
|
|
projectile.symbol=weaponRef->symbols[2];
|
|
}
|
|
if(!strcmp(direction,"left")){
|
|
projectile.direction='a';
|
|
projectile.symbol=weaponRef->symbols[0];
|
|
}
|
|
if(!strcmp(direction,"down")){
|
|
projectile.range*=.4;
|
|
projectile.direction='s';
|
|
projectile.symbol=weaponRef->symbols[6];
|
|
}
|
|
if(!strcmp(direction,"right")){
|
|
projectile.direction='d';
|
|
projectile.symbol=weaponRef->symbols[4];
|
|
}
|
|
int i;
|
|
for(i=0;i<MAX_PROJECTILES;i++){
|
|
if(!projectiles[i].isAlive && !projectiles[i+1].isAlive){
|
|
projectiles[i]=projectile;
|
|
allEntities[0].weapons[allEntities[0].curWeapon].curCapacity--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawScore(){
|
|
werase(scoreWin);
|
|
box(scoreWin, 0, 0);
|
|
mvwprintw(scoreWin, 1, 1, "Current Weapon: %s",allEntities[0].weapons[allEntities[0].curWeapon].name);
|
|
mvwprintw(scoreWin, 2, 1, "Ammo: %d/%d",allEntities[0].weapons[allEntities[0].curWeapon].curCapacity,allEntities[0].weapons[allEntities[0].curWeapon].maxCapacity);
|
|
wnoutrefresh(scoreWin);
|
|
}
|
|
|
|
void drawViewport(){
|
|
box(viewportWin,0,0);
|
|
int i;
|
|
for(i=0; i<MAX_ALIVE; i++){
|
|
if(allEntities[i].isAlive){
|
|
mvwaddch(viewportWin, allEntities[i].curYpos, allEntities[i].curXpos, ' ');
|
|
mvwaddch(viewportWin, allEntities[i].nextYpos, allEntities[i].nextXpos, allEntities[i].symbol);
|
|
allEntities[i].curYpos = allEntities[i].nextYpos;
|
|
allEntities[i].curXpos = allEntities[i].nextXpos;
|
|
}
|
|
}
|
|
wnoutrefresh(viewportWin);
|
|
doupdate();
|
|
}
|
|
|
|
struct possiblePos{
|
|
int x;
|
|
int y;
|
|
}openPos;
|
|
|
|
int getOpenPos(){
|
|
int chooseAnother=1;
|
|
while(chooseAnother){
|
|
spaceExists:
|
|
openPos.y=(rand()%(viewportWinRows-1)+1);
|
|
openPos.x=(rand()%(viewportWinCols-1)+1);
|
|
if(mvwinch(viewportWin,openPos.y,openPos.x)==' '){
|
|
int i;
|
|
for(i=1;i<MAX_ALIVE;i++){
|
|
if(allEntities[i].isAlive && allEntities[i].curYpos==openPos.y && allEntities[i].curXpos==openPos.x){
|
|
chooseAnother=1;
|
|
}
|
|
else{
|
|
chooseAnother=0;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
int x,y;
|
|
for(y=0;y<viewportWinRows;y++){
|
|
for(x=0;x<viewportWinCols;x++){
|
|
if(mvwinch(viewportWin,y,x)==' '){
|
|
chooseAnother=1;
|
|
goto spaceExists;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
void addZombie(){
|
|
if(getOpenPos()){
|
|
int i;
|
|
for(i=0;i<MAX_ALIVE-1;i++){
|
|
if(!allEntities[i].isAlive){
|
|
struct Entity zombie;
|
|
zombie.curYpos=openPos.y;
|
|
zombie.curXpos=openPos.x;
|
|
zombie.nextYpos=openPos.y;
|
|
zombie.nextXpos=openPos.x;
|
|
zombie.type="zombie";
|
|
zombie.symbol='Z';
|
|
zombie.isAlive=1;
|
|
allEntities[i]=zombie;
|
|
mvwaddch(viewportWin, allEntities[i].curYpos, allEntities[i].curXpos, allEntities[i].symbol);
|
|
wnoutrefresh(viewportWin);
|
|
doupdate();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void initializeWorld() {
|
|
curs_set(0);
|
|
getmaxyx(stdscr,scrRows,scrCols);
|
|
viewportWin=newwin(scrRows-10,scrCols,0,0);
|
|
getmaxyx(viewportWin,viewportWinRows,viewportWinCols);
|
|
scoreWin=newwin(10,scrCols,scrRows-10,0);
|
|
|
|
allEntities[0]=player;
|
|
allEntities[0].curYpos=(rand()%(viewportWinRows-2))+1;
|
|
allEntities[0].curXpos=(rand()%(viewportWinCols-2))+1;
|
|
allEntities[0].nextYpos=allEntities[0].curYpos;
|
|
allEntities[0].nextXpos=allEntities[0].curXpos;
|
|
}
|
|
|
|
struct NextPos{
|
|
int x;
|
|
int y;
|
|
}nextPos;
|
|
|
|
void getNextPos(int y, int x, int y2, int x2){
|
|
int w = x2 - x;
|
|
int h = y2 - y;
|
|
int dx1 = 0, dy1 = 0, dx2 = 0, dy2 =0;
|
|
if (w<0){
|
|
dx1 = -1;
|
|
}
|
|
else if(w>0){
|
|
dx1 = 1;
|
|
}
|
|
if (h<0){
|
|
dy1 = -1;
|
|
}
|
|
else if(h>0){
|
|
dy1 = 1;
|
|
}
|
|
if (w<0){
|
|
dx2 = -1;
|
|
}
|
|
else if(w>0){
|
|
dx2 = 1;
|
|
}
|
|
int longest = abs(w);
|
|
int shortest = abs(h);
|
|
if (!(longest>shortest)) {
|
|
longest = abs(h);
|
|
shortest = abs(w);
|
|
if (h<0){
|
|
dy2 = -1;
|
|
}
|
|
else if (h>0){
|
|
dy2 = 1;
|
|
}
|
|
dx2 = 0;
|
|
}
|
|
int numerator = longest >> 1;
|
|
numerator += shortest;
|
|
if (!(numerator<longest)) {
|
|
numerator -= longest;
|
|
x += dx1;
|
|
y += dy1;
|
|
} else {
|
|
x += dx2;
|
|
y += dy2;
|
|
}
|
|
nextPos.x = x;
|
|
nextPos.y = y;
|
|
}
|
|
|
|
void moveZombies(){
|
|
int i;
|
|
for(i=1;i<MAX_ALIVE;i++){
|
|
if(allEntities[i].isAlive){
|
|
int moveCheck = rand()%10000;
|
|
if(moveCheck==1){
|
|
getNextPos(allEntities[i].curYpos,allEntities[i].curXpos,allEntities[0].curYpos,allEntities[0].curXpos);
|
|
allEntities[i].nextYpos = nextPos.y;
|
|
allEntities[i].nextXpos = nextPos.x;
|
|
if(allEntities[i].nextYpos < 1 || allEntities[i].nextYpos>viewportWinRows-2){
|
|
allEntities[i].nextYpos = allEntities[i].curYpos;
|
|
}
|
|
if(allEntities[i].nextXpos < 1 || allEntities[i].nextXpos>viewportWinCols-2){
|
|
allEntities[i].nextXpos = allEntities[i].curXpos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
srand(time(NULL));
|
|
initscr();
|
|
cbreak();
|
|
keypad(stdscr, TRUE);
|
|
noecho();
|
|
initializeWorld();
|
|
nodelay(stdscr, TRUE);
|
|
int ch;
|
|
allEntities[0].weapons[0]=pistol;
|
|
allEntities[0].curWeapon=0;
|
|
while(1) {
|
|
drawScore();
|
|
drawViewport();
|
|
//addZombie();
|
|
moveProjectiles();
|
|
moveZombies();
|
|
doupdate();
|
|
|
|
ch = getch();
|
|
switch(ch){
|
|
case 'w':
|
|
case 'W':{
|
|
if(allEntities[0].curYpos > 1){
|
|
allEntities[0].nextYpos--;
|
|
if(allEntities[0].nextYpos < 0){
|
|
allEntities[0].nextYpos = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 's':
|
|
case 'S':{
|
|
if(allEntities[0].curYpos < viewportWinRows - 2){
|
|
allEntities[0].nextYpos++;
|
|
if(allEntities[0].nextYpos > viewportWinRows-1){
|
|
allEntities[0].nextYpos = viewportWinRows-1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'a':
|
|
case 'A':{
|
|
if(allEntities[0].curXpos > 1){
|
|
allEntities[0].nextXpos--;
|
|
if(allEntities[0].nextXpos < 0){
|
|
allEntities[0].nextXpos = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'd':
|
|
case 'D':{
|
|
if(allEntities[0].curXpos < scrCols-2){
|
|
allEntities[0].nextXpos++;
|
|
if(allEntities[0].nextXpos > viewportWinCols-1){
|
|
allEntities[0].nextXpos = viewportWinCols-1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'z':
|
|
case 'Z':{
|
|
addZombie();
|
|
break;
|
|
}
|
|
case KEY_UP:{
|
|
if(allEntities[0].weapons[allEntities[0].curWeapon].curCapacity>0){
|
|
fireWeapon(&pistol, "up");
|
|
}
|
|
break;
|
|
}
|
|
case KEY_LEFT:{
|
|
if(allEntities[0].weapons[allEntities[0].curWeapon].curCapacity>0){
|
|
fireWeapon(&pistol, "left");
|
|
}
|
|
break;
|
|
}
|
|
case KEY_DOWN:{
|
|
if(allEntities[0].weapons[allEntities[0].curWeapon].curCapacity>0){
|
|
fireWeapon(&pistol, "down");
|
|
}
|
|
break;
|
|
}
|
|
case KEY_RIGHT:{
|
|
if(allEntities[0].weapons[allEntities[0].curWeapon].curCapacity>0){
|
|
fireWeapon(&pistol, "right");
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if (ch != ERR) {lastPressed=ch;};
|
|
break;
|
|
}
|
|
struct Entity * hitEntity = collisionDetect(allEntities[0].curYpos, allEntities[0].curXpos);
|
|
if(hitEntity){
|
|
if(!strcmp(hitEntity->type,"zombie")){
|
|
mvwprintw(viewportWin,1,10,"TEST");
|
|
allEntities[0].isAlive=0;
|
|
mvwaddch(viewportWin, allEntities[0].curYpos, allEntities[0].curXpos, ' ');
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|