timesynk/interface/sdl.c

558 lines
19 KiB
C

#include <SDL/SDL.h>
#if !defined (__APPLE__)
#include <SDL/SDL_image.h>
#else
#include <SDL_image.h>
#endif
#include "sdl.h"
#include "../stubs.h"
#include "../main.h"
#include "../common.h"
#include "../player.h"
#include "../game.h"
#include "../context.h"
#include "../tiles/tiles.h"
#include "../tile.h"
#include "../console.h"
#include "../net/sockets.h"
void interfaceSetInterval(const char *input_string) {
if (input_string) {
int new_tickrate = atoi(input_string);
SDL_RemoveTimer(timer_id);
if (new_tickrate > 0) {
tickrate = new_tickrate;
timer_id = SDL_AddTimer(tickrate, pushTickEvent, NULL);
}
}
}
static int networkThread(void *nada) {
int reuse = 1;
int net_socket;
struct sockaddr_in server_address;
if ((net_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
consoleLog("socket() success'd");
return ERROR;
}
consoleLog("socket() success'd");
if (setsockopt(net_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0) {
consoleLog("setsockopt() error'd");
}
consoleLog("setsockopt() success'd");
//fcntl(net_socket, F_SETFL, O_NONBLOCK);
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(31337);
if (bind(net_socket, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
consoleLog("bind() error'd");
return ERROR;
}
consoleLog("bind() success'd");
if (listen(net_socket, 32) < 0) {
consoleLog("listen() error'd");
return ERROR;
}
consoleLog("listen() success'd");
char test_string[31];
sprintf(test_string, "server running on fd %d!", net_socket);
consoleLog(test_string);
/******/
// fd_set master_fds;
// fd_set read_fds;
// int max_fd;
int i; // current fd in for
char data_buffer[2048];
/* set up our fds */
FD_ZERO(&master_fds);
FD_ZERO(&read_fds);
FD_SET(net_socket, &master_fds);
max_fd = net_socket;
SDL_Event event;
SDL_UserEvent user_event;
user_event.type = SDL_USEREVENT;
user_event.code = EVENT_NET;
user_event.data1 = NULL;
user_event.data2 = NULL;
event.type = SDL_USEREVENT;
event.user = user_event;
while(1 == 1) {
//while (is_networking) {
read_fds = master_fds;
if (select(max_fd+1, &read_fds, NULL, NULL, NULL) == -1) {
consoleLog("error on network select()");
}
for (i=0;i <= max_fd; i++) {
if (FD_ISSET(i, &read_fds)) {
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 remote_ip[INET6_ADDRSTRLEN];
event.user.data1 = (void *)new_fd;
event.user.data2 = (void *)inet_ntop(client_address.ss_family, get_in_addr((struct sockaddr *)&client_address), remote_ip, INET6_ADDRSTRLEN);
event.user.code = EVENT_NET_ACCEPT;
SDL_PushEvent(&event);
}
} else if (i == client_socket) {
int bytes = 0;
if((bytes = recv(i, data_buffer, sizeof data_buffer, 0)) <= 0) {
if (bytes == 0) {
event.user.code = EVENT_NET_CLIENT_LOST;
event.user.data1 = (void *)i;
} else {
event.user.code = EVENT_NET_CLIENT_ERROR;
event.user.data1 = (void *)i;
}
SDL_PushEvent(&event);
close(i);
FD_CLR(i, &master_fds);
} else {
data_buffer[bytes] = '\0'; // TEMP
struct NetMessage *net_message;
net_message = malloc(sizeof(struct NetMessage));
net_message->fd = i;
net_message->size = bytes;
net_message->data = (void *)data_buffer;
event.user.code = EVENT_NET_CLIENT_RECV;
event.user.data1 = (void *)i;
event.user.data2 = (void *)net_message;
SDL_PushEvent(&event);
}
} 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) {
event.user.code = EVENT_NET_LOST;
event.user.data1 = (void *)i;
} else {
event.user.code = EVENT_NET_ERROR;
event.user.data1 = (void *)i;
}
SDL_PushEvent(&event);
close(i);
FD_CLR(i, &master_fds);
} else { // good data
data_buffer[bytes] = '\0'; // TEMP
struct NetMessage *net_message;
net_message = malloc(sizeof(struct NetMessage));
net_message->fd = i;
net_message->size = bytes;
net_message->data = (void *)data_buffer;
event.user.code = EVENT_NET_RECV;
event.user.data1 = (void *)i;
event.user.data2 = (void *)net_message;
SDL_PushEvent(&event);
}
}
}
}
}
return 0;
}
Uint32 pushTickEvent(Uint32 interval, void *param) {
SDL_Event event;
SDL_UserEvent user_event;
user_event.type = SDL_USEREVENT;
user_event.code = EVENT_TICK;
user_event.data1 = NULL;
user_event.data2 = NULL;
event.type = SDL_USEREVENT;
event.user = user_event;
SDL_PushEvent(&event);
return interval;
}
int interfaceInit() {
// TODO: read OS's best fullscreen resolution and use it
video_width = 800;
video_height = 600;
video_mode = 0;
// Load it up!
// since we use threads, Mac OS 10+ support only. Also threaded os.
#if _WIN32 | _WIN64
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTTHREAD);
#else
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER);
#endif
// Enable Unicode, for later text input
SDL_EnableUNICODE(SDL_ENABLE);
// Set up our SDL Window
if (interfaceVideoSet())
return ERROR;
SDL_WM_SetCaption(NAME, NULL);
consoleAddCommand("tickrate", interfaceSetInterval);
consoleAddCommand("set_video", interfaceVideoSetSize);
camera_surface = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
/* load our surfaces from memory */
font_spritesheet = IMG_Load_RW(SDL_RWFromMem(&font_images, font_images_length), 1);
player_spritesheet = IMG_Load_RW(SDL_RWFromMem(&player_images, player_images_length), 1);
npc_spritesheet = IMG_Load_RW(SDL_RWFromMem(&npc_images, npc_images_length), 1);
wall_spritesheet = IMG_Load_RW(SDL_RWFromMem(&wall_images, wall_images_length), 1);
floor_spritesheet = IMG_Load_RW(SDL_RWFromMem(&floor_images, floor_images_length), 1);
door_spritesheet = IMG_Load_RW(SDL_RWFromMem(&door_images, door_images_length), 1);
shadow_spritesheet = IMG_Load_RW(SDL_RWFromMem(&shadow_images, shadow_images_length), 1);
// Fill our screen w/ black
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 32, 128, 64));
// Update!
SDL_Flip(screen);
/* now we load up our timer, woo */
tickrate = 500;
timer_id = SDL_AddTimer(tickrate, pushTickEvent, NULL);
/* now we spawn a network thread that will push user events to SDL */
network_thread = SDL_CreateThread(networkThread, NULL);
return SUCCESS;
}
/* draw the player's current view */
void cameraDraw() {
SDL_Rect camera_rect = {0, 0, screen->w, screen->h};
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;
// TODO: The camera's center offset should change based upon distance to the map's width/height!
// TODO: Use zoomed copies of spritesheets for tile zooming based on player vision.
float camera_offset_x = (player->x+1)*TILE_WIDTH - (camera_rect.w/2);
float camera_offset_y = (player->y+1)*TILE_WIDTH - (camera_rect.h/2);
while (step_x < end_x) {
step_y = player->y - ((struct PlayerTile*)player->data)->vision;
while (step_y < end_y) {
// TODO: draw the layer immediately in front of the player at partial translucency
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];
int tile_id = current_tile->id;
while(current_tile) {
int x_offset = current_tile->id / 16; // 16 tiles across in spritesheet
int y_offset = current_tile->id - (x_offset*16);
SDL_Rect sprite_offset = { x_offset*TILE_WIDTH, y_offset*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT};
SDL_Rect tile_rect = {(step_x*TILE_WIDTH)-camera_offset_x, (step_y*TILE_WIDTH)-camera_offset_y, TILE_WIDTH, TILE_HEIGHT};
switch (current_tile->tid) {
case WALL:
SDL_BlitSurface(wall_spritesheet, &sprite_offset, camera_surface, &tile_rect);
break;
case FLOOR:
SDL_BlitSurface(floor_spritesheet, &sprite_offset, camera_surface, &tile_rect);
break;
case NPC:
SDL_BlitSurface(npc_spritesheet, &sprite_offset, camera_surface, &tile_rect);
break;
case DOOR:
tile_id = tile_id*4;
tile_id += ((struct DoorTile*)current_tile->data)->state;
y_offset = tile_id / 16;
x_offset = tile_id - (y_offset*16);
sprite_offset.x = x_offset*TILE_WIDTH;
sprite_offset.y = y_offset*TILE_HEIGHT;
SDL_BlitSurface(door_spritesheet, &sprite_offset, camera_surface, &tile_rect);
break;
case PLAYER:
y_offset = current_tile->id / TOTAL_CLASSES;
x_offset = current_tile->id - (y_offset*TOTAL_CLASSES);
sprite_offset.x = x_offset*TILE_WIDTH;
sprite_offset.y = y_offset*TILE_HEIGHT;
SDL_BlitSurface(shadow_spritesheet, NULL, camera_surface, &tile_rect);
SDL_BlitSurface(player_spritesheet, &sprite_offset, camera_surface, &tile_rect);
break;
}
current_tile = current_tile->next;
}
} else {
SDL_Rect tile_rect = {(step_x*TILE_WIDTH)-camera_offset_x, (step_y*TILE_WIDTH)-camera_offset_y, TILE_WIDTH, TILE_HEIGHT};
SDL_FillRect(camera_surface, &tile_rect, SDL_MapRGB(camera_surface->format, 0, 0, 0));
}
step_y++; // move down
}
step_x++; // move right
}
//SDL_BlitSurface(interfaceScaleSurface(camera_surface, camera_surface->w*(camera_surface->w/camera_rect.w), camera_surface->h*(camera_surface->h/camera_rect.h)), NULL, screen, &camera_rect);
SDL_BlitSurface(camera_surface, NULL, screen, &camera_rect);
char test[12];
itoa(tickrate, test, 10);
interfaceDrawString(test, screen->w-strlen(test)*TILE_WIDTH, 0);
}
int interfaceLoop() {
while (SDL_WaitEvent(&event)) {
switch(event.type) {
case SDL_USEREVENT:
if (event.user.code == EVENT_TICK) { // timer
gameLoop();
} else if (event.user.code == EVENT_NET_ACCEPT) { // net
char string[127];
sprintf(string, "net accept of %s on fd %d", (char*)event.user.data2, (int*)event.user.data1);
consoleLog(string);
} else if (event.user.code == EVENT_NET_RECV) {
char string[255];
struct NetMessage *net_message = (struct NetMessage*)event.user.data2;
sprintf(string, "client(%d): %s", net_message->fd, net_message->data);
consoleLog(string);
sprintf(string, "%s", net_message->data);
send(net_message->fd, string, net_message->size, 0);
free(net_message);
} else if (event.user.code == EVENT_NET_LOST) {
char string[23];
sprintf(string, "net lost for fd %d", (int*)event.user.data1);
consoleLog(string);
} else if (event.user.code == EVENT_NET_ERROR) {
char string[23];
sprintf(string, "net error for fd %d", (int*)event.user.data1);
consoleLog(string);
} else if (event.user.code == EVENT_NET_CLIENT_RECV) {// begin client
char string[255];
struct NetMessage *net_message = (struct NetMessage*)event.user.data2;
sprintf(string, "server(%d): %s", net_message->fd, net_message->data);
consoleLog(string);
free(net_message);
}
break;
case SDL_QUIT:
return 0;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym) {
case 304:
case 306:
case 308:
case 310:
break;
default:
globalContext(event.key.keysym.unicode);
break;
}
break;
case SDL_VIDEORESIZE:
video_width = event.resize.w;
video_height = event.resize.h;
interfaceVideoSet();
interfaceDraw(); // redraw
break;
}
interfaceDraw(); // redraw
}
return 1;
}
void interfaceDraw() {
// TODO: instead of redrawing whole screen, redraw last positions of tiles
//SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
cameraDraw(); // draw our current view
if (current_context == &consoleContext) {
interfaceDrawConsole();
}
SDL_Flip(screen); // redraw!
}
void interfaceDrawConsole() {
int line;
struct ConsoleEntry *entry;
entry = console_last_entry;
SDL_Rect print_area = {0, 0, screen->w, 9*TILE_HEIGHT};
SDL_FillRect(screen, &print_area, SDL_MapRGB(screen->format, 0, 128, 28));
for (line = 7;line >= 0;line--) {
if (entry->size > 0) {
interfaceDrawString(entry->string, 0, line*TILE_HEIGHT);
}
if (entry->prev != NULL)
entry = entry->prev;
}
interfaceDrawChar('>', 0, 8*TILE_HEIGHT);
interfaceDrawString(console_cmd, TILE_WIDTH*2, 8*TILE_HEIGHT);
}
void interfaceDrawChar(char ch, int start_x, int start_y) {
int y_offset = ch / 16; // 16 tiles across in spritesheet
int x_offset = ch - (y_offset*16);
SDL_Rect character_offset = { x_offset*TILE_WIDTH, y_offset*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT};
SDL_Rect render_position = {start_x, start_y, TILE_WIDTH, TILE_HEIGHT};
SDL_BlitSurface(font_spritesheet, &character_offset, screen, &render_position);
}
void interfaceDrawString(const char *string, int start_x, int start_y) {
int i = 0;
while (string[i] != '\0') {
interfaceDrawChar(string[i], (start_x)+i*TILE_WIDTH, start_y);
i++;
}
}
void interfacePrint(const char *string) {
int i = 0;
SDL_Rect print_area = {0, screen->h-TILE_HEIGHT, screen->w, TILE_HEIGHT};
SDL_FillRect(screen, &print_area, SDL_MapRGB(screen->format,128, 128, 0));
while (string[i] != '\0') {
int y_offset = string[i] / 16; // 16 tiles across in spritesheet
int x_offset = string[i] - (y_offset*16);
SDL_Rect character_offset = { x_offset*TILE_WIDTH, y_offset*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT};
SDL_Rect render_position = {i*TILE_WIDTH, 512, TILE_WIDTH, TILE_HEIGHT};
SDL_BlitSurface(font_spritesheet, &character_offset, screen, &render_position);
i++;
}
consoleLog(string);
}
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to retrieve */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
return *p;
break;
case 2:
return *(Uint16 *)p;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
break;
case 4:
return *(Uint32 *)p;
break;
default:
return 0; /* shouldn't happen, but avoids warnings */
}
}
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
SDL_Surface *interfaceScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height) {
if(!Surface || !Width || !Height)
return 0;
SDL_Surface *_ret = SDL_CreateRGBSurface(Surface->flags, Width, Height, Surface->format->BitsPerPixel,
Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);
double _stretch_factor_x = ((double)(Width) / (double)(Surface->w)),
_stretch_factor_y = ((double)(Height) / (double)(Surface->h));
Sint32 x, y, o_x, o_y;
for(y = 0; y < Surface->h; y++)
for(x = 0; x < Surface->w; x++)
for(o_y = 0; o_y < _stretch_factor_y; ++o_y)
for(o_x = 0; o_x < _stretch_factor_x; ++o_x)
putpixel(_ret, (Sint32)(_stretch_factor_x * x) + o_x, (Sint32)(_stretch_factor_y * y) + o_y, getpixel(Surface, x, y));
return _ret;
}
int interfaceVideoSet() {
if ((screen = SDL_SetVideoMode(video_width, video_height, 32, SDL_SWSURFACE|SDL_DOUBLEBUF|SDL_RESIZABLE|video_fullscreen)) == NULL) {
return ERROR;
}
free(camera_surface);
camera_surface = SDL_CreateRGBSurface(screen->flags, screen->w, screen->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
return SUCCESS;
}
void interfaceVideoSetSize(const char *input_string) {
if (input_string) {
int i = 0;
int j = 0;
char height[15];
char width[15];
char fullscreen[1];
int position = 0;
while(input_string[i] != '\0') {
if (input_string[i] == 'x') {
width[j] = '\0';
position = 1;
i++; // skip the x
j = 0;
} else if (input_string[i] == ' ') {
height[j] = '\0';
position = 2;
i++; // skip the space
j = 0;
}
if (position == 0) {
width[j] = input_string[i];
} else if (position == 1) {
height[j] = input_string[i];
} else if (position == 2) {
fullscreen[j] = input_string[i];
break; // just a boolean, so let's break
}
j++;
i++;
}
video_height = atoi(height); // ... had to reverse order for it to work on win...
video_width = atoi(width);
if (fullscreen[0] == '1') { // fullscreen
video_fullscreen = SDL_FULLSCREEN;
} else if (fullscreen[0] == '0') { // windowed
video_fullscreen = 0;
}
interfaceVideoSet();
} else {
consoleLog("Syntax: widthxheight 0|1");
}
}
void interfaceClose() {
SDL_Quit();
}