// inits ncurses so we can use wclear() and so on void init_ncurs() { // locale needs to be initialized for ncurses // "" sets "native" locale // TODO should we force some locale (UTF-8) // telnetd gives us "posix" locale char *locale = setlocale(LC_ALL, ""); if (locale == NULL) { syslog(LOG_WARNING, "setlocale failed, continuing anyways"); } /* ncurses init stuff */ initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); /* Function Keys - problems with some terminals */ curs_set(0); /* 0 = invisible, 1 = normal, 2 = really visible */ refresh(); /* checks that terminal size is big enough */ getmaxyx(stdscr, y_size, x_size); if (y_size < 24 || x_size < 80) { printw("This program needs 80x24 characters of screen size to run.\n"); printw("You currently have: %dx%d\n", x_size, y_size); printw("Enlarge your screen and press a key or this program might segfault.\n"); refresh(); todd_getchar(NULL); } /* checks that terminal supports color */ if (has_colors() == FALSE) { printw("Your terminal does not seem to support color!\n"); printw("The game will try to use color whenever possible\n"); printw("You might experience problems\n"); printw("Press a key to continue\n"); refresh(); todd_getchar(NULL); } /* // OLD CODE FOR COLOR STUFF, NOT USED AT THE MOMENT start_color(); init_pair(1, COLOR_RED, COLOR_BLACK); init_pair(2, COLOR_GREEN, COLOR_BLACK); init_pair(3, COLOR_BLUE, COLOR_BLACK); syntax for color usage: attron(COLOR_PAIR(1)); wprintw(stdscr, "color"); attroff(COLOR_PAIR(1)); */ }
/* Prints a message to game window * Waits user to press a key */ void ncurs_modal_msg(const char *fmt, ...) { /* this function has a getch(). Clear the command window so people won't get frustrated */ werase(command_win); wrefresh(command_win); // TODO do we want to clear the window first? va_list argp; va_start(argp, fmt); vwprintw(game_win, fmt, argp); va_end(argp); wprintw(game_win, "\n\n%s\n", _("Continue...")); wrefresh(game_win); todd_getchar(NULL); set_player_location(player.location); /* after getch, redraw command_win */ }
/* Prints a list of items to game window * returns the index of the item that player chooses * or -1 if player cancels or error occures * Parameters: * first_item - Pointer to the description of first item * stride - Memory offset between items * price_offset - Offset from first_item to integer price * or < 1 if no prices should be printed * count - number of items */ int ncurs_listselect(char **first_item, size_t stride, int price_offset, size_t count) { werase(command_win); wrefresh(command_win); for (size_t i = 0; i < count; i++) { // pointer is cast to void and back to calculate the position of next string void *base = first_item; base += stride*i; wprintw(game_win, "%c) %s", i+'a', *(char**)base); if (price_offset > 0) { base += price_offset; wprintw(game_win, "\t%d\n", *(int*)base); } else { wprintw(game_win, "\n"); } } // "Nevermind) wprintw(game_win, "x) %s\n", _("Nevermind")); wrefresh(game_win); unsigned char ch; while (true) { if (!todd_getchar(&ch)) { return -1; } if (ch == 'x') { /* "Nevermind */ return -1; } /* only accept numbers between 0 and count */ if (ch >= 'a' && ch < (char)(('a' + count)&0xFF)) { return ch - 'a'; } } return -1; /* control should never reach this point */ }
int fight_check_dead() { /* TODO: figure out the order of checking deaths: attacks are simultaneous. Iniative? */ // TODO enemy and player codes are almost identical, refactor into a function // bool check_chr_dead(Character *chr); or something bool enemy_dead; bool enemy_dead_elements; // loop through all enemies in combat for (int i = 0; i <= 2; i++) { enemy_dead = false; enemy_dead_elements = false; if (enemy_party.characters[i]->incombat) { /* check if enemy dies */ for (size_t j = 0; j < ELEM_COUNT; j++) { if (enemy_party.characters[i]->elements[j] <= 0) { enemy_dead = true; enemy_dead_elements = true; } } if (enemy_party.characters[i]->health <= 0) enemy_dead = true; // the current enemy is dead (characters[i]) if (enemy_dead) { enemy_party.characters[i]->incombat = false; // TODO: would it look better to display "DEAD" instead of erasing the box? werase(fight_stat_win[3+i]); wrefresh(fight_stat_win[3+i]); int money = 7; player.money += money; if (enemy_dead_elements) ncurs_log_sysmsg(_("%s has caused an elemental imbalance in %s"), player.name, enemy_party.characters[i]->name); ncurs_log_sysmsg(_("%s has killed %s!"), player.name, enemy_party.characters[i]->name); // TODO: share money from a kill someway ncurs_log_sysmsg(_("%s found %d coins from the body"), player.name, money); // if there's no more enemies left, return to dungeons. int alldead = 1; for (int i = 0; i <= 2; i++) if (enemy_party.characters[i]->incombat) alldead = 0; // if all enemies are dead, the battle is over if (alldead) { player.incombat = false; // don't return to combat any more ncurs_log_sysmsg(_("All enemies are slain! The battle is over")); return 1; } // If there's enemies left, the battle continues (i.e. do nothing) } } } /* check if player dies as well */ // TODO: loop through all players, not just yourself bool player_dead = false; bool player_dead_elements = false; for (size_t i = 0; i < 5; i++) { if (player.elements[i] <= 0) { player_dead = true; player_dead_elements = true; } } if (player.health <= 0) player_dead = true; if (player_dead) { wclear (game_win); if (player_dead_elements) // elements below 0, don't die but faint only { db_player_location(LOC_FAINTED); // TODO: which enemy.. ncurs_log_sysmsg(_("%s has caused an elemental imbalance in %s"), enemy_party.characters[0]->name, player.name); mvwprintw(game_win, 6, 0, _("The world around you starts to spin.\nYou sense a great imbalance inside you.")); wattron(game_win, A_BOLD); wattron(game_win, A_UNDERLINE); mvwprintw(game_win, 8, 0, _("You faint. TODO: \"come back in 8 hours??\"")); wattroff(game_win, A_BOLD); wattroff(game_win, A_UNDERLINE); } else // PERMADEATH { /* first, set the player location to "DEAD" */ db_player_location(LOC_DEAD); // TODO: which enemy ncurs_log_sysmsg(_("%s has killed %s!"), enemy_party.characters[0]->name, player.name); mvwprintw(game_win, 6, 0, _("The world fades around you as you fall to the ground, \nbleeding.")); wattron(game_win, A_BOLD); wattron(game_win, A_UNDERLINE); mvwprintw(game_win, 8, 0, _("You are dead.")); wattroff(game_win, A_BOLD); wattroff(game_win, A_UNDERLINE); } // common stuff to death // elemental imbalance wrefresh(game_win); todd_getchar(NULL); playing = false; } if (enemy_dead || player_dead) return 1; /* if enemy / player is dead, don't redraw combat stuff anymore */ else return 0; // redraw combat stuff }
// WINDOW *echowindow points to the window that processes the input // basically it's input_win during the game and stdscr before the game screen is loaded bool todd_getline(char **line, size_t *len, WINDOW *echowindow) { // curs_set displays a nice cursor for user convenience curs_set(1); chat_typing = 1; bool ret = false; size_t buf_len = 20; *line = malloc(buf_len); *len = 0; unsigned char c; do { bool rc = todd_getchar(&c); if (!rc || c == '\t') { // pressing TAB (\t) when in input mode is supposed to toggle chat // // however, pressing TAB when logging in (typing name or password) // would cause the program to quit // -> don't accept TAB when logging in // echowindow == input_win either when // todd_getline is called with input_win // OR they are both NULL (when logging in) // therefore, those two cases can't be true at the same time if (echowindow == input_win && input_win != NULL) { free(*line); *line = NULL; *len = 0; goto cleanup; } continue; // don't accept TAB unless in input_win, just skip it } if (c == '\b') // it's a backspace, go back a character { // if echowindow is NULL it means we're asking for the password // in this case, make backspace work int password = 0; if (echowindow == NULL) { password = 1; echowindow = stdscr; } // don't backspace on an empty string or the pointer will cause a segfault if ((*len) != 0) { // if it's a multibyte (scandic letters, § and so on) // len -1 and len -2 have negative values int multibyte = 0; if ((*line)[*len - 1] < 0) multibyte = 1; int y,x; // get current position of cursor to y and x getyx(echowindow,y,x); // move the cursor left by one wmove(echowindow, y,x-1); // blank it from screen and from buffer wechochar(echowindow, ' '); (*line)[*len] = '\0'; (*len)--; if (multibyte) // there's two chars in buffer, not one { (*line)[*len] = '\0'; (*len)--; } // by calling wechochar, the cursor moves right. move it back wmove(echowindow, y,x-1); wrefresh(echowindow); // if this was in the password field, change echowindow back to original value if (password) echowindow = NULL; } } else // it's just a normal character { if (buf_len <= *len) { buf_len += 20; *line = realloc(*line, buf_len); } (*line)[*len] = c; (*len)++; // echo the character, except when echowindow is NULL echo a * to stdscr // this is a hack: echowindow is NULL only when asking for a password // also, don't echo a \r if (echowindow == NULL && c != '\r') wechochar(stdscr, '*'); else if (c != '\r') // without this, todd_getline would eat the "Halt! who goes there" -message // when asking for player name wechochar(echowindow, c); } } while (c != '\r'); (*len)--; // strip trailing newline (*line)[*len] = '\0'; // insert null terminator curs_set(0); if (*len == 0) // it's an empty string.. return false; // else, return true ret = true; cleanup: if (echowindow == input_win) { werase(echowindow); wrefresh(echowindow); } chat_typing = 0; curs_set(0); return ret; }
int fight_check_dead() { /* TODO: figure out the order of checking deaths: attacks are simultaneous. Iniative? */ // TODO enemy and player codes are almost identical, refactor into a function // bool check_chr_dead(Character *chr); or something /* check if enemy dies */ bool enemy_dead = false; bool enemy_dead_elements = false; for (size_t i = 0; i < ELEM_COUNT; i++) { if (enemy.elements[i] <= 0) { enemy_dead = true; enemy_dead_elements = true; } } if (enemy.health <= 0) enemy_dead = true; if (enemy_dead) { int money = 7; int exp = 10; player.money += money; player.experience += exp; werase(game_win); if (enemy_dead_elements) ncurs_log_sysmsg(_("%s has caused an elemental imbalance in %s"), player.name, enemy.name); ncurs_log_sysmsg(_("%s has killed %s!"), player.name, enemy.name); ncurs_log_sysmsg(_("%s received %d coins and %d XP"), player.name, money, exp); ncurs_modal_msg( _("%s is slain!\n\nYou find %d coins on the corpse, and gain %d experience\n"), enemy.name, money, exp); ac_dungeons(); } /* check if player dies as well */ bool player_dead = false; bool player_dead_elements = false; for (size_t i = 0; i < 5; i++) { if (player.elements[i] <= 0) { player_dead = true; player_dead_elements = true; } } if (player.health <= 0) player_dead = true; if (player_dead) { wclear (game_win); if (player_dead_elements) // elements below 0, don't die but faint only { db_player_location(LOC_FAINTED); ncurs_log_sysmsg(_("%s has caused an elemental imbalance in %s"), enemy.name, player.name); mvwprintw(game_win, 6, 0, _("The world around you starts to spin.\nYou sense a great imbalance inside you.")); wattron(game_win, A_BOLD); wattron(game_win, A_UNDERLINE); mvwprintw(game_win, 8, 0, _("You faint. TODO: \"come back in 8 hours??\"")); wattroff(game_win, A_BOLD); wattroff(game_win, A_UNDERLINE); } else // PERMADEATH { /* first, set the player location to "DEAD" */ db_player_location(LOC_DEAD); ncurs_log_sysmsg(_("%s has killed %s!"), enemy.name, player.name); mvwprintw(game_win, 6, 0, _("The world fades around you as you fall to the ground, \nbleeding.")); wattron(game_win, A_BOLD); wattron(game_win, A_UNDERLINE); mvwprintw(game_win, 8, 0, _("You are dead.")); wattroff(game_win, A_BOLD); wattroff(game_win, A_UNDERLINE); } // common stuff to death // elemental imbalance wrefresh(game_win); todd_getchar(NULL); playing = false; } if (enemy_dead || player_dead) return 1; /* if enemy / player is dead, don't redraw combat stuff anymore */ else return 0; // redraw combat stuff }