305 lines
10 KiB
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);
|
|
}
|