/* ** starlanes v1.4.0 (29-Mar-1997) -- a space-age stock trading game ** ** Copyright (C) 1997 Brian "Beej" Hall ** with modifications by David Barnsdale 2004 and by ~jan6 2020 ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License ** as published by the Free Software Foundation; either version 2 ** of the License, or (at your option) any later version. ** ** This program 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 this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ** ** For more information, contact "beej@ecst.csuchico.edu" ** or "dejvid@zamir.net" and "dejvid@barnsdle.demon.co.uk" */ #include #include #include #include #include #include #include #include #include "common.h" /* color stuff: */ #define BLUE_ON_BLACK COLOR_PAIR(1) #define RED_ON_BLACK COLOR_PAIR(2) #define GREEN_ON_BLACK COLOR_PAIR(3) #define YELLOW_ON_BLACK COLOR_PAIR(4) #define MAGENTA_ON_BLACK COLOR_PAIR(5) #define CYAN_ON_BLACK COLOR_PAIR(6) #define WHITE_ON_BLACK COLOR_PAIR(7) #define YELLOW_ON_BLUE COLOR_PAIR(8) #define WHITE_ON_BLUE COLOR_PAIR(9) #define BLACK_ON_YELLOW COLOR_PAIR(10) #define BLACK_ON_WHITE COLOR_PAIR(11) #define BLACK_ON_RED COLOR_PAIR(12) #define BLACK_ON_BLUE COLOR_PAIR(13) #define BLACK_ON_GREEN COLOR_PAIR(14) #define MAP_TITLE (color?(BLACK_ON_WHITE):(A_REVERSE)) #define MAP_BORDER (color?(WHITE_ON_BLACK|A_BOLD):(A_BOLD)) #define MAP_SPACE (color?(WHITE_ON_BLACK):(A_NORMAL)) #define MAP_STAR (color?(YELLOW_ON_BLACK|A_BOLD):(A_BOLD)) #define MAP_NEWCO (color?(CYAN_ON_BLACK):(A_NORMAL)) #define MAP_BLACKHOLE (A_NORMAL) #define CO_A (color?(BLUE_ON_BLACK|A_BOLD):(A_NORMAL)) #define CO_B (color?(GREEN_ON_BLACK):(A_NORMAL)) #define CO_C (color?(YELLOW_ON_BLACK):(A_NORMAL)) #define CO_D (color?(RED_ON_BLACK):(A_NORMAL)) #define CO_E (color?(MAGENTA_ON_BLACK):(A_NORMAL)) #define GENERAL_TITLE (color?(WHITE_ON_BLUE|A_BOLD):(A_REVERSE)) #define GENERAL_TITLE_BLINK (color?(WHITE_ON_BLUE|A_BOLD|A_BLINK):(A_REVERSE|A_BLINK)) #define GENERAL_BORDER (color?(BLUE_ON_BLACK):(A_NORMAL)) #define GENERAL_TEXT (A_NORMAL) #define COINFO_TITLE (color?(BLACK_ON_GREEN):(A_REVERSE)) #define COINFO_TEXT (A_BOLD) #define STAND_TITLE (color?(YELLOW_ON_BLUE|A_BOLD):(A_REVERSE)) #define STAND_BORDER (color?(BLUE_ON_BLACK):(A_BOLD)) #define STAND_HEADER (color?(WHITE_ON_BLUE):(A_REVERSE)) #define MORE_COINFO_TITLE (color?(YELLOW_ON_BLUE|A_BOLD):(A_REVERSE)) #define MORE_COINFO_BORDER (color?(BLUE_ON_BLACK):(A_BOLD)) #define MORE_COINFO_HEADER (color?(WHITE_ON_BLUE):(A_REVERSE)) #define QUIT_TITLE (color?(BLACK_ON_RED):(A_REVERSE)) #define QUIT_BORDER (color?(RED_ON_BLACK):(A_BOLD)) #define QUIT_TEXT (color?(YELLOW_ON_BLACK|A_BOLD):(A_NORMAL)) /* macros to look at surrounding spaces on the map: */ #define up_obj(move) (((move)-MAPX < 0)?SPACE:map[(move)-MAPX]) #define down_obj(move) (((move)+MAPX >= MAPX*MAPY)?SPACE:map[(move)+MAPX]) #define left_obj(move) (((move)%MAPX)?map[(move)-1]:SPACE) #define right_obj(move) (((move)%MAPX == MAPX-1)?SPACE:map[(move)+1]) /* misc stuff: */ //#define INIT_CO_COST 100 /* initial company start cost */ //#define INIT_CASH 6000 /* initial player cash */ //#define SPLIT_PRICE 3000 /* when stocks split 2-1 */ //#define NUMMOVES 5 /* number of different moves a player gets */ //#define END_PERCENT 55 /* end when this much of the map is full */ #define DEF_LINES 25 /* default number of lines on screen */ #define DEF_COLUMNS 80 /* default number of columns on screen */ #define CR 13 /* various keys */ #define LF 10 #define BS 8 #define DEL 127 #define ESC 27 #define CTRL_L 12 #define CTRL_C 3 #define CTRL_Z 26 /* function prototypes: */ //extern void generate_nme(char *nme); //extern char placemove( PLAYER *pla,int *move, COMPANY *co, int turn,char *map, int Difficulty); //extern void ai_buy_sell(PLAYER *pla, COMPANY *com, int turn,char *map, int Difficulty); void initialize(void); void color_setup(void); void get_num_players(void); void showmap(void); void drawmap(int loc, char c); int get_move(void); /*called by main */ void show_coinfo(void); void more_coinfo(void); void do_move(int move); void do_merge(int *c1, int *c2, int *o1, int *o2); void holding_bonus(void); void holding_bonus(void); void buy_sell(void); int check_endgame(void); int count_used_sectors(void); void calc_cost(int cnum, int move, int n, int s, int w, int e); void new_co_announce(int newc); void suck_announce(int conum, int grown); void merge_announce(int c1, int c2); void xaction_announce(int c1, int c2); void split_announce(int conum); int co_avail(void); void clear_general(char *s,int blink); void center(WINDOW *win, int width, int row, char *s); int my_mvwgetstr(WINDOW *win, int y, int x, int max, int restricted, char *s); void redraw(void); void show_standings(char *title); void show_company_holdings(char *title); int order_compare(const void *v1, const void *v2); void quit_yn(void); void shutdown(void); void usage(void); /* ai functions typedef char nme[100]; void generate_nme( char *nme); char placemove(); */ /* global variables */ char *VERSION = "1.4.1"; char *VERSION_DATE = "13-February-2020"; char *ident = "$Id: starlanes.c 1.2.2 29-Mar-1997 beej@ecst.csuchico.edu $"; /* These two varibles must be cordinated with ai.c */ /* These probably could be constants but untill I'm sure they stay as variables. */ int MAPX = Mx; /* x dimension of map */ int MAPY = My; /* y dimension of map */ int LINES; /* lines in screen */ int COLUMNS; /* columns in screen */ char *map; /* pointer to the map data *//* char *mapc; /* copy of map for ai */ PLAYER *pl; /* pointer to array of players */ COMPANY *co; /* pointer to array of companies */ int numplayers,turn; /* number of players, whose turn it is */ WINDOW *mapwin,*general,*coinfo; /* pointers to the windows */ int color; /* true if we want color */ int sologame=0; /* set to true if nmr of playes is 0*/ int Difficulty; int main(int argc, char *argv[]) { int done = 0,move, colorforce=0, monoforce=0; switch(argc) { case 1: break; case 2: if (argv[1][1] == 'v') { fprintf(stderr,"Starlanes for ncurses v%s Copyright (C) by Brian \"Beej\" Hall %s\n",VERSION,VERSION_DATE); fprintf(stderr,"\nStarlanes comes with ABSOLUTELY NO WARRANTY. This is free\n"); fprintf(stderr,"software, and you are welcome to redistribute it under\n"); fprintf(stderr,"certain conditions. See the file COPYING for details.\n"); exit(1); } else if (argv[1][1] == 'c') colorforce = 1; else if (argv[1][1] == 'm') monoforce = 1; else usage(); break; default: usage(); } /* initscr */ initscr(); start_color(); if (colorforce) color = 1; else if (monoforce) color = 0; else color = has_colors(); if (color) color_setup(); raw(); /* init map, stocks */ srand(time(NULL)); initialize(); /* num players */ get_num_players(); clear(); attron(color?(YELLOW_ON_BLUE|A_BOLD):A_REVERSE); mvprintw(0,0," StarLanes "); attroff(color?(YELLOW_ON_BLUE|A_BOLD):A_REVERSE); attron(color?BLUE_ON_BLACK:A_NORMAL); printw("====================================================================="); attroff(color?BLUE_ON_BLACK:A_NORMAL); wnoutrefresh(stdscr); showmap(); show_coinfo(); do { move = get_move(); do_move(move); holding_bonus(); if ((done = check_endgame()) != 1) { if (pl[turn].ishuman==1) buy_sell(); //else { //mapc=*map ; //ai_buy_sell(pl,co,turn,map,Difficulty); //} turn = (++turn)%numplayers; fprintf(stderr,"\x07"); } } while (!done); shutdown(); return 0; } /* ** initialize() - sets up the map, players, and companies */ void initialize(void) { int i,j; char *lines, *columns; /* get the size of the screen: */ if ((lines=getenv("LINES"))==NULL || (columns=getenv("COLUMNS"))==NULL) { LINES = DEF_LINES; COLUMNS = DEF_COLUMNS; } else { LINES = atoi(lines); COLUMNS = atoi(columns); } /* allocate space for everything: */ if ((map=malloc(MAPX*MAPY)) == NULL) { fprintf(stderr,"starlanes: error mallocing space for map\n"); exit(1); } /* if ((mapc=malloc(MAPX*MAPY)) == NULL) { fprintf(stderr,"starlanes: error mallocing space for copy of map\n"); exit(1); } */ if ((co=calloc(1,NUMCO * sizeof(COMPANY))) == NULL) { fprintf(stderr,"starlanes: error mallocing space for companies\n"); exit(1); } if ((pl=calloc(1,MAXPLAYERS * sizeof(PLAYER))) == NULL) { fprintf(stderr,"starlanes: error mallocing space for players\n"); exit(1); } /* set up the map: */ for(i=0;i MAXPLAYERS); addch(c+'0'); numplayers = (int)c; if (numplayers == 0) numplayers=1; /* if (numplayers == 0){ numplayers=4; sologame=1; sprintf(s,"Please enter level (1 easiest,3 hardest)"); center(stdscr,COLUMNS ,11,s); refresh(); noecho();raw(); do { Difficulty = getch()-'0'; } while (Difficulty < 1 || Difficulty > 3); addch(Difficulty+'0'); } else */ sologame=0; srand(getpid()); /* reseed the dumb random number generator */ turn = rand()%numplayers; nl(); for(plnmr=0;plnmr0){ pl[plnmr].ishuman=0 /* here computer will generate a name */; /* generate_nme(pl[plnmr].name); } else { */ sprintf(s,"Player %d, enter your name: ",plnmr+1); center(stdscr,COLUMNS-8 ,12+sologame+plnmr,s); refresh(); my_mvwgetstr(stdscr,plnmr+12+sologame,49,20,0,pl[plnmr].name); /*getstr(pl[plnmr].name);*/ if (pl[plnmr].name[0] == '\0') plnmr--; // } } nonl(); } /* ** showmap() -- draws the map in the map window */ void showmap(void) { int i,j,attrs; wattron(mapwin,MAP_BORDER); box(mapwin,'|','-'); wattroff(mapwin,MAP_BORDER); wattron(mapwin,MAP_TITLE); center(mapwin,MAPX*3+2,0," Map "); wattroff(mapwin,MAP_TITLE); for(i=0;iNUMMOVES); echo(); for(i=0;i SPLIT_PRICE) split_announce(newc_type-'A'); else show_coinfo(); } } } /* ** do_merge() -- does all the nasty business behind a merge */ void do_merge(int *c1, int *c2, int *o1, int *o2) { int t,i,cb,cs,doswap=0; cb = *c1 - 'A'; cs = *c2 - 'A'; if (co[cs].size == co[cb].size) { /* if same size, check prices */ int pb=0,ps=0; for(i=0;i pb) /* if smaller co has higher worth, swap 'em */ doswap = 1; else if (ps == pb) /* if same price, choose rand */ doswap = rand()%2; } if (co[cs].size > co[cb].size || doswap) { /* cb = merger, cs = mergee */ t = cs; cs = cb; cb = t; } for(i=0;i SPLIT_PRICE) split_announce(cb); } /* ** calc_cost() -- adds value to a company based on surroundings and converts ** NEWCOs to the company */ void calc_cost(int cnum, int move, int n, int s, int w, int e) { if (n == STAR) co[cnum].price += STARCOST; /* stars */ if (s == STAR) co[cnum].price += STARCOST; if (w == STAR) co[cnum].price += STARCOST; if (e == STAR) co[cnum].price += STARCOST; if (n == BLACKHOLE) co[cnum].price += BLACKHOLECOST; /* black holes */ if (s == BLACKHOLE) co[cnum].price += BLACKHOLECOST; if (w == BLACKHOLE) co[cnum].price += BLACKHOLECOST; if (e == BLACKHOLE) co[cnum].price += BLACKHOLECOST; if (n == NEWCO) { /* starter companies */ map[move-MAPX] = cnum + 'A'; /*company represented by A is 0 and so on */ drawmap(move-MAPX,cnum+'A'); co[cnum].size++; co[cnum].price += NEWCOCOST; } if (s == NEWCO) { map[move+MAPX] = cnum + 'A'; drawmap(move+MAPX,cnum+'A'); co[cnum].size++; co[cnum].price += NEWCOCOST; } if (w == NEWCO) { map[move-1] = cnum + 'A'; drawmap(move-1,cnum+'A'); co[cnum].size++; co[cnum].price += NEWCOCOST; } if (e == NEWCO) { map[move+1] = cnum + 'A'; drawmap(move+1,cnum+'A'); co[cnum].size++; co[cnum].price += NEWCOCOST; } wnoutrefresh(mapwin); } /* ** new_co_announce() -- announce the coming of a new company */ void new_co_announce(int newc) { char s[80]; clear_general(" Special Announcement! ",1); wattron(general,A_BOLD); center(general,COLUMNS-2,2,"A new shipping company has been formed!"); sprintf(s,"Its name is %s",co[newc].name); center(general,COLUMNS-2,4,s); wattroff(general,A_BOLD); center(general,COLUMNS-2,7,"Press any key to continue..."); wnoutrefresh(general); noecho();raw(); doupdate(); while (getch() == CTRL_L) redraw(); } /* ** suck_announce() -- when a company gets drawn into a black hole (value < 0) */ void suck_announce(int conum, int grown) { int i; if (conum >= 0) { for(i=0;i= 0 && grown == 1) { /* already existed */ center(general,COLUMNS-2,2,"The company named"); center(general,COLUMNS-2,3,co[conum].name); center(general,COLUMNS-2,4,"has been sucked into a black hole!"); center(general,COLUMNS-2,6,"All players' holdings lost."); show_coinfo(); /* show change */ } else if (conum >= 0 && grown == 0) { /* was trying to start up */ center(general,COLUMNS-2,2,"The company that would have been named"); center(general,COLUMNS-2,3,co[conum].name); center(general,COLUMNS-2,4,"has been sucked into a black hole!"); } else { /* was only a starter company, not a real one yet */ center(general,COLUMNS-2,2,"The new company site just placed"); center(general,COLUMNS-2,3,"has been sucked into a black hole!"); } wattroff(general,A_BOLD); center(general,COLUMNS-2,8,"Press any key to continue..."); wnoutrefresh(general); noecho();raw(); doupdate(); while (getch() == CTRL_L) redraw(); } /* ** merge_announce() -- announce a merger */ void merge_announce(int c1, int c2) { clear_general(" Special Announcement! ",1); wattron(general,A_BOLD); center(general,COLUMNS-2,2,co[c2].name); wattroff(general,A_BOLD); center(general,COLUMNS-2,3,"has just been merged into"); wattron(general,A_BOLD); center(general,COLUMNS-2,4,co[c1].name); wattroff(general,A_BOLD); center(general,COLUMNS-2,7,"Press any key to continue..."); wnoutrefresh(general); noecho();raw(); doupdate(); while (getch() == CTRL_L) redraw(); } /* ** xaction_announce() -- announce transactions after a merger */ void xaction_announce(int c1, int c2) { int i,totalshares=0,newshares,bonus,totalholdings,percentage; clear_general(" Stock Transactions ",0); center(general,COLUMNS-2,2,co[c2].name); wattron(general,color?YELLOW_ON_BLUE|A_BOLD:A_REVERSE); mvwprintw(general,2,4,"=Player===============Old Stock===New Stock===Total Holdings===Bonus="); wattroff(general,color?YELLOW_ON_BLUE|A_BOLD:A_REVERSE); for(i=0;i= min && amt <= max) { pl[turn].cash += (-amt * co[cos[cursor]].price); pl[turn].holdings[cos[cursor]] += amt; } else { mvwprintw(general,cursor+3,40,"Invalid amount! "); wmove(general,cursor+3,55); wnoutrefresh(general); doupdate(); sleep(1); } mvwprintw(general,cursor+3,40," "); sprintf(s," %s (Cash: $%ld) ",pl[turn].name,pl[turn].cash); if (amt) show_coinfo(); pos1 = ((COLUMNS-2)-strlen(s))/2; pos2 = pos1 + strlen(s); wattron(general,GENERAL_TITLE); center(general,COLUMNS-2,0,s); wattroff(general,GENERAL_TITLE); wattron(general,GENERAL_BORDER); mvwaddstr(general,0,pos1-4,"////"); mvwaddstr(general,0,pos2,"////"); wattroff(general,GENERAL_BORDER); wmove(general,cursor+3,strlen(co[cos[cursor]].name)+20); wnoutrefresh(general); break; case ESC: done = 1; } /* switch */ if (cursor != newcur) { /* move cursor */ switch(cos[cursor]) { case 0: attrs = CO_A;break; case 1: attrs = CO_B;break; case 2: attrs = CO_C;break; case 3: attrs = CO_D;break; case 4: attrs = CO_E;break; } wattron(general,attrs); mvwprintw(general,cursor+3,20,co[cos[cursor]].name); wattroff(general,attrs); wattron(general,color?BLACK_ON_WHITE:A_REVERSE); mvwprintw(general, newcur+3, 20, co[cos[newcur]].name); wattroff(general,color?BLACK_ON_WHITE:A_REVERSE); wnoutrefresh(general); cursor = newcur; } } while(!done); } /* ** check_endgame() -- returns true if the game is over */ int check_endgame(void) { int sum=0,i,maptotal; for(i=0;i= END_PERCENT && maptotal <= MAPX*MAPY-4; } /* ** count_used_sectors() -- counts the number of non-empty sectors */ int count_used_sectors(void) { int maptotal, i; for(i=maptotal=0;i nw2) return -1; return 0; } /* ** more_coinfo() -- shows detailed company information */ void more_coinfo(void) { WINDOW *more_coinfo; int numco=0,cos[NUMCO],i,j,attrs,total; for(i=0;i