timesynk/interface/curses.c

305 lines
10 KiB
C

#include <curses.h>
#include <string.h>
#include "curses.h"
#include <sys/time.h>
#include <signal.h>
#include <unistd.h> // read
#include "../helper.h"
#include "../player.h"
#include "../game.h"
#include "../wall.h"
#include "../main.h"
#include "../common.h"
#include "../context.h"
#include "../stubs.h"
#include "../tile.h" // for DoorTile, etc.
#include "../tiles/curses_tiles.h"
#include "../net/sockets.h"
#include "../console.h"
void tickSignal() {
gameLoop();
interfaceDraw();
}
void interfaceSetInterval(const char *input_string) {
if (input_string) {
int tickrate = atoi(input_string);
#if _WIN32 | _WIN64
KillTimer(NULL, win_timer);
win_timer = SetTimer(NULL, 1, tickrate, (TIMERPROC) tickSignal);
#else
getitimer(ITIMER_REAL, &interval);
interval.it_interval.tv_sec = tickrate / 1000;
interval.it_interval.tv_usec = (tickrate*1000) % 1000000;
setitimer(ITIMER_REAL, &interval, NULL);
#endif
}
}
int interfaceInit() {
/* set up our visual display */
if ((screen = initscr()) == NULL) {
perror("initscr() error'd");
return ERROR;
}
original_cursor = curs_set(0); // store original cursor position for restore and hide cursor
keypad(screen, TRUE); // enable arrow keys/keypad
noecho(); // turn off key echoing
nonl(); // do not do NL->CR/NL on output
cbreak(); // Handle char presses immediately, do not wait for \n
if (has_colors()) {
start_color();
// set up all possible color pairs using COLORS as our max (8 default)
int x, y;
for (x=0;x<COLORS;x++) {
for(y=0;y<COLORS;y++) {
init_pair(x*COLORS+y, x, y);
}
}
}
/* set up our file descriptors for input */
FD_ZERO(&master_fds);
FD_ZERO(&read_fds);
interface_fd = fileno(stdin);
FD_SET(interface_fd, &master_fds);
max_fd = interface_fd;
#if _WIN32 | _WIN64
win_timer = SetTimer(NULL, 1, 500, tickSignal);
#else
/* basically instead of using linux-only timer fds, we use SIGALRM for updating the game world instead. I don't know if I agree with it. */
if (signal(SIGALRM, (void (*)(int)) tickSignal) == SIG_ERR) {
consoleLog("Unable to catch SIGALARM for gameLoop, game is broken.");
}
interval.it_value.tv_sec = 500/1000;
interval.it_value.tv_usec = (500*1000) % 1000000;
interval.it_interval = interval.it_value;
if (setitimer(ITIMER_REAL, &interval, NULL) == -1) {
consoleLog("error calling setitmer(), game is broken");
}
#endif
/* add our commands */
consoleAddCommand("tickrate", interfaceSetInterval);
return SUCCESS;
}
int interfaceLoop() {
int i;
char data_buffer[2048];
if (is_networking) {
FD_SET(net_socket, &master_fds);
max_fd = net_socket;
}
while (is_running) {
read_fds = master_fds;
if (select(max_fd+1, &read_fds, NULL, NULL, NULL) == -1) {
consoleLog("error on select()");
}
for (i=0;i <= max_fd; i++) {
if (FD_ISSET(i, &read_fds)) {
if (i == interface_fd) {
if (read(interface_fd, data_buffer, 31) < 0) {
consoleLog("error, couldn't read() interface_fd/STDIN");
} else {
globalContext(data_buffer[0]);
}
} else if (i == net_socket) {
struct sockaddr_storage client_address;
socklen_t address_length = sizeof(client_address);
int new_fd = accept(net_socket, (struct sockaddr *)&client_address, &address_length);
if (new_fd == -1) {
// consoleLog("accept() error'd");
} else {
FD_SET(new_fd, &master_fds);
if (new_fd > max_fd) {
max_fd = new_fd;
}
char string[32];
char remote_ip[INET6_ADDRSTRLEN];
sprintf(string, "accept()'d, new client from %s on fd %d", inet_ntop(client_address.ss_family, get_in_addr((struct sockaddr *)&client_address), remote_ip, INET6_ADDRSTRLEN), new_fd);
consoleLog(string);
}
} else if (i > net_socket) { // handle existing client data
int bytes = 0;
if((bytes = recv(i, data_buffer, sizeof data_buffer, 0)) <= 0) {
if (bytes == 0) {
consoleLog("lost client");
} else {
consoleLog("recv() error'd");
}
close(i);
FD_CLR(i, &master_fds);
} else { // good data
//char string[sizeof data_buffer];
//sprintf(string, "client: %s", data_buffer);
//consoleLog(string);
consoleLog("client message");
}
}
}
}
interfaceDraw();
}
return 1;
}
void interfaceDraw() {
//clear();
int step_x = player->x - ((struct PlayerTile*)player->data)->vision;
int step_y = player->y - ((struct PlayerTile*)player->data)->vision;
int end_x = player->x + ((struct PlayerTile*)player->data)->vision;
int end_y = player->y + ((struct PlayerTile*)player->data)->vision;
float camera_offset_x = player->x - (80/2); // TODO: get term size
float camera_offset_y = player->y - (24/2);
while (step_x < end_x) {
step_y = player->y - ((struct PlayerTile*)player->data)->vision;
while (step_y < end_y) {
//if (visible_matrix[step_x][step_y] & TILE_VISIBLE) {
if (step_x >= 0 && step_y >= 0 && step_x < current_map->width && step_y < current_map->height) {
struct Tile *current_tile;
current_tile = &(current_map)->matrix[step_x][step_y];
while(current_tile) {
short tile_id = current_tile->id;
unsigned int type_id = current_tile->tid;
int color;
switch (type_id) {
case WALL:
color = curses_walls[tile_id].fg * COLORS + curses_walls[tile_id].bg;
attron(COLOR_PAIR(color) | curses_walls[tile_id].attr);
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, curses_walls[tile_id].ch);
attroff(COLOR_PAIR(color) | curses_walls[tile_id].attr);
break;
case FLOOR:
color = curses_walls[tile_id].fg * COLORS + curses_walls[tile_id].bg;
attron(COLOR_PAIR(color) | curses_walls[tile_id].attr);
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, '.');
attroff(COLOR_PAIR(color) | curses_walls[tile_id].attr);
break;
case DOOR:
// doors work by taking the original id(0) and adding the current state(0=closed,1=open,2=broken,3=missing). For one door, you need 4 individual tiles.
tile_id = tile_id*4;
tile_id += ((struct DoorTile*)(current_tile->data))->state;
color = curses_doors[tile_id].fg * COLORS + curses_doors[tile_id].bg;
attron(COLOR_PAIR(color) | curses_doors[tile_id].attr);
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, curses_doors[tile_id].ch);
attroff(COLOR_PAIR(color) | curses_doors[tile_id].attr);
break;
case PLAYER:
color = curses_players[tile_id].fg * COLORS + curses_players[tile_id].bg;
attron(COLOR_PAIR(color) | curses_players[tile_id].attr);
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, '@');
attroff(COLOR_PAIR(color) | curses_players[tile_id].attr);
break;
case NPC:
color = curses_npcs[tile_id].fg * COLORS + curses_npcs[tile_id].bg;
attron(COLOR_PAIR(color) | curses_npcs[tile_id].attr);
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, curses_npcs[tile_id].ch);
attroff(COLOR_PAIR(color) | curses_npcs[tile_id].attr);
break;
}
current_tile = current_tile->next;
}
} else {
mvwaddch(screen, step_y-camera_offset_y, step_x-camera_offset_x, ' ');
}
step_y++; // move down
}
step_x++; // move right
}
move(player->y-camera_offset_y, player->x-camera_offset_x);
if (current_context == &consoleContext) {
interfaceDrawConsole();
}
refresh();
}
void interfaceClose() {
wrefresh(screen);
delwin(screen);
endwin();
}
/* non-main() funcs */
void interfacePrint(const char *input_string) {
// TODO: "zero" out whole line before printing new one (so overlap is gone)
mvwaddstr(screen, 22, 0, input_string);
consoleLog(input_string);
}
void interfacePrintf(const char *input_string, ...) {
int *byte_location;
byte_location = &input_string+1; // our first arg, effectively
int byte_offset; // our memory reference used for getting argument offsets from the original string
char formatted_string[32];// start at 32?
//int formatted_string_length = 8;
int format_step = 0;
int input_step = 0;
char current_char = input_string[0];
formatted_string[0] = current_char;
int handle_format = 0;
while (current_char != '\0') {
input_step++;
current_char = input_string[input_step];
if (handle_format == 1) {
switch(current_char) {
case 'c': // for char
byte_offset = *byte_location++; // our arg location
handle_format = 0;
format_step++;
//formatted_string[format_step] = *byte_location;
formatted_string[format_step] = byte_offset;
// convert decimal to curses
break;
case 's':
handle_format = 0;
byte_offset = *byte_location++; // our arg location
while(byte_offset != '\0') {
format_step++;
formatted_string[format_step] = byte_offset;
byte_offset = *byte_location++; // our arg location
}
break;
}
} else if (current_char == '%') {
handle_format = 1;
} else {
format_step++;
formatted_string[format_step] = current_char;
}
}
mvwaddstr(screen, 22, 0, formatted_string);
}
void interfaceDrawConsole() {
int line;
struct ConsoleEntry *entry;
entry = console_last_entry;
for (line = 7;line >= 0;line--) {
move(line, 0);
if (entry->size > 0) {
addstr(consoleGetEntryString(entry));
}
if (entry->prev)
entry = entry->prev;
hline(' ', 80);
}
move(9, 0);
hline('-', 80);
move(8, 0);
hline(' ', 80);
move(8, 0);
addstr("> ");
addstr(console_cmd);
}