From 4002b213e55640f785f0ce030081da65037df827 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 15:43:36 +0200 Subject: [PATCH 01/54] src/gfx/fades.cpp: Made a few file-internal functions static. --- src/gfx/fades.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gfx/fades.cpp b/src/gfx/fades.cpp index 57ed1e4..088ec35 100644 --- a/src/gfx/fades.cpp +++ b/src/gfx/fades.cpp @@ -23,7 +23,7 @@ #include "gfx/gfx.h" #include "util/wutil.h" -void horisontal_split(void) { +static void horisontal_split(void) { Bitmap *upper; Bitmap *lower; int c1, c2 = 1; @@ -45,7 +45,7 @@ void horisontal_split(void) { } -void vertical_split() { +static void vertical_split() { Bitmap *left; Bitmap *right; int c1, c2; @@ -70,7 +70,7 @@ void vertical_split() { } -void pixel_fade(void) { +static void pixel_fade(void) { int c1, c2; for (c1 = 0; c1 < 20; c1++) { @@ -86,7 +86,7 @@ void pixel_fade(void) { } -void partial_fade(void) { +static void partial_fade(void) { unsigned char next_color[256]; int hit_value, temp_hit_value; int c, c2, c3, temp; From 30e4ad2dc7831dd91606456fb32a12de72249336 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 15:47:55 +0200 Subject: [PATCH 02/54] Move draw_with_vircr_mode check inside fillrect function --- src/gfx/gfx.cpp | 6 ++---- src/io/video.cpp | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gfx/gfx.cpp b/src/gfx/gfx.cpp index a392965..b2b59ae 100644 --- a/src/gfx/gfx.cpp +++ b/src/gfx/gfx.cpp @@ -44,8 +44,7 @@ void putpix(int x, int y, unsigned char c, int x1, int y1, int x2, int y2) { } } - if (!draw_with_vircr_mode) - fillrect(x, y, 1, 1, c); + fillrect(x, y, 1, 1, c); } void draw_line(int x1, int y1, int x2, int y2, unsigned char vari) { @@ -85,8 +84,7 @@ void fill_vircr(int x1, int y1, int x2, int y2, unsigned char vari) { memset(&vircr[x1 + lasky * 320], vari, x2 - x1 + 1); } - if (!draw_with_vircr_mode) - fillrect(x1, y1, x2 - x1 + 1, y2 - y1 + 1, vari); + fillrect(x1, y1, x2 - x1 + 1, y2 - y1 + 1, vari); } void tyhjaa_vircr(void) { diff --git a/src/io/video.cpp b/src/io/video.cpp index 896ef28..cfdad9e 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -82,6 +82,10 @@ static Uint32 getcolor(unsigned char c) { void fillrect(int x, int y, int w, int h, int c) { SDL_Rect r; + + if (draw_with_vircr_mode) + return; + r.x = x; r.y = y; r.w = w; From 5dca7634b2ac7613ae742062c1c87cec1f7bf16b Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 15:49:23 +0200 Subject: [PATCH 03/54] Add a missing include to gfx/font.h (it refers to a Bitmap) --- src/gfx/font.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gfx/font.h b/src/gfx/font.h index 19eddb9..29dd060 100644 --- a/src/gfx/font.h +++ b/src/gfx/font.h @@ -21,6 +21,7 @@ #ifndef FONT_H #define FONT_H +#include "gfx/bitmap.h" class Font { private: From 7d26c0de22c216c6760afea4b10a2a1a8e9c8abf Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 15:49:45 +0200 Subject: [PATCH 04/54] src/io/dksfile.cpp: Remove unused variable --- src/io/dksfile.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/io/dksfile.cpp b/src/io/dksfile.cpp index 93d9b14..df24234 100644 --- a/src/io/dksfile.cpp +++ b/src/io/dksfile.cpp @@ -156,7 +156,6 @@ int dksopen(const char *nimi) { int extdksopen(const char *nimi) { int lask; int kohta = -1; - int faili = 0; for (lask = 0; lask < MAX_ENTRIES; lask++) { if (!strcmp(dirri[lask].nimi, nimi)) { @@ -167,7 +166,6 @@ int extdksopen(const char *nimi) { if (kohta == -1) { dks_faili = fopen(nimi, "rb"); - faili = 1; if (dks_faili == NULL) return (0); } else { From 63062900d9710555f630272f432a930c4d55427a Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 05/54] Clean up command-line parameter handling slightly Functional change: the (undocumented) -level option previously had the syntax -levelLEVELNAME - now it is -level LEVELNAME to be more consistent with other options. --- src/menus/tripmenu.cpp | 2 +- src/triplane.cpp | 29 ++++------------------------- src/triplane.h | 6 ------ src/util/wutil.cpp | 31 +++++++++++++++++++++++++++++++ src/util/wutil.h | 4 ++++ 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 79a5f36..7b99981 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -595,7 +595,7 @@ int solo_player_menu(void) { int highest_mission = 0; if (findparameter("-solomenumission")) { - sscanf(parametrit[findparameter("-solomenumission") + 1], "%d", &mission_re_fly); + sscanf(findparameter_arg("-solomenumission"), "%d", &mission_re_fly); } if (mission_re_fly == 999) { diff --git a/src/triplane.cpp b/src/triplane.cpp index 030fbb7..bae1ecd 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -144,11 +144,6 @@ Font *fontti; Font *frost; Font *grid2; -//\ Parameter control - -char parametrit[40][40]; -int parametri_kpl; - //\ Shots control int pohja = 0; @@ -362,7 +357,6 @@ extern char mission_names[24][30]; //\\\\ Prototypes void hangarmenu_handle(void); -int findparameter(const char *jono); void controls(void); void detect_collision(void); void main_engine(void); @@ -903,17 +897,6 @@ void init_player(int l, int pommit) { } } -int findparameter(const char *jono) { - int laskuri; - - for (laskuri = 1; laskuri < parametri_kpl; laskuri++) - if (!strncmp(parametrit[laskuri], jono, strlen(jono))) - return (laskuri); - - return (0); -} - - void controls(void) { int l; @@ -3041,7 +3024,7 @@ void load_level(void) { loading_text("Loading levelinfo."); - if (!findparameter("-level")) { + if (findparameter_arg("-level") == NULL) { if (!playing_solo) { sprintf(levelname, "level%d", config.current_multilevel + 1); } else { @@ -3049,8 +3032,8 @@ void load_level(void) { } } else { - sprintf(levelname, parametrit[findparameter("-level")] + 6); - + strncpy(levelname, findparameter_arg("-level"), 80); + levelname[79] = '\0'; } @@ -3593,11 +3576,7 @@ int main(int argc, char *argv[]) { FILE *faili; Bitmap *lakuva1; - for (laskuri = 0; laskuri < argc; laskuri++) - strcpy(parametrit[laskuri], argv[laskuri]); - - parametri_kpl = argc; - + findparameter_init(argc, argv); if (findparameter("-?") || findparameter("-h") || findparameter("--help") || findparameter("-help")) { printf("Triplane Classic " TRIPLANE_VERSION " - a side-scrolling dogfighting game.\n"); diff --git a/src/triplane.h b/src/triplane.h index b55ec2a..a32f758 100644 --- a/src/triplane.h +++ b/src/triplane.h @@ -120,11 +120,6 @@ extern Font *fontti; extern Font *frost; extern Font *grid2; -//\ Parameter control - -extern char parametrit[40][40]; -extern int parametri_kpl; - //\ Shots control extern int pohja; @@ -321,7 +316,6 @@ extern int main_engine_random_seed; /***************************** Functions **************************************/ extern int small_warning(const char *message); -extern int findparameter(const char *jono); extern void kangas_terrain_to_screen(int leftx); extern void main_engine(void); extern void do_aftermath(int show_it_all); diff --git a/src/util/wutil.cpp b/src/util/wutil.cpp index 73dded9..5181e70 100644 --- a/src/util/wutil.cpp +++ b/src/util/wutil.cpp @@ -37,6 +37,9 @@ int32_t asinit[ASIN_NRO]; static int trigs_initialized = 0; +static int saved_argc = 0; +static char **saved_argv = NULL; + void setwrandom(int seed) { triplane_srandom(seed); } @@ -239,3 +242,31 @@ int squareroot(int number) { return new_result; } + +void findparameter_init(int argc, char **argv) { + saved_argc = argc; + saved_argv = argv; +} + +int findparameter(const char *jono) { + int laskuri; + + for (laskuri = 1; laskuri < saved_argc; laskuri++) + if (!strncmp(saved_argv[laskuri], jono, strlen(jono))) + return (laskuri); + + return (0); +} + +const char *findparameter_arg(const char *jono) { + int laskuri; + + for (laskuri = 1; laskuri < saved_argc; laskuri++) + if (!strncmp(saved_argv[laskuri], jono, strlen(jono))) + break; + + if (laskuri + 1 < saved_argc) + return saved_argv[laskuri + 1]; + else + return NULL; +} diff --git a/src/util/wutil.h b/src/util/wutil.h index 118919a..7d6570b 100644 --- a/src/util/wutil.h +++ b/src/util/wutil.h @@ -40,4 +40,8 @@ int squareroot(int number); extern int cosinit[361]; extern int sinit[361]; +void findparameter_init(int argc, char **argv); +int findparameter(const char *jono); +const char *findparameter_arg(const char *jono); + #endif From 6ca0a4e08d5034bea07677a4d6e791da0a4ca874 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 06/54] Improve all_bitmaps handling Every Bitmap object now stores a unique integer id, which is the index where it is located in the all_bitmaps array. This makes all_bitmaps_delete much more efficient. --- src/gfx/bitmap.cpp | 56 +++++++++++++++++++++++++--------------------- src/gfx/bitmap.h | 1 + 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index 8e7b2a5..f84677e 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -41,39 +41,43 @@ #define MAX_BITMAPS 8192 Bitmap *all_bitmaps[MAX_BITMAPS]; -int all_bitmaps_n = 0; +int all_bitmaps_last_alloc = -1; unsigned char *pointti; -static void all_bitmaps_add(Bitmap * b) { - if (draw_with_vircr_mode) - return; +static int all_bitmaps_add(Bitmap * b) { + int wrapped = 0; - assert(all_bitmaps_n < MAX_BITMAPS); - all_bitmaps[all_bitmaps_n++] = b; -} + if (all_bitmaps_last_alloc == -1) { + memset(all_bitmaps, 0, MAX_BITMAPS * sizeof(Bitmap *)); + } -static void all_bitmaps_delete(Bitmap * b) { - int i; + do { + all_bitmaps_last_alloc++; + if (all_bitmaps_last_alloc >= MAX_BITMAPS) { + all_bitmaps_last_alloc = 0; + wrapped++; + assert(wrapped <= 1); /* else all bitmap positions are used */ + } + } while (all_bitmaps[all_bitmaps_last_alloc] != NULL); - if (draw_with_vircr_mode) - return; + all_bitmaps[all_bitmaps_last_alloc] = b; + return all_bitmaps_last_alloc; +} - for (i = 0; i < all_bitmaps_n; i++) - if (all_bitmaps[i] == b) - break; - if (i < all_bitmaps_n) - all_bitmaps[i] = all_bitmaps[--all_bitmaps_n]; +static void all_bitmaps_delete(int id) { + all_bitmaps[id] = NULL; } void all_bitmaps_refresh(void) { int i; - if (draw_with_vircr_mode) - return; - - for (i = 0; i < all_bitmaps_n; i++) - all_bitmaps[i]->refresh_sdlsurface(); + for (i = 0; i < MAX_BITMAPS; i++) { + if (all_bitmaps[i] == NULL) + continue; + if (!draw_with_vircr_mode) + all_bitmaps[i]->refresh_sdlsurface(); + } } /* Make a copy of the image data in source, enlarged zoom times */ @@ -242,8 +246,8 @@ Bitmap::Bitmap(const char *image_name, int transparent) { name = image_name; hastransparency = transparent; sdlsurface = NULL; + id = all_bitmaps_add(this); refresh_sdlsurface(); - all_bitmaps_add(this); } @@ -255,13 +259,13 @@ Bitmap::Bitmap(int width, int height, unsigned char *image_data, const char *nam this->name = name; this->hastransparency = 1; this->sdlsurface = NULL; + this->id = all_bitmaps_add(this); refresh_sdlsurface(); - all_bitmaps_add(this); } Bitmap::~Bitmap() { - all_bitmaps_delete(this); + all_bitmaps_delete(id); if (sdlsurface != NULL) { SDL_FreeSurface(sdlsurface); sdlsurface = NULL; @@ -394,8 +398,8 @@ Bitmap::Bitmap(int x1, int y1, int xl, int yl, Bitmap * source_image) { name = source_image->name; hastransparency = source_image->hastransparency; sdlsurface = NULL; + id = all_bitmaps_add(this); refresh_sdlsurface(); - all_bitmaps_add(this); } /* Create a new Bitmap from the contents of vircr at (x,y) to (x+w,y+h) */ @@ -416,8 +420,8 @@ Bitmap::Bitmap(int x, int y, int w, int h) { name = "from_vircr"; hastransparency = 0; sdlsurface = NULL; + id = all_bitmaps_add(this); refresh_sdlsurface(); - all_bitmaps_add(this); } void Bitmap::blit_to_bitmap(Bitmap * to, int xx, int yy) { diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index b54d2d2..1a9bf6a 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -31,6 +31,7 @@ class Bitmap { int16_t width, height; int external_image_data; // boolean: is image_data owned by this instance int hastransparency; + int id; // a unique ID for this Bitmap SDL_Surface *sdlsurface; public: From 8abf878fb04f6848408fd348c69851030bf26f12 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 07/54] Optimize clip rectangle handling in Bitmap::blit() Don't try to draw a bitmap that is completely outside the clip rectangle. --- src/gfx/bitmap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index f84677e..c35195b 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -321,6 +321,9 @@ void Bitmap::blit(int xx, int yy, int rx, int ry, int rx2, int ry2) { if ((ry > ry2) || (rx > rx2)) return; + if (xx + width <= rx || xx > rx2 || yy + height <= ry || yy > ry2) + return; /* no part is inside the clip rectangle */ + if (update_vircr_mode) { fromminy = (yy >= ry) ? 0 : ry - yy; fromminx = (xx >= rx) ? 0 : rx - xx; From 2963fd45a007c535980bfb51df30d1e1996d6e9e Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 08/54] Minor cleanup in refresh handling all_bitmaps_refresh() should be called whenever the palette changes (not only after a video mode change), otherwise the bitmaps may have incorrect colors. Except we still don't do it when only the water colors are rotated, because that would be too slow - making water work in -sdldraw mode would require more extensive changes. This only affects -sdldraw mode (in the default drawing mode, all_bitmaps_refresh() does nothing). --- src/io/video.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/io/video.cpp b/src/io/video.cpp index cfdad9e..21b7f57 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -71,6 +71,9 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { } memcpy(&curpal[firstcolor], cc, n * sizeof(SDL_Color)); wfree(cc); + + if (n != 8) // FIXME hack to ignore rotate_water_palet + all_bitmaps_refresh(); } static Uint32 getcolor(unsigned char c) { @@ -232,7 +235,6 @@ static int init_mode(int new_mode, const char *paletname) { dksclose(); setpal_range(ruutu.paletti, 0, 256); - all_bitmaps_refresh(); current_mode = new_mode; return 1; From 3bc82d4f892ed582b028e9bb57610a2dd0114074 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 09/54] Minor cleanup in fillrect code Made fillrect update vircr by itself (previously its callers had to do it). --- src/gfx/gfx.cpp | 14 -------------- src/io/video.cpp | 11 +++++++++++ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/gfx/gfx.cpp b/src/gfx/gfx.cpp index b2b59ae..65239b9 100644 --- a/src/gfx/gfx.cpp +++ b/src/gfx/gfx.cpp @@ -36,13 +36,6 @@ void putpix(int x, int y, unsigned char c, int x1, int y1, int x2, int y2) { return; if (y > y2) return; - if (update_vircr_mode) { - if (current_mode == VGA_MODE) { - vircr[x + (y << 8) + (y << 6)] = c; - } else { - vircr[x + y * 800] = c; - } - } fillrect(x, y, 1, 1, c); } @@ -77,13 +70,6 @@ void boxi(int x1, int y1, int x2, int y2, unsigned char vari) { } void fill_vircr(int x1, int y1, int x2, int y2, unsigned char vari) { - int lasky; - - if (update_vircr_mode) { - for (lasky = y1; lasky <= y2; lasky++) - memset(&vircr[x1 + lasky * 320], vari, x2 - x1 + 1); - } - fillrect(x1, y1, x2 - x1 + 1, y2 - y1 + 1, vari); } diff --git a/src/io/video.cpp b/src/io/video.cpp index 21b7f57..c8e0789 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -86,6 +86,17 @@ static Uint32 getcolor(unsigned char c) { void fillrect(int x, int y, int w, int h, int c) { SDL_Rect r; + if (update_vircr_mode) { + int screenw = (current_mode == VGA_MODE) ? 320 : 800; + if (w == 1 && h == 1) { + vircr[x + y * screenw] = c; + } else { + int i; + for (i = 0; i < h; i++) + memset(&vircr[x + (y + i) * screenw], c, w); + } + } + if (draw_with_vircr_mode) return; From 44ac4dbac55cd864ba4f0c3ce25c1101b657a392 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 10/54] Preload all six mod files at start Previously the four "national" music files were loaded and unloaded when needed (and the two others at the start). Now all music is preloaded when Triplane starts, as is done with samples. --- src/menus/tripmenu.cpp | 29 ++--------------------------- src/world/tripaudio.cpp | 5 +++++ src/world/tripaudio.h | 1 + 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 7b99981..35294a9 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -48,8 +48,6 @@ int aces_score[MAX_PLAYERS_IN_ROSTER]; #define CHARS_PER_LINE 70 #define LINELENGHT 100 -sb_mod_file *national_mod = NULL; - char rank_names[6][10] = { "2nd Lt.", "1st Lt.", @@ -582,7 +580,6 @@ void load_descriptions(int number) { int solo_player_menu(void) { char facenames[4][7] = { "GERFAC", "FINFAC", "ENGFAC", "JAPFAC" }; char missionnames[4][7] = { "MISSI0", "MISSI1", "MISSI2", "MISSI3" }; - char modnames[4][7] = { "mgerma", "mfinla", "mengla", "mjapan" }; Bitmap *misboa = 0; Bitmap *misbak = 0; @@ -631,15 +628,7 @@ int solo_player_menu(void) { if ((mission_re_fly == -1) && is_there_sound && config.music_on && !findparameter("-nomusic")) { sdl_stop_music(); - national_mod = sdl_load_mod_file(modnames[solo_country]); - if (national_mod == NULL) { - printf("Error locating music.\n"); - exit(1); - - } - - sdl_play_music(national_mod); - + sdl_play_music(national_mod[solo_country]); } while (flag == 0) { @@ -703,16 +692,12 @@ int solo_player_menu(void) { case 3: if ((mission_re_fly == -1) && is_there_sound && config.music_on && !findparameter("-nomusic")) { sdl_stop_music(); - sdl_free_mod_file(national_mod); - } return 2; case 2: if (is_there_sound && config.music_on && !findparameter("-nomusic")) { sdl_stop_music(); - sdl_free_mod_file(national_mod); - } return 0; @@ -2877,7 +2862,6 @@ int kangas_menu(void) { if ((mission_re_fly == -1) && is_there_sound && config.music_on && !findparameter("-nomusic")) { sdl_stop_music(); - sdl_free_mod_file(national_mod); } @@ -2965,7 +2949,6 @@ void letter_menu(void) { int exit_flag = 0; int x, y, n1, n2; char country_names[4][10] = { "German", "Finnish", "English", "Japanese" }; - char modnames[4][7] = { "mgerma", "mfinla", "mengla", "mjapan" }; Bitmap *letter; Bitmap *temp; @@ -2976,14 +2959,7 @@ void letter_menu(void) { delete temp; if (is_there_sound && config.music_on && !findparameter("-nomusic")) { - national_mod = sdl_load_mod_file(modnames[solo_country]); - if (national_mod == NULL) { - printf("Error locating music.\n"); - exit(1); - - } - sdl_play_music(national_mod); - + sdl_play_music(national_mod[solo_country]); } @@ -3031,7 +3007,6 @@ void letter_menu(void) { if (is_there_sound && config.music_on && !findparameter("-nomusic")) { sdl_stop_music(); - sdl_free_mod_file(national_mod); } } diff --git a/src/world/tripaudio.cpp b/src/world/tripaudio.cpp index f71c545..de9d847 100644 --- a/src/world/tripaudio.cpp +++ b/src/world/tripaudio.cpp @@ -26,6 +26,7 @@ sb_mod_file *triplane_mod; sb_mod_file *aces_mod; +sb_mod_file *national_mod[4]; sb_sample *sample_itexp[3]; sb_sample *sample_bomb[4]; @@ -182,6 +183,10 @@ void load_music(void) { triplane_mod = sdl_load_mod_file("music1"); aces_mod = sdl_load_mod_file("maces"); + national_mod[0] = sdl_load_mod_file("mgerma"); + national_mod[1] = sdl_load_mod_file("mfinla"); + national_mod[2] = sdl_load_mod_file("mengla"); + national_mod[3] = sdl_load_mod_file("mjapan"); } void clear_sfx(void) { diff --git a/src/world/tripaudio.h b/src/world/tripaudio.h index a430834..061fc3b 100644 --- a/src/world/tripaudio.h +++ b/src/world/tripaudio.h @@ -25,6 +25,7 @@ extern sb_mod_file *triplane_mod; extern sb_mod_file *aces_mod; +extern sb_mod_file *national_mod[4]; extern sb_sample *sample_itexp[3]; extern sb_sample *sample_bomb[4]; From 33229a3cb90aff032781e83fb74393e1abc41d9b Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 11/54] Support for creating a Bitmap given its data A small extension to the Bitmap class constructor. --- src/gfx/bitmap.cpp | 20 ++++++++++++++++---- src/gfx/bitmap.h | 6 +++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index c35195b..bb792be 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -251,14 +251,26 @@ Bitmap::Bitmap(const char *image_name, int transparent) { } -Bitmap::Bitmap(int width, int height, unsigned char *image_data, const char *name) { - this->image_data = image_data; +Bitmap::Bitmap(int width, int height, + unsigned char *image_data, + const char *name, + int hastransparency, + int copy_image_data) { this->width = width; this->height = height; - this->external_image_data = 1; this->name = name; - this->hastransparency = 1; + this->hastransparency = hastransparency; this->sdlsurface = NULL; + + if (copy_image_data) { + this->image_data = (unsigned char *) walloc(width * height); + this->external_image_data = 0; + memcpy(this->image_data, image_data, width * height); + } else { + this->image_data = image_data; + this->external_image_data = 1; + } + this->id = all_bitmaps_add(this); refresh_sdlsurface(); } diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index 1a9bf6a..4673ce7 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -36,7 +36,11 @@ class Bitmap { public: Bitmap(const char *image_name, int transparent = 1); - Bitmap(int xl, int yl, unsigned char *image_data, const char *name = "unknown"); + Bitmap(int xl, int yl, + unsigned char *image_data, + const char *name = "unknown", + int hastransparency = 1, + int copy_image_data = 0); Bitmap(int x1, int y1, int xl, int yl, Bitmap * source_image); Bitmap(int x, int y, int w, int h); ~Bitmap(); From 3f5e183548b98a46f6f29ffa692d9840aca5887d Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 12/54] Minor fixes to Makefile --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f441bfc..e8829d6 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,6 @@ install: mkdir -p $(DESTDIR)$(PREFIX)/share/man/man6 $(INSTALL_DATA) doc/triplane.6 $(DESTDIR)$(PREFIX)/share/man/man6/triplane.6 test: - if [ ! -d triplane-testsuite ]; then echo Please darcs get http://iki.fi/lindi/darcs/triplane-testsuite; false; fi SDL_VIDEODRIVER=dummy bash tools/run-all-tests tools/run-one-test ./triplane triplane-testsuite build-data-from-source: tools/pcx2pgd @@ -98,5 +97,5 @@ dist: # man -Tps doc/triplane.6 > triplane.ps # ps2pdf triplane.ps -.PHONY: all checkdepend depend clean install test +.PHONY: all checkdepend depend clean install test build-data-from-source dist -include .depend From 1be23823f1c1da2e1e74b98f0d6f262527e9bd77 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 16:40:00 +0200 Subject: [PATCH 13/54] Remove unused function --- src/menus/tripmenu.cpp | 24 ------------------------ src/menus/tripmenu.h | 1 - 2 files changed, 25 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 35294a9..20e052a 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -59,30 +59,6 @@ char rank_names[6][10] = { /**************************** Functions ***************************************/ -void show_feat5(void) { - Bitmap *feat5; - feat5 = new Bitmap("FEAT5"); - int n1 = 0, n2 = 0; - int x, y; - - wait_mouse_relase(); - - feat5->info(&x, &y); - - feat5->blit((320 - x) >> 1, (200 - y) >> 1); - do_all(); - - while (!(n1 || n2)) { - koords(&x, &y, &n1, &n2); - - } - - - wait_mouse_relase(); - - -} - int get_rank(int player) { int l, l2, l3; diff --git a/src/menus/tripmenu.h b/src/menus/tripmenu.h index 355d015..2509158 100644 --- a/src/menus/tripmenu.h +++ b/src/menus/tripmenu.h @@ -54,6 +54,5 @@ void aces_multi_total(void); void aces_solo_total(void); void aces_one_solo(int country, int mission); int calculate_multitotal(int player); -void show_feat5(void); #endif From 760ec1343b67f9b1a58d59616675c8c0d265d89f Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 16:41:20 +0200 Subject: [PATCH 14/54] Unified key and mouse handling in menus Replace many variations of kbhit/getch/koords/wait_mouse_relase loops with unified menu_keys and menu_mouse functions. Also let keyboard keys emulate mouse buttons: Space = left button, Enter = right button. Minor functional changes: the Credits and "letter" menus can now be exited only with Enter, Space, Escape or mouse buttons (any key worked previously); and the small_warning dialog can be canceled with Escape (previously only the mouse worked). Also clean up kbhit(), getch() and select_key(), fixing some bugs: Make getch() return 0 when the event queue is empty (previously it could get stuck in a busy loop, e.g. when pressing Shift in certain menus). Make kbhit() clear the event queue when it returns false (previously some loops could keep unprocessed events in the SDL event loop indefinitely). Adjust all callers of getch() to check kbhit() first and call do_all() in their loop. Make select_key() use getch() instead of a custom loop. Moved "key" array and wait_relase() to sdl_compat.cpp where they fit better, and add a delay into the busy loop of wait_relase(). --- Makefile | 3 +- src/gfx/font.cpp | 8 +- src/io/sdl_compat.cpp | 66 +++++++++----- src/io/sdl_compat.h | 14 +-- src/menus/menusupport.cpp | 83 ++++++++++++++++++ src/menus/menusupport.h | 31 +++++++ src/menus/tripmenu.cpp | 178 +++++++++++--------------------------- src/menus/tripmenu.h | 1 - src/settings.cpp | 54 ++++-------- src/settings.h | 3 +- src/triplane.cpp | 91 +++++-------------- 11 files changed, 262 insertions(+), 270 deletions(-) create mode 100644 src/menus/menusupport.cpp create mode 100644 src/menus/menusupport.h diff --git a/Makefile b/Makefile index e8829d6..af162c0 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ COMMON_OBJS = src/gfx/bitmap.o src/gfx/font.o \ src/io/sdl_compat.o src/io/video.o \ src/io/mouse.o src/io/dksfile.o src/io/timing.o TRIPLANE_OBJS = src/triplane.o src/world/tripai.o \ - src/world/tripmis.o src/gfx/fades.o src/menus/tripmenu.o \ + src/world/tripmis.o src/gfx/fades.o \ + src/menus/menusupport.o src/menus/tripmenu.o \ src/world/terrain.o src/world/fobjects.o src/world/tmexept.o \ src/gfx/extra.o src/settings.o src/world/plane.o src/io/joystick.o src/io/sound.o \ src/world/tripaudio.o diff --git a/src/gfx/font.cpp b/src/gfx/font.cpp index 586a974..904a2ed 100644 --- a/src/gfx/font.cpp +++ b/src/gfx/font.cpp @@ -146,11 +146,11 @@ int Font::scanf(int x, int y, char *str, int max_len) { tausta_roska->blit(x, y); printf(printf(x, y, "%s", str), y, "_"); do_all(); + if (!kbhit()) { + continue; + } ch = getch(); - if (!ch) { - getch(); - ch = 0; - } else if (ch != 13) { + if (ch != 0 && ch != 13) { if (ch == 8) { if (kohta) { kohta--; diff --git a/src/io/sdl_compat.cpp b/src/io/sdl_compat.cpp index 20adc4b..e0e21ea 100644 --- a/src/io/sdl_compat.cpp +++ b/src/io/sdl_compat.cpp @@ -20,6 +20,7 @@ #include #include +#include "io/video.h" #ifdef HAVE_SDL_MIXER /* @@ -36,18 +37,24 @@ #include "util/wutil.h" #include "io/timing.h" +Uint8 *key; +int key_size; + int kbhit(void) { SDL_Event e; int ret; nopeuskontrolli(); - ret = SDL_PeepEvents(&e, 1, SDL_PEEKEVENT, ~0); + SDL_PumpEvents(); + ret = SDL_PeepEvents(&e, 1, SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYUP)); if (ret) { - if (e.type == SDL_KEYUP) { - return 1; - } else { - SDL_PollEvent(&e); + // leave SDL_KEYUP event in queue (getch() should be called next) + return 1; + } else { + // clear event queue (triplane code needs only SDL_KEYUP events) + while (SDL_PollEvent(&e)) { + ; } } return 0; @@ -56,32 +63,47 @@ int kbhit(void) { int getch(void) { SDL_Event e; - for (;;) { - if (SDL_PollEvent(&e)) { - if (e.type == SDL_KEYUP) { - int s, m; - s = e.key.keysym.sym; - m = e.key.keysym.mod; - if (s == SDLK_RSHIFT || s == SDLK_LSHIFT) { - continue; - } - if (m == KMOD_LSHIFT || m == KMOD_RSHIFT) { - if (s >= SDLK_a && s <= SDLK_z) { - s = toupper(s); - } + SDL_PumpEvents(); + while (SDL_PollEvent(&e)) { + if (e.type == SDL_KEYUP) { + int s, m; + s = e.key.keysym.sym; + m = e.key.keysym.mod; + if (s == SDLK_RSHIFT || s == SDLK_LSHIFT) { + continue; + } + if (m == KMOD_LSHIFT || m == KMOD_RSHIFT) { + if (s >= SDLK_a && s <= SDLK_z) { + s = toupper(s); } - return s; } + return s; } } + + return 0; // no key was found } void update_key_state(void) { - SDL_Event e; - SDL_PumpEvents(); - while (SDL_PollEvent(&e)) { + while (getch() != 0) ; + + key = SDL_GetKeyState(&key_size); +} + +void wait_relase(void) { + int c = 0; + + while (c != SDLK_LAST) { + nopeuskontrolli(); + do_all(); + update_key_state(); + + for (c = 0; c < SDLK_LAST; c++) + if (key[c] && c != SDLK_NUMLOCK && c != SDLK_CAPSLOCK && c != SDLK_SCROLLOCK) + break; } + } /** diff --git a/src/io/sdl_compat.h b/src/io/sdl_compat.h index 29dd68d..045becd 100644 --- a/src/io/sdl_compat.h +++ b/src/io/sdl_compat.h @@ -27,18 +27,11 @@ #define SAMPLE_VOLUME 20 -#define PAUSE_KEY SDLK_PAUSE - #define SOUNDCARD_NONE 0 #define SOUNDCARD_GUS 1 #define SOUNDCARD_SB 2 #define SOUNDCARD_SDL 3 -int kbhit(void); -int getch(void); - -extern unsigned char *key; - typedef struct { int right_volume, left_volume; #ifdef HAVE_SDL_MIXER @@ -52,7 +45,14 @@ typedef struct { #endif } sb_mod_file; +extern Uint8 *key; +extern int key_size; + +int kbhit(void); +int getch(void); void update_key_state(void); +void wait_relase(void); + int sdl_init_sounds(void); void sdl_uninit_sounds(void); void sdl_play_sample(sb_sample * sample, int looping = 0); diff --git a/src/menus/menusupport.cpp b/src/menus/menusupport.cpp new file mode 100644 index 0000000..7ac794b --- /dev/null +++ b/src/menus/menusupport.cpp @@ -0,0 +1,83 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +/* Support functions for menus */ + +#include "io/sdl_compat.h" +#include "io/mouse.h" +#include + +static int menu_n1 = 0, menu_n2 = 0; + +void menu_keys(int *exit_flag, int *help_flag) { + int ch; + + while (kbhit()) { + ch = getch(); + if (ch == SDLK_F1) { + if (help_flag != NULL) + *help_flag = !*help_flag; + } else if (ch == SDLK_ESCAPE) { + if (exit_flag != NULL) + *exit_flag = 1; + } else if (ch == SDLK_SPACE) { + menu_n1 = 1; + } else if (ch == SDLK_RETURN) { + menu_n2 = 1; + } + } +} + +void menu_mouse(int *x, int *y, int *n1, int *n2) { + koords(x, y, n1, n2); + if (menu_n1 == 1) { + *n1 = 1; + menu_n1 = 0; + } + if (menu_n2 == 1) { + *n2 = 1; + menu_n2 = 0; + } +} + +void wait_mouse_relase(int nokb) { + int n1 = 1, n2 = 1, x, y; + + while (n1 || n2) { + koords(&x, &y, &n1, &n2); + + if (!nokb) + while (kbhit()) + getch(); + } +} + +void wait_press_and_release(void) { + int x, y, n1, n2; + + n1 = 0; + while (!n1) { + if (kbhit()) + break; + koords(&x, &y, &n1, &n2); + } + + wait_mouse_relase(0); +} diff --git a/src/menus/menusupport.h b/src/menus/menusupport.h new file mode 100644 index 0000000..e665b8b --- /dev/null +++ b/src/menus/menusupport.h @@ -0,0 +1,31 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +#ifndef MENUSUPPORT_H +#define MENUSUPPORT_H + +/* Support functions for menus */ + +void menu_keys(int *exit_flag, int *help_flag); +void menu_mouse(int *x, int *y, int *n1, int *n2); +void wait_mouse_relase(int nokb = 0); +void wait_press_and_release(void); + +#endif diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 20e052a..2c7fe3d 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -22,7 +22,8 @@ #include "io/trip_io.h" #include "triplane.h" -#include "tripmenu.h" +#include "menus/tripmenu.h" +#include "menus/menusupport.h" #include #include #include @@ -561,7 +562,7 @@ int solo_player_menu(void) { Bitmap *misbak = 0; Bitmap *face = 0; Bitmap *mission = 0; - int flag = 0; + int flag = 0, exit_flag = 0; int l; int x, y, n1, n2; @@ -608,11 +609,12 @@ int solo_player_menu(void) { } while (flag == 0) { - if (kbhit() && getch() == SDLK_ESCAPE) { + menu_keys(&exit_flag, NULL); + if (exit_flag) { flag = 2; } - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); cursor->blit(x - 10, y - 10); do_all_clear(); @@ -694,7 +696,6 @@ void roster_menu(void) { static int number = -1; int keysetmode = 0; int help_on = 0; - char ch; Bitmap *help; Bitmap *rosteri; Bitmap *buttl, *buttr; @@ -738,20 +739,9 @@ void roster_menu(void) { hiiri_to(264, 109); while (!exit_flag) { - if (kbhit()) { - if (!(ch = getch())) { - ch = getch(); - if (ch == 59) - wtoggle(&help_on); + menu_keys(&exit_flag, &help_on); - } else { - if (ch == 27) - exit_flag = 1; - } - - } - - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); if (number != -1) { if (!keysetmode) { @@ -978,40 +968,40 @@ void roster_menu(void) { frost->printf(125, 100, "Key for upward turn [%s]", SDL_GetKeyName((SDLKey) roster[number].up)); do_all(); - roster[number].up = select_key(number, roster[number].up); + roster[number].up = select_key(roster[number].up); rosteri->blit(0, 0); frost->printf(125, 100, "Key for downward turn [%s]", SDL_GetKeyName((SDLKey) roster[number].down)); do_all(); - roster[number].down = select_key(number, roster[number].down); + roster[number].down = select_key(roster[number].down); rosteri->blit(0, 0); frost->printf(125, 100, "Key for roll [%s]", SDL_GetKeyName((SDLKey) roster[number].roll)); do_all(); - roster[number].roll = select_key(number, roster[number].roll); + roster[number].roll = select_key(roster[number].roll); rosteri->blit(0, 0); frost->printf(125, 100, "Key for engine power [%s]", SDL_GetKeyName((SDLKey) roster[number].power)); do_all(); - roster[number].power = select_key(number, roster[number].power); + roster[number].power = select_key(roster[number].power); rosteri->blit(0, 0); frost->printf(125, 100, "Key for bomb drop [%s]", SDL_GetKeyName((SDLKey) roster[number].bombs)); do_all(); - roster[number].bombs = select_key(number, roster[number].bombs); + roster[number].bombs = select_key(roster[number].bombs); rosteri->blit(0, 0); frost->printf(125, 100, "Key for guns [%s]", SDL_GetKeyName((SDLKey) roster[number].guns)); do_all(); - roster[number].guns = select_key(number, roster[number].guns); + roster[number].guns = select_key(roster[number].guns); break; case 5: @@ -1131,11 +1121,9 @@ void options_menu(void) { opt2 = new Bitmap("OPT2"); while (!exit_flag) { - if (kbhit()) - if (getch() == 27) - exit_flag = 1; + menu_keys(&exit_flag, NULL); - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); optionme->blit(0, 0); kohta[3 - optimode]->blit(248, 13); frost->printf(73, 43, "%s", selitykset[optimode]); @@ -1871,11 +1859,9 @@ void transfer_menu(void) { optionme = new Bitmap("TRANSF"); while (!exit_flag) { - if (kbhit()) - if (getch() == 27) - exit_flag = 1; + menu_keys(&exit_flag, NULL); - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); optionme->blit(0, 0); color_bites[config.current_multilevel]->blit(39 + config.current_multilevel * 80 - (config.current_multilevel / 3) * 240, @@ -1964,11 +1950,15 @@ static void joystick_setup(int joy, Bitmap * controlme) { controlme->blit(0, 0); frost->printf(54, 93, "Keep joystick idle and press"); frost->printf(54, 100, "Space (Esc=use old settings)"); - do_all(); do { + do_all(); + if (!kbhit()) { + c = 0; + continue; + } c = getch(); - } while (c != 27 && c != ' '); - if (c == 27) + } while (c != SDLK_ESCAPE && c != ' '); + if (c == SDLK_ESCAPE) goto joystick_setup_exit; save_axis_state(idle, joy); @@ -1977,11 +1967,15 @@ static void joystick_setup(int joy, Bitmap * controlme) { controlme->blit(0, 0); frost->printf(54, 93, "Do '%s' on joystick and", acts[i].prompt); frost->printf(54, 100, "press Space or D=disable this"); - do_all(); do { + do_all(); + if (!kbhit()) { + c = 0; + continue; + } c = getch(); - } while (c != 27 && c != ' ' && c != 'd' && c != 'D'); - if (c == 27) { + } while (c != SDLK_ESCAPE && c != ' ' && c != 'd' && c != 'D'); + if (c == SDLK_ESCAPE) { goto joystick_setup_exit; } else if (c == 'd' || c == 'D') { set_disabled_action(acts[i].act); @@ -2001,7 +1995,6 @@ static void joystick_setup(int joy, Bitmap * controlme) { } void controls_menu(void) { - char ch; int help_on = 0; int exit_flag = 0; int x, y, n1, n2; @@ -2021,20 +2014,9 @@ void controls_menu(void) { while (!exit_flag) { - if (kbhit()) { - if (!(ch = getch())) { - ch = getch(); - if (ch == 59) - wtoggle(&help_on); - - } else { - if (ch == 27) - exit_flag = 1; - } + menu_keys(&exit_flag, &help_on); - } - - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); @@ -2142,42 +2124,42 @@ void controls_menu(void) { frost->printf(56, 97, "Key for upward turn [%s]", SDL_GetKeyName((SDLKey) player_keys[active].up)); do_all(); - player_keys[active].up = select_key(active, player_keys[active].up); + player_keys[active].up = select_key(player_keys[active].up); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); frost->printf(56, 97, "Key for downward turn [%s]", SDL_GetKeyName((SDLKey) player_keys[active].down)); do_all(); - player_keys[active].down = select_key(active, player_keys[active].down); + player_keys[active].down = select_key(player_keys[active].down); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); frost->printf(56, 97, "Key for roll [%s]", SDL_GetKeyName((SDLKey) player_keys[active].roll)); do_all(); - player_keys[active].roll = select_key(active, player_keys[active].roll); + player_keys[active].roll = select_key(player_keys[active].roll); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); frost->printf(56, 97, "Key for engine power [%s]", SDL_GetKeyName((SDLKey) player_keys[active].power)); do_all(); - player_keys[active].power = select_key(active, player_keys[active].power); + player_keys[active].power = select_key(player_keys[active].power); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); frost->printf(56, 97, "Key for bomb drop [%s]", SDL_GetKeyName((SDLKey) player_keys[active].bombs)); do_all(); - player_keys[active].bombs = select_key(active, player_keys[active].bombs); + player_keys[active].bombs = select_key(player_keys[active].bombs); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); frost->printf(56, 97, "Key for guns [%s]", SDL_GetKeyName((SDLKey) player_keys[active].guns)); do_all(); - player_keys[active].guns = select_key(active, player_keys[active].guns); + player_keys[active].guns = select_key(player_keys[active].guns); save_keyset(); break; @@ -2237,7 +2219,6 @@ void assign_menu(void) { int lym[4] = { 0, 11, 24, 36 }; int response; int help_on = 0; - char ch; if (!roster[0].pilotname[0]) { response = small_warning("You have no pilots in the roster and\n" @@ -2258,20 +2239,9 @@ void assign_menu(void) { while (!exit_flag) { - if (kbhit()) { - if (!(ch = getch())) { - ch = getch(); - if (ch == 59) - wtoggle(&help_on); - - } else { - if (ch == 27) - exit_flag = 1; - } + menu_keys(&exit_flag, &help_on); - } - - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); acesme->blit(0, 0); @@ -2536,7 +2506,6 @@ void aces_menu(void) { int menuselect; int current_page = 0; int help_on = 0; - char ch; Bitmap *acesme; Bitmap *firstpage; Bitmap *buttl; @@ -2562,21 +2531,9 @@ void aces_menu(void) { do_all_clear(); while (!exit_flag) { + menu_keys(&exit_flag, &help_on); - if (kbhit()) { - if (!(ch = getch())) { - ch = getch(); - if (ch == 59) - wtoggle(&help_on); - - } else { - if (ch == 27) - exit_flag = 1; - } - - } - - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); if (!current_page) firstpage->blit(75, 34); @@ -2737,11 +2694,9 @@ int kangas_menu(void) { } while (!exit_flag) { - if (kbhit() && getch() == SDLK_ESCAPE) { - exit_flag = 1; - } + menu_keys(&exit_flag, NULL); - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); kangas->blit_fullscreen(); kangas_terrain_to_screen(place_x); @@ -2872,12 +2827,9 @@ void credits_menu(void) { init_vga("PALET7"); while (!exit_flag) { - if (kbhit()) { - getch(); - exit_flag = 1; - } + menu_keys(&exit_flag, NULL); + menu_mouse(&x, &y, &n1, &n2); - koords(&x, &y, &n1, &n2); credi1->blit(0, 0); @@ -2942,12 +2894,9 @@ void letter_menu(void) { letter = new Bitmap("LETTER"); while (!exit_flag) { - if (kbhit()) { - getch(); - exit_flag = 1; - } + menu_keys(&exit_flag, NULL); + menu_mouse(&x, &y, &n1, &n2); - koords(&x, &y, &n1, &n2); letter->blit(0, 0); medal1->blit(15, 80); frost->printf(145, 104, "From: %s central command headquaters.", country_names[solo_country]); @@ -2986,15 +2935,12 @@ void letter_menu(void) { } } - - void main_menu(void) { int exit_flag = 0; int x, y, n1, n2; int menuselect; int l, l2, l3; int help_on = 0; - int ch; Bitmap *help; help = new Bitmap("HELP1"); @@ -3009,17 +2955,9 @@ void main_menu(void) { hiiri_to(254, 120); while (!exit_flag) { - if (kbhit()) { - ch = getch(); - if (ch == SDLK_F1) { - wtoggle(&help_on); - } else { - if (ch == SDLK_ESCAPE) - exit_flag = 1; - } - } + menu_keys(&exit_flag, &help_on); - koords(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2); menu1->blit(0, 0); // 0,0,799,599 grid2->printf(34, 156, "Press F1\nfor Help"); @@ -3463,15 +3401,3 @@ void print_filled_roster(int number) { frost->printf(122, 177, "Total: %d", ts + roster[number].solo_mis_totals); } - -void wait_mouse_relase(int nokb) { - int n1 = 1, n2 = 1, x, y; - - while (n1 || n2) { - koords(&x, &y, &n1, &n2); - - if (!nokb) - while (kbhit()) - getch(); - } -} diff --git a/src/menus/tripmenu.h b/src/menus/tripmenu.h index 2509158..6c722ad 100644 --- a/src/menus/tripmenu.h +++ b/src/menus/tripmenu.h @@ -43,7 +43,6 @@ int solo_player_menu(void); void print_clear_roster(Bitmap * rosteri); void print_filled_roster(int number); -void wait_mouse_relase(int nokb = 0); void load_descriptions(int number); void show_descriptions(int number); void sort_and_show(int percent = 0); diff --git a/src/settings.cpp b/src/settings.cpp index 3a07121..d2ea570 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "io/sdl_compat.h" #include "io/sound.h" #include #include @@ -33,7 +34,6 @@ //\\ Keys -Uint8 *key = SDL_GetKeyState(NULL); struct keymap player_keys[4]; //\\ Rosterdata @@ -106,44 +106,22 @@ FILE *settings_open(const char *filename, const char *mode) { return fp; } -void wait_relase(void) { - int c = 0; - - while (c != SDLK_LAST) { - update_key_state(); - for (c = 0; c < SDLK_LAST; c++) - if (key[c] && c != SDLK_NUMLOCK && c != SDLK_CAPSLOCK && c != SDLK_SCROLLOCK) - break; - } - -} - -int select_key(int player, int old) { - int c; - int flag = 1; - - while (flag) { - if (key[SDLK_ESCAPE]) - flag = 0; - - update_key_state(); - - for (c = 0; c < SDLK_LAST; c++) - if (key[c] && c != SDLK_NUMLOCK && c != SDLK_CAPSLOCK && c != SDLK_SCROLLOCK) - break; - - if (c != SDLK_LAST) - if ((c != SDLK_ESCAPE) && (c != SDLK_PAUSE)) { - wait_relase(); - return c; - } - if (player == 100) - return 100; +int select_key(int old) { + int ch; + + for (;;) { + while (!kbhit()) + do_all(); + ch = getch(); + + if (ch == SDLK_ESCAPE) + return old; + else if (ch == SDLK_PAUSE || ch == SDLK_NUMLOCK || + ch == SDLK_CAPSLOCK || ch == SDLK_SCROLLOCK) + continue; + else + return ch; } - - wait_relase(); - return old; - } void swap_roster_endianes(void) { diff --git a/src/settings.h b/src/settings.h index 9ebb415..2e508d7 100644 --- a/src/settings.h +++ b/src/settings.h @@ -163,8 +163,7 @@ extern configuration config; void load_keyset(void); void save_keyset(void); -int select_key(int player, int old); -void wait_relase(void); +int select_key(int old); void load_roster(void); void save_roster(void); diff --git a/src/triplane.cpp b/src/triplane.cpp index bae1ecd..4cc7899 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -27,6 +27,7 @@ #include "triplane.h" #include "io/joystick.h" #include "gfx/gfx.h" +#include "menus/menusupport.h" #include "menus/tripmenu.h" #include "world/terrain.h" #include "world/fobjects.h" @@ -375,7 +376,6 @@ void itgun_sound(int itgun_x); void rotate_water_palet(void); void cause_damage(int amount, int plane); int small_warning(const char *message); -int big_warning(const char *message); void load_all_samples(void); void init_sologame(void); void write_files(void); @@ -604,14 +604,20 @@ void init_sologame(void) { int small_warning(const char *message) { Bitmap *warnkuva; - int flag = 1; + int flag = 1, exit_flag = 0; int x, y, n1, n2; int response = 0; warnkuva = new Bitmap("WARN2"); while (flag) { - koords(&x, &y, &n1, &n2); + menu_keys(&exit_flag, NULL); + menu_mouse(&x, &y, &n1, &n2); + + if (exit_flag) { + flag = 0; + response = 0; + } tyhjaa_vircr(); warnkuva->blit(87, 59); @@ -637,21 +643,14 @@ int small_warning(const char *message) { } } - while (n1 || n2) - koords(&x, &y, &n1, &n2); + + wait_mouse_relase(); delete warnkuva; return response; } -int big_warning(const char *message) { - if (message == NULL) - return 0; - else - return 0; -} - void cause_damage(int amount, int plane) { player_endurance[plane] -= amount; @@ -1896,9 +1895,8 @@ void do_aftermath(int show_it_all) { int struct_score = 0; int some_score; int aaa_score = 0; - int x, y, n1, n2; + int x, y, n1, n2, exit_flag = 0; int need_for_letter = 0; - char ch; Bitmap *fly, *exit; int x_coord; int best_in_record = 0; @@ -2260,27 +2258,10 @@ void do_aftermath(int show_it_all) { l = 1; while (l == 1) { - if (kbhit()) { - if ((ch = getch()) == 27) { - l = 0; - - } - - if (ch == 13) { - l = 2; - if (!need_for_letter) { - if (!mission_success) - mission_re_fly = solo_mission; - else - mission_re_fly = 999; - } - } - - } - - - koords(&x, &y, &n1, &n2); - + menu_keys(&exit_flag, NULL); + if (exit_flag) + l = 0; + menu_mouse(&x, &y, &n1, &n2); if (n1 || n2) { if (playing_solo) { @@ -3571,7 +3552,7 @@ void handle_parameters(void) { } int main(int argc, char *argv[]) { - int x, y, n1, n2; + int x, n1, n2; int laskuri; FILE *faili; Bitmap *lakuva1; @@ -3687,23 +3668,8 @@ int main(int argc, char *argv[]) { fflush(stdout); } - n1 = 0; - while (!n1 && !findparameter("-autostart")) { - if (kbhit()) - break; - - koords(&x, &y, &n1, &n2); - - if (n1 || n2) { - wait_mouse_relase(); - break; - } - - } - - while (kbhit() && !findparameter("-autostart")) - getch(); - + if (!findparameter("-autostart")) + wait_press_and_release(); if (sfx_loaded) { sample_alku->right_volume = 0; @@ -3720,22 +3686,10 @@ int main(int argc, char *argv[]) { delete lakuva1; - - while (!kbhit() && !findparameter("-autostart")) { - koords(&x, &y, &n1, &n2); - - if (n1 || n2) { - wait_mouse_relase(); - break; - } - - } + if (!findparameter("-autostart")) + wait_press_and_release(); #endif - while (kbhit() && !findparameter("-autostart")) - getch(); - - loading_text("Loading roster."); load_roster(); @@ -3743,8 +3697,7 @@ int main(int argc, char *argv[]) { if (!findparameter("-debugnographics")) init_vga("PALET5"); - while (kbhit()) - getch(); + wait_mouse_relase(); main_menu(); save_roster(); From a6f3b730d9d7e832d5bd2e36676fbece97a14b5e Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 15/54] Keyboard navigation for all menus The Tab key can now be used to move to any selectable items in the menus (and the mouse-button-emulation keys Space and Return can be used to select them). --- src/menus/menusupport.cpp | 43 ++++++++++++- src/menus/menusupport.h | 10 ++- src/menus/tripmenu.cpp | 125 ++++++++++++++++++++++++++++++++++---- src/triplane.cpp | 10 ++- 4 files changed, 172 insertions(+), 16 deletions(-) diff --git a/src/menus/menusupport.cpp b/src/menus/menusupport.cpp index 7ac794b..316a2ab 100644 --- a/src/menus/menusupport.cpp +++ b/src/menus/menusupport.cpp @@ -20,11 +20,12 @@ /* Support functions for menus */ +#include "menus/menusupport.h" #include "io/sdl_compat.h" #include "io/mouse.h" #include -static int menu_n1 = 0, menu_n2 = 0; +static int menu_mousetab = 0, menu_n1 = 0, menu_n2 = 0; void menu_keys(int *exit_flag, int *help_flag) { int ch; @@ -41,12 +42,50 @@ void menu_keys(int *exit_flag, int *help_flag) { menu_n1 = 1; } else if (ch == SDLK_RETURN) { menu_n2 = 1; + } else if (ch == SDLK_TAB) { + menu_mousetab++; } } } -void menu_mouse(int *x, int *y, int *n1, int *n2) { +/* + * positions is an array, ordered first by y then by x, ending with an + * entry with active=-1; or NULL if none + */ +void menu_mouse(int *x, int *y, int *n1, int *n2, + const menu_position *positions) { + const menu_position *p; + koords(x, y, n1, n2); + + if (positions == NULL) + menu_mousetab = 0; + if (menu_mousetab > 0) { + for (p = positions; p->active >= 0; p++) { + if (p->active == 0 || + p->y < *y || + (p->y == *y && p->x <= *x)) { + continue; + } + if (menu_mousetab == 1) + break; + menu_mousetab--; + } + + if (p->active < 0) { // no next found, so find first active + for (p = positions; p->active == 0; p++) + ; + } + + if (p->active == 1) { + *x = p->x; + *y = p->y; + hiiri_to(*x, *y); + } + + menu_mousetab = 0; + } + if (menu_n1 == 1) { *n1 = 1; menu_n1 = 0; diff --git a/src/menus/menusupport.h b/src/menus/menusupport.h index e665b8b..5356c50 100644 --- a/src/menus/menusupport.h +++ b/src/menus/menusupport.h @@ -23,8 +23,16 @@ /* Support functions for menus */ +#include + +struct menu_position { + int x, y; + int active; +}; + void menu_keys(int *exit_flag, int *help_flag); -void menu_mouse(int *x, int *y, int *n1, int *n2); +void menu_mouse(int *x, int *y, int *n1, int *n2, + const menu_position *positions = NULL); void wait_mouse_relase(int nokb = 0); void wait_press_and_release(void); diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 2c7fe3d..32e6743 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -568,6 +568,11 @@ int solo_player_menu(void) { int highest_mission = 0; + menu_position positions[] = { + { 110, 32, 1 }, { 110, 32+27*1, 1 }, { 110, 32+27*2, 1 }, + { 110, 32+27*3, 1 }, { 110, 32+27*4, 1 }, { 110, 32+27*5, 1 }, + { 300, 170, 1 }, { 0, 0, -1 } }; + if (findparameter("-solomenumission")) { sscanf(findparameter_arg("-solomenumission"), "%d", &mission_re_fly); } @@ -583,6 +588,10 @@ int solo_player_menu(void) { } + for (l = 0; l <= 5; l++) { + positions[l].active = (l <= highest_mission); + } + if (mission_re_fly != -1) { solo_mission = mission_re_fly; flag = 3; @@ -614,7 +623,7 @@ int solo_player_menu(void) { flag = 2; } - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); cursor->blit(x - 10, y - 10); do_all_clear(); @@ -707,6 +716,10 @@ void roster_menu(void) { Bitmap *ribbon[6]; int l, l2, l3, l4; int rank; + menu_position positions[] = { + { 282, 31, 0 /* !keysetmode */ }, { 264, 109, 0 /* !keysetmode */ }, + { 58, 147, 1 }, { 141, 176, 0 /* keysetmode */ }, + { 195, 178, 1 }, { 213, 178, 1 }, { 302, 194, 1 }, { 0, 0, -1 } }; if (number == -1 && roster[0].pilotname[0]) number = 0; @@ -741,7 +754,10 @@ void roster_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, &help_on); - menu_mouse(&x, &y, &n1, &n2); + positions[0].active = !keysetmode; + positions[1].active = !keysetmode; + positions[3].active = keysetmode; + menu_mouse(&x, &y, &n1, &n2, positions); if (number != -1) { if (!keysetmode) { @@ -1102,6 +1118,43 @@ void options_menu(void) { Bitmap *opt1, *opt2; int optimode = 0; int l; + menu_position positions01[] = { /* for optimode 0 and 1 */ + { 252, 29, 1 }, + { 217, 64, 1 }, { 227, 64, 1 }, { 252, 64, 1 }, + { 217, 74, 1 }, { 227, 74, 1 }, + { 217, 84, 1 }, { 227, 84, 1 }, + { 217, 94, 1 }, { 227, 94, 1 }, + { 217, 104, 1 }, { 227, 104, 1 }, { 252, 104, 1 }, + { 217, 114, 1 }, { 227, 114, 1 }, + { 217, 124, 0 /* optimode==1 */ }, { 227, 124, 0 /* optimode==1 */ }, + { 217, 134, 1 }, { 227, 134, 1 }, + { 252, 134, 1 }, { 284, 172, 1 }, { 0, 0, -1 } }; + menu_position positions2[] = { /* for optimode 2 */ + { 252, 29, 1 }, + { 217, 64, 1 }, { 227, 64, 1 }, { 252, 64, 1 }, + { 217, 74, 1 }, { 227, 74, 1 }, + { 252, 104, 1 }, { 252, 134, 1 }, { 284, 172, 1 }, + { 0, 0, -1 } }; + menu_position positions3[] = { /* for optimode 3 */ + { 37, 14, 0 /* config.all_planes_are!=0 */ }, + { 252, 29, 1 }, + { 37, 35, 0 /* config.all_planes_are!=0 */ }, + { 37, 56, 0 /* config.all_planes_are!=0 */ }, + { 217, 64, 1 }, { 227, 64, 1 }, { 252, 64, 1 }, + { 217, 74, 1 }, { 227, 74, 1 }, + { 37, 77, 0 /* config.all_planes_are!=0 */ }, + { 217, 84, 1 }, { 227, 84, 1 }, + { 222, 94, 1 }, + { 217, 104, 1 }, { 227, 104, 1 }, { 252, 104, 1 }, + { 37, 114, 0 /* config.alliance!=0 */ }, + { 217, 114, 1 }, { 227, 114, 1 }, + { 217, 124, 1 }, { 227, 124, 1 }, + { 217, 134, 1 }, { 227, 134, 1 }, { 252, 134, 1 }, + { 37, 144, 0 /* config.alliance!=0 */ }, + { 217, 144, 1 }, { 227, 144, 1 }, + { 217, 154, 1 }, { 227, 154, 1 }, + { 37, 172, 0 /* config.alliance!=0 */ }, + { 284, 172, 1 }, { 0, 0, -1 } }; char selitykset[4][80] = { "This form has questions about your vision.", @@ -1123,7 +1176,24 @@ void options_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, NULL); - menu_mouse(&x, &y, &n1, &n2); + if (optimode == 0) { + positions01[15].active = 0; + positions01[16].active = 0; + } else if (optimode == 1) { + positions01[15].active = 1; + positions01[16].active = 1; + } else if (optimode == 3) { + positions3[0].active = (config.all_planes_are != 0); + positions3[2].active = (config.all_planes_are != 0); + positions3[3].active = (config.all_planes_are != 0); + positions3[9].active = (config.all_planes_are != 0); + positions3[16].active = (config.alliance != 0); + positions3[24].active = (config.alliance != 0); + positions3[29].active = (config.alliance != 0); + } + menu_mouse(&x, &y, &n1, &n2, + (optimode == 2) ? positions2 : + (optimode == 3) ? positions3 : positions01); optionme->blit(0, 0); kohta[3 - optimode]->blit(248, 13); frost->printf(73, 43, "%s", selitykset[optimode]); @@ -1836,6 +1906,11 @@ void transfer_menu(void) { Bitmap *color_bites[6]; Bitmap *descs[6]; int l; + menu_position positions[] = { + { 90, 20, 1 }, + { 78, 70, 1 }, { 78+1*80, 70, 1 }, { 78+2*80, 70, 1 }, + { 78, 70+50, 1 }, { 78+1*80, 70+50, 1 }, { 78+2*80, 70+50, 1 }, + { 0, 0, -1 } }; optionme = new Bitmap("TRANS2"); @@ -1861,7 +1936,7 @@ void transfer_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, NULL); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); optionme->blit(0, 0); color_bites[config.current_multilevel]->blit(39 + config.current_multilevel * 80 - (config.current_multilevel / 3) * 240, @@ -2003,6 +2078,10 @@ void controls_menu(void) { Bitmap *napp[4]; Bitmap *help; int active = 0; + menu_position positions[] = { + { 80, 15, 1 }, + { 17, 28, 1 }, { 44, 28, 1 }, { 17, 51, 1 }, { 44, 51, 1 }, + { 160, 65, 1 }, { 266, 65, 1 }, { 153, 130, 1 }, { 0, 0, -1 } }; controlme = new Bitmap("NAPPIS"); napp[0] = new Bitmap("NAPPRE"); @@ -2016,7 +2095,7 @@ void controls_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, &help_on); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); @@ -2219,6 +2298,21 @@ void assign_menu(void) { int lym[4] = { 0, 11, 24, 36 }; int response; int help_on = 0; + menu_position positions[] = { + { 100, 15, 1 }, + { 32, 48+lym[0], 1 }, { 185, 48+lym[0], 1 }, + { 32, 48+lym[1], 1 }, { 185, 48+lym[1], 1 }, + { 32, 48+lym[2], 1 }, + { 185, 48+lym[2], 1 }, + { 32, 48+lym[3], 1 }, { 138, 48+lym[3], 1 }, { 147, 48+lym[3], 1 }, + { 185, 48+lym[3], 1 }, { 291, 48+lym[3], 1 }, { 300, 48+lym[3], 1 }, + { 32, 121+lym[0], 1 }, { 185, 121+lym[0], 1 }, + { 32, 121+lym[1], 1 }, { 185, 121+lym[1], 1 }, + { 32, 121+lym[2], 1 }, + { 185, 121+lym[2], 1 }, + { 32, 121+lym[3], 1 }, { 138, 121+lym[3], 1 }, { 147, 121+lym[3], 1 }, + { 185, 121+lym[3], 1 }, { 291, 121+lym[3], 1 }, { 300, 121+lym[3], 1 }, + { 0, 0, -1 } }; if (!roster[0].pilotname[0]) { response = small_warning("You have no pilots in the roster and\n" @@ -2237,11 +2331,10 @@ void assign_menu(void) { ruksi = new Bitmap("RUKSI"); help = new Bitmap("HELP5"); - while (!exit_flag) { menu_keys(&exit_flag, &help_on); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); acesme->blit(0, 0); @@ -2511,7 +2604,8 @@ void aces_menu(void) { Bitmap *buttl; Bitmap *buttr; Bitmap *help; - + menu_position positions[] = { { 239, 181, 1 }, { 257, 181, 1 }, + { 280, 181, 1 }, { 0, 0, -1 } }; if (is_there_sound && config.music_on) { sdl_stop_music(); @@ -2533,7 +2627,7 @@ void aces_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, &help_on); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); if (!current_page) firstpage->blit(75, 34); @@ -2657,6 +2751,10 @@ int kangas_menu(void) { int menuselect; int place_x = -2079; int showing_texts = 1; + menu_position positions[] = { + { 15, 18, 1}, + { 8, 195, 1}, { 26, 195, 1}, { 62, 195, 1}, { 80, 195, 1}, + { 0, 0, -1} }; Bitmap *kangas = new Bitmap("KANGAS", 0); Bitmap *buttr = new Bitmap("BUTTR"); @@ -2696,7 +2794,7 @@ int kangas_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, NULL); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); kangas->blit_fullscreen(); kangas_terrain_to_screen(place_x); @@ -2943,6 +3041,11 @@ void main_menu(void) { int help_on = 0; Bitmap *help; + menu_position positions[] = { + { 51, 40, 1 }, { 103, 40, 1 }, { 156, 40, 1 }, { 214, 40, 1 }, + { 268, 40, 1 }, { 51, 77, 1 }, { 268, 77, 1 }, { 51, 120, 1 }, + { 254, 120, 1 }, { 268, 169, 1 }, { 0, 0, -1 } }; + help = new Bitmap("HELP1"); if (is_there_sound && config.music_on) { @@ -2957,7 +3060,7 @@ void main_menu(void) { while (!exit_flag) { menu_keys(&exit_flag, &help_on); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); menu1->blit(0, 0); // 0,0,799,599 grid2->printf(34, 156, "Press F1\nfor Help"); diff --git a/src/triplane.cpp b/src/triplane.cpp index 4cc7899..61b656e 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -607,12 +607,14 @@ int small_warning(const char *message) { int flag = 1, exit_flag = 0; int x, y, n1, n2; int response = 0; + menu_position positions[] = { + { 111, 124, 1 }, { 206, 124, 1 }, { 0, 0, -1 } }; warnkuva = new Bitmap("WARN2"); while (flag) { menu_keys(&exit_flag, NULL); - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); if (exit_flag) { flag = 0; @@ -1902,6 +1904,10 @@ void do_aftermath(int show_it_all) { int best_in_record = 0; int sisennys; int mission_success = 0; + menu_position positions[] = { + { 62, 195, 0 /* playing_solo */ }, { 80, 195, 1 }, { 0, 0, -1 } }; + + positions[0].active = playing_solo; fly = new Bitmap("FLY"); exit = new Bitmap("EXIT"); @@ -2261,7 +2267,7 @@ void do_aftermath(int show_it_all) { menu_keys(&exit_flag, NULL); if (exit_flag) l = 0; - menu_mouse(&x, &y, &n1, &n2); + menu_mouse(&x, &y, &n1, &n2, positions); if (n1 || n2) { if (playing_solo) { From 34943ff4b6309c1b8bb63e013ebae3c2747597ba Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 16/54] Arrow-key navigation for menus All menus can now be navigated with the arrow keys (in addition to Tab and the mouse). --- src/menus/menusupport.cpp | 64 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/menus/menusupport.cpp b/src/menus/menusupport.cpp index 316a2ab..54babab 100644 --- a/src/menus/menusupport.cpp +++ b/src/menus/menusupport.cpp @@ -23,9 +23,11 @@ #include "menus/menusupport.h" #include "io/sdl_compat.h" #include "io/mouse.h" +#include #include static int menu_mousetab = 0, menu_n1 = 0, menu_n2 = 0; +static int menu_mousedx = 0, menu_mousedy = 0; void menu_keys(int *exit_flag, int *help_flag) { int ch; @@ -44,6 +46,14 @@ void menu_keys(int *exit_flag, int *help_flag) { menu_n2 = 1; } else if (ch == SDLK_TAB) { menu_mousetab++; + } else if (ch == SDLK_LEFT) { + menu_mousedx = -1; + } else if (ch == SDLK_RIGHT) { + menu_mousedx = 1; + } else if (ch == SDLK_UP) { + menu_mousedy = -1; + } else if (ch == SDLK_DOWN) { + menu_mousedy = 1; } } } @@ -54,12 +64,62 @@ void menu_keys(int *exit_flag, int *help_flag) { */ void menu_mouse(int *x, int *y, int *n1, int *n2, const menu_position *positions) { - const menu_position *p; + const menu_position *p, *bestp; + int dist, bestdist; koords(x, y, n1, n2); - if (positions == NULL) + if (positions == NULL) { menu_mousetab = 0; + menu_mousedx = menu_mousedy = 0; + } + + if (menu_mousedx != 0 || menu_mousedy != 0) { + /* + * We select the nearest active position that is at most 45 + * degrees off from the requested direction + */ + bestp = NULL; + bestdist = -1; + for (p = positions; p->active >= 0; p++) { + if (p->active == 0) + continue; + if (p->x == *x && p->y == *y) + continue; + + if (menu_mousedx == 0) { + if (abs(p->x - *x) > abs(p->y - *y)) + continue; + } else { + if (menu_mousedx * (p->x - *x) < 0) + continue; + } + + if (menu_mousedy == 0) { + if (abs(p->y - *y) > abs(p->x - *x)) + continue; + } else { + if (menu_mousedy * (p->y - *y) < 0) + continue; + } + + dist = ((p->y - *y) * (p->y - *y)) + + ((p->x - *x) * (p->x - *x)); + if (bestp == NULL || dist < bestdist) { + bestp = p; + bestdist = dist; + } + } + + if (bestp != NULL) { + *x = bestp->x; + *y = bestp->y; + hiiri_to(*x, *y); + } + + menu_mousedx = menu_mousedy = 0; + } + if (menu_mousetab > 0) { for (p = positions; p->active >= 0; p++) { if (p->active == 0 || From b9f6681dd048b4a7928a3c059cd59c7492792fed Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 17/54] Minor display cleanup in do_aftermath do_aftermath uselessly called do_all a few times. --- src/triplane.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/triplane.cpp b/src/triplane.cpp index 61b656e..0a32b41 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -1942,8 +1942,6 @@ void do_aftermath(int show_it_all) { fontti->printf(254, 80 + l * 21, "%4d%%", (player_hits[l] * 1000) / (firedi)); } - - do_all(); } if (playing_solo) { @@ -2157,9 +2155,6 @@ void do_aftermath(int show_it_all) { } - do_all(); - - if (tempt < 0) tempt = 0; From 4c11ba60816316ab8cd9d1be1379b8c9fc66e765 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 18/54] Code cleanup: move code out of main_menu() Clean up main_menu() by moving code that sets some game type -related variables into a new function. --- src/menus/tripmenu.cpp | 47 +----------------------------------------- src/triplane.cpp | 44 +++++++++++++++++++++++++++++++++++++++ src/triplane.h | 2 ++ 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 32e6743..d7637d3 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3250,10 +3250,6 @@ void main_menu(void) { init_vga("PALET5"); - for (l = 0; l < 16; l++) { - player_exists[l] = 0; - plane_present[l] = 0; - } for (l = 0; l < 4; l++) { if (config.player_type[l] == 1 || config.player_type[l] == 3) { if (config.player_number[l] != -1) { @@ -3268,48 +3264,7 @@ void main_menu(void) { break; } - playing_solo = 0; - solo_mode = -1; - - for (l = 0; l < 4; l++) { - switch (config.player_type[l]) { - case 0: - player_exists[l] = 0; - break; - - case 1: - player_exists[l] = 1; - computer_active[l] = 0; - playing_solo = 1; - solo_country = l; - solo_mode = l; - break; - - case 2: - if ((l == 1 || l == 2) && config.current_multilevel == 5) { - player_exists[l] = 0; - computer_active[l] = 0; - config.player_type[l] = 0; - - } else { - player_exists[l] = 1; - computer_active[l] = 1; - } - break; - - case 3: - player_exists[l] = 1; - computer_active[l] = 0; - break; - - - } - - } - - - - + set_player_types(); if (is_there_sound && (!findparameter("-nomusic"))) { diff --git a/src/triplane.cpp b/src/triplane.cpp index 0a32b41..1d286aa 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -898,6 +898,50 @@ void init_player(int l, int pommit) { } } +/* set playing_solo, solo_country and other variables from current config */ +void set_player_types(void) { + int l; + + playing_solo = 0; + solo_mode = -1; + + for (l = 0; l < 16; l++) { + player_exists[l] = 0; + plane_present[l] = 0; + } + + for (l = 0; l < 4; l++) { + switch (config.player_type[l]) { + case 0: + player_exists[l] = 0; + break; + + case 1: + player_exists[l] = 1; + computer_active[l] = 0; + playing_solo = 1; + solo_country = l; + solo_mode = l; + break; + + case 2: + if ((l == 1 || l == 2) && config.current_multilevel == 5) { + player_exists[l] = 0; + computer_active[l] = 0; + config.player_type[l] = 0; + } else { + player_exists[l] = 1; + computer_active[l] = 1; + } + break; + + case 3: + player_exists[l] = 1; + computer_active[l] = 0; + break; + } + } +} void controls(void) { int l; diff --git a/src/triplane.h b/src/triplane.h index a32f758..1fd0a20 100644 --- a/src/triplane.h +++ b/src/triplane.h @@ -321,6 +321,8 @@ extern void main_engine(void); extern void do_aftermath(int show_it_all); extern void clear_level(void); extern void init_player(int l, int pommit = 1); +void set_player_types(void); + extern void cause_damage(int amount, int plane); extern void do_flags(void); From 400d7428d788594a0bb21e5e310289946618af5a Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 19/54] Clean up controls handling Read player controls in a separate function instead of inside the main game loop code. Add set_keys_* functions which are used to set where the current controls come from. --- src/triplane.cpp | 217 ++++++++++++++++++++++++-------------------- src/triplane.h | 9 +- src/world/plane.cpp | 2 +- src/world/plane.h | 2 +- 4 files changed, 128 insertions(+), 102 deletions(-) diff --git a/src/triplane.cpp b/src/triplane.cpp index 1d286aa..7613f98 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -173,6 +173,8 @@ int hangarmenu_max_gas[16]; int hangarmenu_max_ammo[16]; int hangarmenu_max_bombs[16]; +keymap current_keys[4]; +int32_t current_joystick[2]; /* Timing */ short int viimeiset_framet = 0; @@ -198,8 +200,6 @@ int player_on_airfield[16]; int collision_detect = 1; int part_collision_detect = 1; -int power_reverse = 0; -int power_on_off = 0; int loading_texts = 0; int solo_mode = -1; int aftermath; @@ -943,6 +943,113 @@ void set_player_types(void) { } } +void set_keys_none(void) { + memset(current_keys, 0, 4*sizeof(keymap)); // SDLK_UNKNOWN is 0 + current_joystick[0] = -1; + current_joystick[1] = -1; +} + +void set_keys_from_multiplayer(int country) { + memcpy(¤t_keys[country], &player_keys[country], sizeof(keymap)); + current_joystick[0] = config.joystick[0]; + current_joystick[1] = config.joystick[1]; +} + +void set_keys_from_roster(int country, int player_num) { + current_keys[country].up = roster[player_num].up; + current_keys[country].down = roster[player_num].down; + current_keys[country].roll = roster[player_num].roll; + current_keys[country].power = roster[player_num].power; + current_keys[country].guns = roster[player_num].guns; + current_keys[country].bombs = roster[player_num].bombs; + // don't set current_joystick +} + +// find new controls for player (0-3), writing them to the arguments +// *power needs to have the previous value for power (for on/off power) +void get_controls_for_player(int player, + int *down, int *up, int *power, + int *roll, int *guns, int *bombs) { + // for on/off power: when the power key is held down, have we + // already changed the setting + static int onoff_power_changed[4] = {0, 0, 0, 0}; + int joynum; + int inmenu = (hangarmenu_active[player] || in_closing[player]); + + for (joynum = 0; joynum <= 1; joynum++) { + if (current_joystick[joynum] == player) { + get_joystick_action(joynum, inmenu, + down, up, power, roll, guns, bombs); + if (!joystick_has_roll_button(joynum) && !inmenu) { + // Autoroll code + *roll = 0; + if (*down == *up) /* not turning up/down */ + if ((player_upsidedown[player] && (player_angle[player] < 23040 || player_angle[player] > 69120)) || + (!player_upsidedown[player] && (player_angle[player] < 69120 && player_angle[player] > 23040))) + if (!player_rolling[player]) + *roll = 1; + } + break; + } + } + + if (joynum == 2 && key) { // no joystick control for this player + if (key[current_keys[player].down]) + *down = 1; + else + *down = 0; + + if (key[current_keys[player].up]) + *up = 1; + else + *up = 0; + + if (!config.poweronoff) { + if (key[current_keys[player].power]) + *power = (config.powerrev ? 0 : 1); + else + *power = (config.powerrev ? 1 : 0); + } else { // on/off power code + if (key[current_keys[player].power]) { + if (!onoff_power_changed[player]) { + *power = !(*power); + onoff_power_changed[player] = 1; + } + } else { + onoff_power_changed[player] = 0; + // and don't change *power + } + + if (in_closing[player]) + *power = 0; + } + + if (key[current_keys[player].bombs]) + *bombs = 1; + else + *bombs = 0; + + if (key[current_keys[player].roll]) + *roll = 1; + else + *roll = 0; + + if (key[current_keys[player].guns]) + *guns = 1; + else + *guns = 0; + } else if (joynum == 2) { // no joystick, keyboard inactive + // act as if no keys are pressed + *down = 0; + *up = 0; + if (!config.poweronoff) + *power = (config.powerrev ? 1 : 0); + *bombs = 0; + *roll = 0; + *guns = 0; + } +} + void controls(void) { int l; @@ -1124,99 +1231,10 @@ void controls(void) { if (l > 3) continue; - - if (!playing_solo && config.joystick[0] == l) { - get_joystick_action(0, (hangarmenu_active[l] || in_closing[l]), - &new_mc_down[l], &new_mc_up[l], &new_mc_power[l], &new_mc_roll[l], &new_mc_guns[l], &new_mc_bomb[l]); - if (!joystick_has_roll_button(0) && !(hangarmenu_active[l] || in_closing[l])) { - // Autoroll code - new_mc_roll[l] = 0; - if (new_mc_down[l] == new_mc_up[l]) /* not turning up/down */ - if ((player_upsidedown[l] && (player_angle[l] < 23040 || player_angle[l] > 69120)) || - (!player_upsidedown[l] && (player_angle[l] < 69120 && player_angle[l] > 23040))) - if (!player_rolling[l]) - new_mc_roll[l] = 1; - } - } else { - if (!playing_solo && config.joystick[1] == l) { - get_joystick_action(1, (hangarmenu_active[l] || in_closing[l]), - &new_mc_down[l], &new_mc_up[l], &new_mc_power[l], &new_mc_roll[l], &new_mc_guns[l], &new_mc_bomb[l]); - if (!joystick_has_roll_button(1) && !(hangarmenu_active[l] || in_closing[l])) { - // Autoroll code - new_mc_roll[l] = 0; - if (new_mc_down[l] == new_mc_up[l]) /* not turning up/down */ - if ((player_upsidedown[l] && (player_angle[l] < 23040 || player_angle[l] > 69120)) || - (!player_upsidedown[l] && (player_angle[l] < 69120 && player_angle[l] > 23040))) - if (!player_rolling[l]) - new_mc_roll[l] = 1; - } - } else { - if ((playing_solo ? key[roster[config.player_number[solo_country]].down] : key[player_keys[l].down])) - new_mc_down[l] = 1; - else - new_mc_down[l] = 0; - - if ((playing_solo ? key[roster[config.player_number[solo_country]].up] : key[player_keys[l].up])) - new_mc_up[l] = 1; - else - new_mc_up[l] = 0; - - if (!power_on_off) { - if (!power_reverse) { - if ((playing_solo ? key[roster[config.player_number[solo_country]].power] : key[player_keys[l].power])) - new_mc_power[l] = 1; - else - new_mc_power[l] = 0; - } else { - if ((playing_solo ? key[roster[config.player_number[solo_country]].power] : key[player_keys[l].power])) - new_mc_power[l] = 0; - else - new_mc_power[l] = 1; - } - } else { - if ((playing_solo ? key[roster[config.player_number[solo_country]].power] : key[player_keys[l].power])) { - if (!controls_power2[l]) { - if (new_mc_power[l]) - new_mc_power[l] = 0; - else - new_mc_power[l] = 1; - } - controls_power2[l] = 1; - - } else - controls_power2[l] = 0; - - if (in_closing[l]) - new_mc_power[l] = 0; - - } - - new_mc_bomb[l] = 0; - - if ((playing_solo ? key[roster[config.player_number[solo_country]].bombs] : key[player_keys[l].bombs])) { - new_mc_bomb[l] = 1; - - } - - new_mc_roll[l] = 0; - if ((playing_solo ? key[roster[config.player_number[solo_country]].roll] : key[player_keys[l].roll])) { - - new_mc_roll[l] = 1; - - - } - - - new_mc_guns[l] = 0; - - if ((playing_solo ? key[roster[config.player_number[solo_country]].guns] : key[player_keys[l].guns])) { - new_mc_guns[l] = 1; - - } - } - } - - + get_controls_for_player(l, + &new_mc_down[l], &new_mc_up[l], + &new_mc_power[l], &new_mc_roll[l], + &new_mc_guns[l], &new_mc_bomb[l]); if (player_spinning[l]) { if (!player_rolling[l]) @@ -1485,9 +1503,6 @@ void main_engine(void) { part_collision_detect = config.partcollision; } - power_on_off = config.poweronoff; - power_reverse = config.powerrev; - for (l = 0; l < 16; l++) { player_sides[l] = player_tsides[l]; @@ -1656,9 +1671,15 @@ void main_engine(void) { if (playing_solo) { init_exeptions(solo_country, solo_mission); tyhjaa_vircr(); + set_keys_none(); + set_keys_from_roster(solo_country, config.player_number[solo_country]); } //// Open joysticks if (!playing_solo) { + set_keys_from_multiplayer(0); + set_keys_from_multiplayer(1); + set_keys_from_multiplayer(2); + set_keys_from_multiplayer(3); open_close_joysticks(config.joystick[0] != -1, config.joystick[1] != -1); } //// Record diff --git a/src/triplane.h b/src/triplane.h index 1fd0a20..02a9670 100644 --- a/src/triplane.h +++ b/src/triplane.h @@ -167,8 +167,6 @@ extern int player_on_airfield[16]; extern int collision_detect; extern int part_collision_detect; extern int end_game_after; -extern int power_reverse; -extern int power_on_off; extern int loading_texts; extern int solo_mode; extern int aftermath; @@ -323,6 +321,13 @@ extern void clear_level(void); extern void init_player(int l, int pommit = 1); void set_player_types(void); +void set_keys_none(void); +void set_keys_from_multiplayer(int country); +void set_keys_from_roster(int country, int player_num); +void get_controls_for_player(int player, + int *down, int *up, int *power, + int *roll, int *guns, int *bombs); + extern void cause_damage(int amount, int plane); extern void do_flags(void); diff --git a/src/world/plane.cpp b/src/world/plane.cpp index 3fd7a17..1d60d19 100644 --- a/src/world/plane.cpp +++ b/src/world/plane.cpp @@ -27,7 +27,7 @@ /* Variables */ -unsigned char controls_up[16], controls_down[16], controls_power[16], controls_power2[16]; +unsigned char controls_up[16], controls_down[16], controls_power[16]; /* Note: 1== enable, 0== disable */ int player_x[16], player_y[16]; int player_speed[16]; diff --git a/src/world/plane.h b/src/world/plane.h index b7ae503..00bb7ff 100644 --- a/src/world/plane.h +++ b/src/world/plane.h @@ -22,7 +22,7 @@ #define PLANE_H /* Note: 1== enabled, 0== disabled */ -extern unsigned char controls_up[16], controls_down[16], controls_power[16], controls_power2[16]; +extern unsigned char controls_up[16], controls_down[16], controls_power[16]; extern int player_x[16], player_y[16]; extern int player_speed[16]; From 3de563b59050d97d8a1b227a84d00ca31f42eb70 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:05:00 +0300 Subject: [PATCH 20/54] Support multiplayer players without roster entries It is now possible to assign an anonymous pilot for multiplayer (not solo) games. It works like a normal human pilot except that nothing is saved in the roster for this player. Also fixes a bug where the up/down arrows in the Assign Players menu allowed switching to an empty player name (which was removed when exiting the menu). --- src/menus/tripmenu.cpp | 168 +++++++++++++++-------------------------- src/triplane.cpp | 2 +- 2 files changed, 60 insertions(+), 110 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index d7637d3..17542bb 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2352,9 +2352,10 @@ void assign_menu(void) { ruksi->blit(28 + lx, 45 + ly + lym[config.player_type[l]]); - if (config.player_number[l] != -1) { + if (config.player_number[l] == -2) { + frost->printf(46 + lx, 82 + ly, "(don't save in roster)"); + } else if (config.player_number[l] != -1) { frost->printf(46 + lx, 82 + ly, roster[config.player_number[l]].pilotname); - } for (l2 = 0; l2 < 4; l2++) { @@ -2394,8 +2395,11 @@ void assign_menu(void) { if (menusubselect2 == 1) { for (l = 0; l < 4; l++) { - if (l == menusubselect1) + if (l == menusubselect1) { + if (config.player_number[l] == -2) + config.player_number[l] = -1; // updated below continue; + } config.player_type[l] = 0; config.player_number[l] = -1; @@ -2416,11 +2420,14 @@ void assign_menu(void) { config.player_number[menusubselect1] = -1; else { if (config.player_number[menusubselect1] == -1) { - - for (l = 0; l < MAX_PLAYERS_IN_ROSTER; l++) { - if (!roster[l].pilotname[0]) { - config.player_type[menusubselect1] = 0; - config.player_number[menusubselect1] = -1; + for (l = 0; l <= MAX_PLAYERS_IN_ROSTER; l++) { + if (l == MAX_PLAYERS_IN_ROSTER || !roster[l].pilotname[0]) { + if (menusubselect2 == 3) { + config.player_number[menusubselect1] = -2; + } else { + config.player_type[menusubselect1] = 0; + config.player_number[menusubselect1] = -1; + } break; } else { for (l3 = 0; l3 < 4; l3++) { @@ -2440,12 +2447,6 @@ void assign_menu(void) { } } - if (l == MAX_PLAYERS_IN_ROSTER) { - config.player_type[menusubselect1] = 0; - config.player_number[menusubselect1] = -1; - - } - } } @@ -2460,115 +2461,57 @@ void assign_menu(void) { break; case 3: - ly = 0; lx = config.player_number[menusubselect1]; - l2 = lx + 1; - - while (!ly) { - ly = 1; - - if (lx == l2) { - l2 = -1; - break; + l2 = lx; + do { + l2 = (l2 == -2) ? 0 : l2 + 1; + if (l2 >= MAX_PLAYERS_IN_ROSTER || (l2 >= 0 && !roster[l2].pilotname[0])) { + if (config.player_type[menusubselect1] == 3) + l2 = -2; + else + l2 = 0; } - - if (l2 != -1) { - if (!roster[l2].pilotname[0]) { - ly = 0; - l2 = -1; - continue; - } - - - for (l = 0; l < 4; l++) { - if (l == menusubselect1) - continue; - - if (config.player_number[l] == l2) { - l2++; - ly = 0; - continue; - } - - - } - - } else { - - l2 = -1; + if (l2 == lx) + l2 = (config.player_type[menusubselect1] == 3) ? -2 : -1; + if (l2 < 0) break; + for (l = 0; l < 4; l++) { + if (l == menusubselect1) + continue; + if (config.player_number[l] == l2) + break; } - } - + } while (l != 4); config.player_number[menusubselect1] = l2; - break; case 4: - ly = 0; lx = config.player_number[menusubselect1]; - l2 = lx - 1; - if (l2 < -1) - l2 = -1; - - - - while (!ly) { - ly = 1; - - if (l2 != -1) { - - for (l = 0; l < 4; l++) { - if (l == menusubselect1) - continue; - - if (config.player_number[l] == l2) { - l2--; - ly = 0; + l2 = lx; + do { + if (l2 == 0 && config.player_type[menusubselect1] == 3) { + l2 = -2; + } else if (l2 <= 0) { + for (l2 = MAX_PLAYERS_IN_ROSTER - 1; l2 >= 0; l2--) + if (roster[l2].pilotname[0]) break; - } - - - } - } else { - - l2 = -1; - break; + l2--; } - } - - if (l2 == -1) { - for (l2 = MAX_PLAYERS_IN_ROSTER - 1; l2 >= 0; l2--) { - if (roster[l2].pilotname[0]) { - for (l = 0; l < 4; l++) { - if (l == menusubselect1) - continue; - - if (config.player_number[l] == l2) { - break; - - } - } - - if (l == 4) - break; - - } + if (l2 == lx) + l2 = (config.player_type[menusubselect1] == 3) ? -2 : -1; + if (l2 < 0) + break; + for (l = 0; l < 4; l++) { + if (l == menusubselect1) + continue; + if (config.player_number[l] == l2) + break; } - - - config.player_number[menusubselect1] = l2; - break; - } - + } while (l != 4); config.player_number[menusubselect1] = l2; - - - break; - } } @@ -2582,6 +2525,10 @@ void assign_menu(void) { config.player_type[l] = 0; config.player_number[l] = -1; } + if (config.player_number[l] == -2 && config.player_type[l] != 3) { + config.player_type[l] = 0; + config.player_number[l] = -1; + } } delete acesme; @@ -3107,8 +3054,11 @@ void main_menu(void) { for (l = 0; l < 4; l++) { if (config.player_type[l] == 3) { - frost->printf(110, 130 + l * 11, "%d. %s", l + 1, roster[config.player_number[l]].pilotname); - + frost->printf(110, 130 + l * 11, "%d. %s", + l + 1, + (config.player_number[l] == -2) + ? "Anonymous pilot" + : roster[config.player_number[l]].pilotname); } if (config.player_type[l] == 2) { diff --git a/src/triplane.cpp b/src/triplane.cpp index 7613f98..2db0112 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -2275,7 +2275,7 @@ void do_aftermath(int show_it_all) { } - if (config.player_type[l] == 3 && config.player_number[l] != -1) { + if (config.player_type[l] == 3 && config.player_number[l] >= 0) { roster[config.player_number[l]].multi_mis_flown++; tempt = 0; From 58dd5c07f4eb67e5ce818e9313121713e0e08937 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 6 Apr 2014 12:36:14 +0300 Subject: [PATCH 21/54] Improve warning dialog UI Just releasing a mouse button press which resulted in a warning dialog could exit the warning dialog if the mouse cursor happened to be on top of the warning dialog buttons. --- src/triplane.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/triplane.cpp b/src/triplane.cpp index 2db0112..020a654 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -612,6 +612,8 @@ int small_warning(const char *message) { warnkuva = new Bitmap("WARN2"); + wait_mouse_relase(); // ensure that exiting requires a click + while (flag) { menu_keys(&exit_flag, NULL); menu_mouse(&x, &y, &n1, &n2, positions); From 2c476c093be2c1db0ac68c05d634ffefb7289913 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Tue, 6 Jan 2015 23:44:57 +0200 Subject: [PATCH 22/54] Upgrade to SDL 2.0 SDL 2.0 supports scaling to arbitrary window sizes and normal-resolution full-screen mode. --- Makefile | 4 +- src/gfx/bitmap.cpp | 112 ++++++--------------------- src/gfx/bitmap.h | 2 +- src/io/joystick.cpp | 8 +- src/io/mouse.cpp | 50 +++++++++++- src/io/sdl_compat.cpp | 18 ++--- src/io/sdl_compat.h | 2 +- src/io/video.cpp | 169 +++++++++++++++-------------------------- src/io/video.h | 7 +- src/menus/tripmenu.cpp | 41 +++++----- src/settings.cpp | 6 +- src/triplane.cpp | 48 +++++++----- 12 files changed, 204 insertions(+), 263 deletions(-) diff --git a/Makefile b/Makefile index af162c0..8f21193 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ DESTDIR ?= CXX ?= g++ OPTIFLAG = -O2 -g -SDL_CONFIG ?= sdl-config +SDL_CONFIG ?= sdl2-config VERSION = 1.0.8 CFLAGS := -Wall -Isrc $(OPTIFLAG) $(CFLAGS_NOSDL) `$(SDL_CONFIG) --cflags` -DHAVE_SDL_MIXER "-DTRIPLANE_DATA=\"$(PREFIX)/share/games/triplane\"" "-DTRIPLANE_VERSION=\"$(VERSION)\"" LDFLAGS = -LIBS := `$(SDL_CONFIG) --libs` -lSDL_mixer -lm +LIBS := `$(SDL_CONFIG) --libs` -lSDL2_mixer -lm INSTALL_DATA ?= install -m 644 INSTALL_PROGRAM ?= install diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index bb792be..1997373 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -80,102 +80,39 @@ void all_bitmaps_refresh(void) { } } -/* Make a copy of the image data in source, enlarged zoom times */ -static unsigned char *duplicate_enlarged(const unsigned char *source, int width, int height, int zoom) { - uint8_t *target = (uint8_t *) walloc(width * height * zoom * zoom); - int i, j, k; - const uint8_t *in = source; - uint8_t *out = target; - - /* optimized versions using 32-bit and 16-bit writes when possible */ - if (zoom == 4 && sizeof(char *) >= 4) { /* word size >= 4 */ - uint32_t cccc; - for (j = 0; j < height * zoom; j += zoom) { - for (i = 0; i < width * zoom; i += zoom) { - cccc = *in | (*in << 8) | (*in << 16) | (*in << 24); - in++; - for (k = 0; k < zoom; k++) { - *(uint32_t *) (&out[(j + k) * (width * zoom) + i]) = cccc; - } - } - } - } else if (zoom == 3) { - uint16_t cc, c; - for (j = 0; j < height * zoom; j += zoom) { - for (i = 0; i < width * zoom; i += zoom) { - c = *in++; - cc = c | (c << 8); - for (k = 0; k < zoom; k++) { - *(uint16_t *) (&out[(j + k) * (width * zoom) + i]) = cc; - out[(j + k) * (width * zoom) + i + 2] = c; - } - } - } - } else if (zoom == 2) { - uint16_t cc; - for (j = 0; j < height * zoom; j += zoom) { - for (i = 0; i < width * zoom; i += zoom) { - cc = *in | (*in << 8); - in++; - for (k = 0; k < zoom; k++) { - *(uint16_t *) (&out[(j + k) * (width * zoom) + i]) = cc; - } - } - } - } else { /* unoptimized version */ - int l; - uint8_t c; - for (j = 0; j < height * zoom; j += zoom) { - for (i = 0; i < width * zoom; i += zoom) { - c = *in++; - for (k = 0; k < zoom; k++) { - for (l = 0; l < zoom; l++) { - out[(j + k) * (width * zoom) + (i + l)] = c; - } - } - } - } - } - - return target; -} - void Bitmap::refresh_sdlsurface() { - unsigned char *imgmult = NULL; SDL_Surface *tmps; if (sdlsurface != NULL) { - SDL_FreeSurface(sdlsurface); + SDL_DestroyTexture(sdlsurface); sdlsurface = NULL; } if (draw_with_vircr_mode) return; /* sdlsurfaces are not used */ - if (pixel_multiplier > 1) { - imgmult = duplicate_enlarged(image_data, width, height, pixel_multiplier); - tmps = SDL_CreateRGBSurfaceFrom(imgmult, width * pixel_multiplier, height * pixel_multiplier, 8, width * pixel_multiplier, 0, 0, 0, 0); - } else { - tmps = SDL_CreateRGBSurfaceFrom(image_data, width, height, 8, width, 0, 0, 0, 0); - } + tmps = SDL_CreateRGBSurfaceFrom(image_data, width, height, 8, width, 0, 0, 0, 0); if (tmps == NULL) { fprintf(stderr, "SDL_CreateRGBSurfaceFrom: %s\n", SDL_GetError()); exit(1); } - SDL_SetPalette(tmps, SDL_LOGPAL, curpal, 0, 256); + + SDL_Palette *tmppal = SDL_AllocPalette(256); + SDL_SetPaletteColors(tmppal, curpal, 0, 256); + SDL_SetSurfacePalette(tmps, tmppal); + if (hastransparency) - SDL_SetColorKey(tmps, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0xff); - sdlsurface = SDL_DisplayFormat(tmps); + SDL_SetColorKey(tmps, SDL_TRUE, 0xff); + + sdlsurface = SDL_CreateTextureFromSurface(video_state.renderer, tmps); if (sdlsurface == NULL) { - fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError()); + fprintf(stderr, "SDL_CreateTextureFromSurface: %s\n", SDL_GetError()); exit(1); } SDL_FreeSurface(tmps); - if (pixel_multiplier > 1) { - wfree(imgmult); - } + SDL_FreePalette(tmppal); } Bitmap::Bitmap(const char *image_name, int transparent) { @@ -279,7 +216,7 @@ Bitmap::Bitmap(int width, int height, Bitmap::~Bitmap() { all_bitmaps_delete(id); if (sdlsurface != NULL) { - SDL_FreeSurface(sdlsurface); + SDL_DestroyTexture(sdlsurface); sdlsurface = NULL; } if (!external_image_data) @@ -294,8 +231,9 @@ void Bitmap::blit_fullscreen(void) { if (update_vircr_mode) memcpy(vircr, image_data, 320 * 200); - if (!draw_with_vircr_mode) - SDL_BlitSurface(sdlsurface, NULL, video_state.surface, NULL); + if (!draw_with_vircr_mode) { + SDL_RenderCopy(video_state.renderer, sdlsurface, NULL, NULL); + } } /* @@ -366,20 +304,14 @@ void Bitmap::blit(int xx, int yy, int rx, int ry, int rx2, int ry2) { clip.h = ry2 - ry + 1; pos.x = xx; pos.y = yy; - if (pixel_multiplier > 1) { - clip.x *= pixel_multiplier; - clip.y *= pixel_multiplier; - clip.w *= pixel_multiplier; - clip.h *= pixel_multiplier; - pos.x *= pixel_multiplier; - pos.y *= pixel_multiplier; - } - SDL_SetClipRect(video_state.surface, &clip); - if (SDL_BlitSurface(sdlsurface, NULL, video_state.surface, &pos) != 0) { - fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError()); + pos.w = width; + pos.h = height; + SDL_RenderSetClipRect(video_state.renderer, &clip); + if (SDL_RenderCopy(video_state.renderer, sdlsurface, NULL, &pos) != 0) { + fprintf(stderr, "SDL_RenderCopy: %s\n", SDL_GetError()); exit(1); } - SDL_SetClipRect(video_state.surface, NULL); + SDL_RenderSetClipRect(video_state.renderer, NULL); } } diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index 4673ce7..41899e1 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -32,7 +32,7 @@ class Bitmap { int external_image_data; // boolean: is image_data owned by this instance int hastransparency; int id; // a unique ID for this Bitmap - SDL_Surface *sdlsurface; + SDL_Texture *sdlsurface; // the field name derives from SDL 1.x legacy public: Bitmap(const char *image_name, int transparent = 1); diff --git a/src/io/joystick.cpp b/src/io/joystick.cpp index 0dc8736..a7edd10 100644 --- a/src/io/joystick.cpp +++ b/src/io/joystick.cpp @@ -117,20 +117,20 @@ void save_joysticks_data(const char *filename) { */ void open_close_joysticks(int joy1, int joy2) { if (SDL_NumJoysticks() >= 1) { - if (!joy1 && SDL_JoystickOpened(0)) { + if (!joy1 && joydev[0] != NULL) { SDL_JoystickClose(joydev[0]); joydev[0] = NULL; } - if (joy1 && !SDL_JoystickOpened(0)) { + if (joy1 && joydev[0] == NULL) { joydev[0] = SDL_JoystickOpen(0); } } if (SDL_NumJoysticks() >= 2) { - if (!joy2 && SDL_JoystickOpened(1)) { + if (!joy2 && joydev[1] != NULL) { SDL_JoystickClose(joydev[1]); joydev[1] = NULL; } - if (joy2 && !SDL_JoystickOpened(1)) { + if (joy2 && joydev[1] == NULL) { joydev[1] = SDL_JoystickOpen(1); } } diff --git a/src/io/mouse.cpp b/src/io/mouse.cpp index b60abea..d9fd6df 100644 --- a/src/io/mouse.cpp +++ b/src/io/mouse.cpp @@ -29,18 +29,60 @@ #include "io/video.h" #include +/* + * SDL_RenderSetLogicalSize does not expose functions to calculate its + * mapping, and both SDL_WarpMouseInWindow and SDL_GetMouseState work + * with physical coordinates. We thus need to convert them manually. + * Perhaps one day SDL will have equivalent functions that we can use + * instead of these... + */ +static void logical_to_physical(int logx, int logy, int *physx, int *physy) { + float scaleX, scaleY; + SDL_Rect vp; + + SDL_RenderGetScale(video_state.renderer, &scaleX, &scaleY); + SDL_RenderGetViewport(video_state.renderer, &vp); + + *physx = floor(vp.x * scaleX) + floor(logx * scaleX); + *physy = floor(vp.y * scaleY) + floor(logy * scaleY); +} + +static void physical_to_logical(int physx, int physy, int *logx, int *logy) { + float scaleX, scaleY; + SDL_Rect vp; + + SDL_RenderGetScale(video_state.renderer, &scaleX, &scaleY); + SDL_RenderGetViewport(video_state.renderer, &vp); + + if (physx / scaleX <= vp.x) + *logx = 0; + else if (physx / scaleX >= vp.x + vp.w) + *logx = vp.w - 1; + else + *logx = ceil((physx - floor(vp.x * scaleX)) / scaleX); + + if (physy / scaleY <= vp.y) + *logy = 0; + else if (physy / scaleY >= vp.y + vp.h) + *logy = vp.h - 1; + else + *logy = ceil((physy - floor(vp.y * scaleY)) / scaleY); +} + void hiiri_to(int x, int y) { - SDL_WarpMouse(x * pixel_multiplier, y * pixel_multiplier); + int px, py; + logical_to_physical(x, y, &px, &py); + SDL_WarpMouseInWindow(video_state.window, px, py); } void koords(int *x, int *y, int *n1, int *n2) { Uint8 ret; + int px, py; SDL_PumpEvents(); - ret = SDL_GetMouseState(x, y); + ret = SDL_GetMouseState(&px, &py); *n1 = !!(ret & SDL_BUTTON(1)); *n2 = !!(ret & SDL_BUTTON(3)); - *x /= pixel_multiplier; - *y /= pixel_multiplier; + physical_to_logical(px, py, x, y); } diff --git a/src/io/sdl_compat.cpp b/src/io/sdl_compat.cpp index e0e21ea..f88718a 100644 --- a/src/io/sdl_compat.cpp +++ b/src/io/sdl_compat.cpp @@ -37,8 +37,8 @@ #include "util/wutil.h" #include "io/timing.h" -Uint8 *key; -int key_size; +const Uint8 *key = NULL; +int key_size = 0; int kbhit(void) { SDL_Event e; @@ -47,7 +47,7 @@ int kbhit(void) { nopeuskontrolli(); SDL_PumpEvents(); - ret = SDL_PeepEvents(&e, 1, SDL_PEEKEVENT, SDL_EVENTMASK(SDL_KEYUP)); + ret = SDL_PeepEvents(&e, 1, SDL_PEEKEVENT, SDL_KEYUP, SDL_KEYUP); if (ret) { // leave SDL_KEYUP event in queue (getch() should be called next) return 1; @@ -88,22 +88,21 @@ void update_key_state(void) { while (getch() != 0) ; - key = SDL_GetKeyState(&key_size); + key = SDL_GetKeyboardState(&key_size); } void wait_relase(void) { int c = 0; - while (c != SDLK_LAST) { + while (c != key_size) { nopeuskontrolli(); do_all(); update_key_state(); - for (c = 0; c < SDLK_LAST; c++) - if (key[c] && c != SDLK_NUMLOCK && c != SDLK_CAPSLOCK && c != SDLK_SCROLLOCK) + for (c = 0; c < key_size; c++) + if (key[c]) break; } - } /** @@ -244,8 +243,7 @@ sb_mod_file *sdl_load_mod_file(const char *name) { mod = (sb_mod_file *) walloc(sizeof(sb_mod_file)); rwops = SDL_RWFromConstMem(p, len); - mod->music = Mix_LoadMUS_RW(rwops); - SDL_FreeRW(rwops); + mod->music = Mix_LoadMUSType_RW(rwops, MUS_MOD, SDL_TRUE); if (mod->music == NULL) { fprintf(stderr, "sdl_load_mod_file: %s\n", Mix_GetError()); exit(1); diff --git a/src/io/sdl_compat.h b/src/io/sdl_compat.h index 045becd..5800539 100644 --- a/src/io/sdl_compat.h +++ b/src/io/sdl_compat.h @@ -45,7 +45,7 @@ typedef struct { #endif } sb_mod_file; -extern Uint8 *key; +extern const Uint8 *key; extern int key_size; int kbhit(void); diff --git a/src/io/video.cpp b/src/io/video.cpp index c8e0789..6338ced 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -26,7 +26,7 @@ #include #include -struct video_state_t video_state = { NULL, 0, 0 }; +struct video_state_t video_state = { NULL, NULL, NULL, NULL, 0 }; struct naytto ruutu; @@ -34,7 +34,6 @@ int current_mode = VGA_MODE; unsigned char *vircr; int update_vircr_mode = 1; int draw_with_vircr_mode = 1; -int pixel_multiplier = 1; /* current pixel multiplier */ int pixel_multiplier_vga = 1, pixel_multiplier_svga = 1; int wantfullscreen = 1; @@ -58,17 +57,13 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { cc[i].g = 4 * pal[from][1]; cc[i].b = 4 * pal[from][2]; } + cc[i].a = 255; if (reverse) from--; else from++; } - if (draw_with_vircr_mode) { - SDL_SetPalette(video_state.surface, video_state.haverealpalette ? SDL_PHYSPAL : SDL_LOGPAL, cc, firstcolor, n); - } else { - SDL_SetPalette(video_state.surface, SDL_PHYSPAL | SDL_LOGPAL, cc, firstcolor, n); - } memcpy(&curpal[firstcolor], cc, n * sizeof(SDL_Color)); wfree(cc); @@ -76,13 +71,6 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { all_bitmaps_refresh(); } -static Uint32 getcolor(unsigned char c) { - if (video_state.haverealpalette) - return c; - else - return SDL_MapRGB(video_state.surface->format, curpal[c].r, curpal[c].g, curpal[c].b); -} - void fillrect(int x, int y, int w, int h, int c) { SDL_Rect r; @@ -104,75 +92,42 @@ void fillrect(int x, int y, int w, int h, int c) { r.y = y; r.w = w; r.h = h; - if (pixel_multiplier > 1) { - r.x *= pixel_multiplier; - r.y *= pixel_multiplier; - r.w *= pixel_multiplier; - r.h *= pixel_multiplier; - } - SDL_FillRect(video_state.surface, &r, getcolor(c)); + SDL_SetRenderDrawColor(video_state.renderer, + curpal[c].r, + curpal[c].g, + curpal[c].b, + curpal[c].a); + SDL_RenderFillRect(video_state.renderer, &r); } void do_all(int do_retrace) { if (draw_with_vircr_mode) { - if (pixel_multiplier > 1) { - int i, j, k; - int w = (current_mode == VGA_MODE) ? 320 : 800; - int h = (current_mode == VGA_MODE) ? 200 : 600; - uint8_t *in = vircr, *out = (uint8_t *) video_state.surface->pixels; - /* optimized versions using 32-bit and 16-bit writes when possible */ - if (pixel_multiplier == 4 && sizeof(char *) >= 4) { /* word size >= 4 */ - uint32_t cccc; - for (j = 0; j < h * pixel_multiplier; j += pixel_multiplier) { - for (i = 0; i < w * pixel_multiplier; i += pixel_multiplier) { - cccc = *in | (*in << 8) | (*in << 16) | (*in << 24); - in++; - for (k = 0; k < pixel_multiplier; k++) { - *(uint32_t *) (&out[(j + k) * (w * pixel_multiplier) + i]) = cccc; - } - } - } - } else if (pixel_multiplier == 3) { - uint16_t cc, c; - for (j = 0; j < h * pixel_multiplier; j += pixel_multiplier) { - for (i = 0; i < w * pixel_multiplier; i += pixel_multiplier) { - c = *in++; - cc = c | (c << 8); - for (k = 0; k < pixel_multiplier; k++) { - *(uint16_t *) (&out[(j + k) * (w * pixel_multiplier) + i]) = cc; - out[(j + k) * (w * pixel_multiplier) + i + 2] = c; - } - } - } - } else if (pixel_multiplier == 2) { - uint16_t cc; - for (j = 0; j < h * pixel_multiplier; j += pixel_multiplier) { - for (i = 0; i < w * pixel_multiplier; i += pixel_multiplier) { - cc = *in | (*in << 8); - in++; - for (k = 0; k < pixel_multiplier; k++) { - *(uint16_t *) (&out[(j + k) * (w * pixel_multiplier) + i]) = cc; - } - } - } - } else { /* unoptimized version */ - int l; - uint8_t c; - for (j = 0; j < h * pixel_multiplier; j += pixel_multiplier) { - for (i = 0; i < w * pixel_multiplier; i += pixel_multiplier) { - c = *in++; - for (k = 0; k < pixel_multiplier; k++) { - for (l = 0; l < pixel_multiplier; l++) { - out[(j + k) * (w * pixel_multiplier) + (i + l)] = c; - } - } - } - } - } - } + int w = (current_mode == VGA_MODE) ? 320 : 800; + int wh = (current_mode == VGA_MODE) ? 320 * 200 : 800 * 600; + int i; + uint8_t *in = vircr; + uint32_t *out = video_state.texture_buffer; + + for (i = 0; i < wh; i++, in++, out++) + *out = ((255 << 24) | + (curpal[*in].r << 16) | + (curpal[*in].g << 8) | + curpal[*in].b); + + SDL_UpdateTexture(video_state.texture, + NULL, + video_state.texture_buffer, + w * sizeof(uint32_t)); + SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); + SDL_RenderClear(video_state.renderer); + SDL_RenderCopy(video_state.renderer, video_state.texture, NULL, NULL); + } + SDL_RenderPresent(video_state.renderer); + if (!draw_with_vircr_mode) { + // Prepare for drawing the next frame + SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); + SDL_RenderClear(video_state.renderer); } - - SDL_Flip(video_state.surface); } static void sigint_handler(int dummy) { @@ -191,50 +146,52 @@ void init_video(void) { signal(SIGINT, sigint_handler); atexit(SDL_Quit); video_state.init_done = 1; - - SDL_WM_SetCaption("Triplane Classic", "Triplane Classic"); + video_state.window = SDL_CreateWindow("Triplane Classic", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + pixel_multiplier_vga * 320, + pixel_multiplier_vga * 200, + (wantfullscreen + ? SDL_WINDOW_FULLSCREEN_DESKTOP + : SDL_WINDOW_RESIZABLE)); + assert(video_state.window); + video_state.renderer = SDL_CreateRenderer(video_state.window, -1, 0); + assert(video_state.renderer); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); SDL_ShowCursor(SDL_DISABLE); - - if (!draw_with_vircr_mode) { - vircr = (unsigned char *) walloc(800 * 600); - } + vircr = (unsigned char *) walloc(800 * 600); } } static int init_mode(int new_mode, const char *paletname) { - Uint32 mode_flags; - const SDL_VideoInfo *vi; int las, las2; int w = (new_mode == SVGA_MODE) ? 800 : 320; int h = (new_mode == SVGA_MODE) ? 600 : 200; init_video(); - mode_flags = SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWPALETTE; - - if (!draw_with_vircr_mode) - mode_flags |= SDL_ANYFORMAT; - if (wantfullscreen) - mode_flags |= SDL_FULLSCREEN; - - if (draw_with_vircr_mode && pixel_multiplier > 1) - wfree(vircr); + // FIXME resize the window in non-fullscreen mode? (what about user resizes?) - pixel_multiplier = (new_mode == SVGA_MODE) ? pixel_multiplier_svga : pixel_multiplier_vga; + if (video_state.texture != NULL) { + SDL_DestroyTexture(video_state.texture); + video_state.texture = NULL; + } + if (video_state.texture_buffer != NULL) { + wfree(video_state.texture_buffer); + video_state.texture_buffer = NULL; + } - video_state.surface = SDL_SetVideoMode(w * pixel_multiplier, h * pixel_multiplier, 8, mode_flags); - assert(video_state.surface); + SDL_RenderSetLogicalSize(video_state.renderer, w, h); if (draw_with_vircr_mode) { - if (pixel_multiplier > 1) { - vircr = (uint8_t *) walloc(w * h); - } else { - vircr = (uint8_t *) video_state.surface->pixels; - } + video_state.texture = SDL_CreateTexture(video_state.renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + w, + h); + assert(video_state.texture); + video_state.texture_buffer = (uint32_t *) walloc(w * h * sizeof(uint32_t)); } - /* else vircr is preallocated in init_video */ - vi = SDL_GetVideoInfo(); - video_state.haverealpalette = (vi->vfmt->palette != NULL); dksopen(paletname); diff --git a/src/io/video.h b/src/io/video.h index b85d1a5..2a928f7 100644 --- a/src/io/video.h +++ b/src/io/video.h @@ -35,9 +35,11 @@ struct naytto { extern naytto ruutu; struct video_state_t { - SDL_Surface *surface; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *texture; + uint32_t *texture_buffer; int init_done; - int haverealpalette; }; extern struct video_state_t video_state; extern SDL_Color curpal[256]; @@ -54,7 +56,6 @@ void init_video(void); extern unsigned char *vircr; extern Bitmap *standard_background; extern int current_mode; -extern int pixel_multiplier; extern int update_vircr_mode; extern int draw_with_vircr_mode; extern int pixel_multiplier_vga, pixel_multiplier_svga; diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 17542bb..6883878 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -45,7 +45,6 @@ int mission_headline_pixels; int aces_number_of_entries; int aces_score[MAX_PLAYERS_IN_ROSTER]; - #define CHARS_PER_LINE 70 #define LINELENGHT 100 @@ -773,10 +772,10 @@ void roster_menu(void) { setk1->blit(125, 172); frost->printf(125, 93, "Up [%s]\nDown [%s]\nRoll [%s]", - SDL_GetKeyName((SDLKey) roster[number].up), SDL_GetKeyName((SDLKey) roster[number].down), - SDL_GetKeyName((SDLKey) roster[number].roll)); - frost->printf(170, 114, "Power [%s]\nBombs [%s]\nGuns [%s]\n", SDL_GetKeyName((SDLKey) roster[number].power), - SDL_GetKeyName((SDLKey) roster[number].bombs), SDL_GetKeyName((SDLKey) roster[number].guns)); + SDL_GetKeyName((SDL_Keycode) roster[number].up), SDL_GetKeyName((SDL_Keycode) roster[number].down), + SDL_GetKeyName((SDL_Keycode) roster[number].roll)); + frost->printf(170, 114, "Power [%s]\nBombs [%s]\nGuns [%s]\n", SDL_GetKeyName((SDL_Keycode) roster[number].power), + SDL_GetKeyName((SDL_Keycode) roster[number].bombs), SDL_GetKeyName((SDL_Keycode) roster[number].guns)); } } else { @@ -981,40 +980,40 @@ void roster_menu(void) { rosteri->blit(0, 0); - frost->printf(125, 100, "Key for upward turn [%s]", SDL_GetKeyName((SDLKey) roster[number].up)); + frost->printf(125, 100, "Key for upward turn [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].up)); do_all(); roster[number].up = select_key(roster[number].up); rosteri->blit(0, 0); - frost->printf(125, 100, "Key for downward turn [%s]", SDL_GetKeyName((SDLKey) roster[number].down)); + frost->printf(125, 100, "Key for downward turn [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].down)); do_all(); roster[number].down = select_key(roster[number].down); rosteri->blit(0, 0); - frost->printf(125, 100, "Key for roll [%s]", SDL_GetKeyName((SDLKey) roster[number].roll)); + frost->printf(125, 100, "Key for roll [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].roll)); do_all(); roster[number].roll = select_key(roster[number].roll); rosteri->blit(0, 0); - frost->printf(125, 100, "Key for engine power [%s]", SDL_GetKeyName((SDLKey) roster[number].power)); + frost->printf(125, 100, "Key for engine power [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].power)); do_all(); roster[number].power = select_key(roster[number].power); rosteri->blit(0, 0); - frost->printf(125, 100, "Key for bomb drop [%s]", SDL_GetKeyName((SDLKey) roster[number].bombs)); + frost->printf(125, 100, "Key for bomb drop [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].bombs)); do_all(); roster[number].bombs = select_key(roster[number].bombs); rosteri->blit(0, 0); - frost->printf(125, 100, "Key for guns [%s]", SDL_GetKeyName((SDLKey) roster[number].guns)); + frost->printf(125, 100, "Key for guns [%s]", SDL_GetKeyName((SDL_Keycode) roster[number].guns)); do_all(); roster[number].guns = select_key(roster[number].guns); @@ -2152,10 +2151,10 @@ void controls_menu(void) { if (config.joystick[0] != active && config.joystick[1] != active) { frost->printf(170, 93, "Up [%s] Down [%s] Roll [%s]", - SDL_GetKeyName((SDLKey) player_keys[active].up), SDL_GetKeyName((SDLKey) player_keys[active].down), - SDL_GetKeyName((SDLKey) player_keys[active].roll)); - frost->printf(170, 100, "Power [%s] Bombs [%s] Guns [%s]", SDL_GetKeyName((SDLKey) player_keys[active].power), - SDL_GetKeyName((SDLKey) player_keys[active].bombs), SDL_GetKeyName((SDLKey) player_keys[active].guns)); + SDL_GetKeyName((SDL_Keycode) player_keys[active].up), SDL_GetKeyName((SDL_Keycode) player_keys[active].down), + SDL_GetKeyName((SDL_Keycode) player_keys[active].roll)); + frost->printf(170, 100, "Power [%s] Bombs [%s] Guns [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].power), + SDL_GetKeyName((SDL_Keycode) player_keys[active].bombs), SDL_GetKeyName((SDL_Keycode) player_keys[active].guns)); } else { int joy = (config.joystick[0] == active) ? 0 : 1; char *ups = get_joy_action_string(&joystick_config[joy].up); @@ -2200,42 +2199,42 @@ void controls_menu(void) { controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for upward turn [%s]", SDL_GetKeyName((SDLKey) player_keys[active].up)); + frost->printf(56, 97, "Key for upward turn [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].up)); do_all(); player_keys[active].up = select_key(player_keys[active].up); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for downward turn [%s]", SDL_GetKeyName((SDLKey) player_keys[active].down)); + frost->printf(56, 97, "Key for downward turn [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].down)); do_all(); player_keys[active].down = select_key(player_keys[active].down); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for roll [%s]", SDL_GetKeyName((SDLKey) player_keys[active].roll)); + frost->printf(56, 97, "Key for roll [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].roll)); do_all(); player_keys[active].roll = select_key(player_keys[active].roll); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for engine power [%s]", SDL_GetKeyName((SDLKey) player_keys[active].power)); + frost->printf(56, 97, "Key for engine power [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].power)); do_all(); player_keys[active].power = select_key(player_keys[active].power); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for bomb drop [%s]", SDL_GetKeyName((SDLKey) player_keys[active].bombs)); + frost->printf(56, 97, "Key for bomb drop [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].bombs)); do_all(); player_keys[active].bombs = select_key(player_keys[active].bombs); controlme->blit(0, 0); napp[active]->blit((active % 2) * 27 + 10, (active / 2) * 23 + 22); - frost->printf(56, 97, "Key for guns [%s]", SDL_GetKeyName((SDLKey) player_keys[active].guns)); + frost->printf(56, 97, "Key for guns [%s]", SDL_GetKeyName((SDL_Keycode) player_keys[active].guns)); do_all(); player_keys[active].guns = select_key(player_keys[active].guns); diff --git a/src/settings.cpp b/src/settings.cpp index d2ea570..9689924 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -116,8 +116,8 @@ int select_key(int old) { if (ch == SDLK_ESCAPE) return old; - else if (ch == SDLK_PAUSE || ch == SDLK_NUMLOCK || - ch == SDLK_CAPSLOCK || ch == SDLK_SCROLLOCK) + else if (ch == SDLK_PAUSE || ch == SDLK_NUMLOCKCLEAR || + ch == SDLK_CAPSLOCK || ch == SDLK_SCROLLLOCK) continue; else return ch; @@ -255,7 +255,7 @@ void load_keyset(void) { player_keys[1].up = SDLK_DOWN; player_keys[1].down = SDLK_UP; - player_keys[1].roll = SDLK_KP5; + player_keys[1].roll = SDLK_KP_5; player_keys[1].power = SDLK_KP_PLUS; player_keys[1].guns = SDLK_HOME; player_keys[1].bombs = SDLK_LEFT; diff --git a/src/triplane.cpp b/src/triplane.cpp index 020a654..d7e1fad 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -946,24 +946,30 @@ void set_player_types(void) { } void set_keys_none(void) { - memset(current_keys, 0, 4*sizeof(keymap)); // SDLK_UNKNOWN is 0 + // SDLK_UNKNOWN and SDL_SCANCODE_UNKNOWN are 0 + memset(current_keys, 0, 4*sizeof(keymap)); current_joystick[0] = -1; current_joystick[1] = -1; } void set_keys_from_multiplayer(int country) { - memcpy(¤t_keys[country], &player_keys[country], sizeof(keymap)); + current_keys[country].up = SDL_GetScancodeFromKey(player_keys[country].up); + current_keys[country].down = SDL_GetScancodeFromKey(player_keys[country].down); + current_keys[country].roll = SDL_GetScancodeFromKey(player_keys[country].roll); + current_keys[country].power = SDL_GetScancodeFromKey(player_keys[country].power); + current_keys[country].guns = SDL_GetScancodeFromKey(player_keys[country].guns); + current_keys[country].bombs = SDL_GetScancodeFromKey(player_keys[country].bombs); current_joystick[0] = config.joystick[0]; current_joystick[1] = config.joystick[1]; } void set_keys_from_roster(int country, int player_num) { - current_keys[country].up = roster[player_num].up; - current_keys[country].down = roster[player_num].down; - current_keys[country].roll = roster[player_num].roll; - current_keys[country].power = roster[player_num].power; - current_keys[country].guns = roster[player_num].guns; - current_keys[country].bombs = roster[player_num].bombs; + current_keys[country].up = SDL_GetScancodeFromKey(roster[player_num].up); + current_keys[country].down = SDL_GetScancodeFromKey(roster[player_num].down); + current_keys[country].roll = SDL_GetScancodeFromKey(roster[player_num].roll); + current_keys[country].power = SDL_GetScancodeFromKey(roster[player_num].power); + current_keys[country].guns = SDL_GetScancodeFromKey(roster[player_num].guns); + current_keys[country].bombs = SDL_GetScancodeFromKey(roster[player_num].bombs); // don't set current_joystick } @@ -1712,41 +1718,47 @@ void main_engine(void) { while (flag) { update_key_state(); - if (key[SDLK_PAUSE]) { + if (key && key[SDL_SCANCODE_PAUSE]) { // wait until pause key is released, then pressed and released again - while (key[SDLK_PAUSE]) { // still pressed + while (!key || key[SDL_SCANCODE_PAUSE]) { // still pressed nopeuskontrolli(); + do_all(); update_key_state(); } - while (!key[SDLK_PAUSE]) { // released + while (!key || !key[SDL_SCANCODE_PAUSE]) { // released nopeuskontrolli(); + do_all(); update_key_state(); } - while (key[SDLK_PAUSE]) { // pressed again + while (!key || key[SDL_SCANCODE_PAUSE]) { // pressed again nopeuskontrolli(); + do_all(); update_key_state(); } } // use F4 as an alias for the Pause key // because Pause does not always work reliably // (and is not present on all keyboards) - if (key[SDLK_F4]) { + if (key && key[SDL_SCANCODE_F4]) { // wait until F4 is released, then pressed and released again - while (key[SDLK_F4]) { // still pressed + while (!key || key[SDL_SCANCODE_F4]) { // still pressed nopeuskontrolli(); + do_all(); update_key_state(); } - while (!key[SDLK_F4]) { // released + while (!key || !key[SDL_SCANCODE_F4]) { // released nopeuskontrolli(); + do_all(); update_key_state(); } - while (key[SDLK_F4]) { // pressed again + while (!key || key[SDL_SCANCODE_F4]) { // pressed again nopeuskontrolli(); + do_all(); update_key_state(); } } - if (key[SDLK_ESCAPE]) { + if (key && key[SDL_SCANCODE_ESCAPE]) { flag = 0; mission_interrupted = 1; } @@ -1865,7 +1877,7 @@ void main_engine(void) { solo_success = 1; } - if (key[SDLK_F1] && key[SDLK_F2] && key[SDLK_F3]) { + if (key && key[SDL_SCANCODE_F1] && key[SDL_SCANCODE_F2] && key[SDL_SCANCODE_F3]) { frost->printf(40, 40, "SoloDestRemaining: %d. l=%d\n", solo_dest_remaining, l); } From 58be1502ef07f1cd6a8bdcdfbf8a18ddd6e3dfd0 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 5 Nov 2017 18:30:22 +0200 Subject: [PATCH 23/54] Fix bug in keyboard navigation of main menu --- src/menus/tripmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 6883878..9f79448 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2989,7 +2989,7 @@ void main_menu(void) { menu_position positions[] = { { 51, 40, 1 }, { 103, 40, 1 }, { 156, 40, 1 }, { 214, 40, 1 }, - { 268, 40, 1 }, { 51, 77, 1 }, { 268, 77, 1 }, { 51, 120, 1 }, + { 268, 40, 1 }, { 51, 77, 1 }, { 51, 120, 1 }, { 254, 120, 1 }, { 268, 169, 1 }, { 0, 0, -1 } }; help = new Bitmap("HELP1"); From db4d487dc03d98c0dfff4d6fb7a960a5f889afa6 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 5 Nov 2017 18:34:56 +0200 Subject: [PATCH 24/54] Use "Anonymous pilot" consistently --- src/menus/tripmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 9f79448..846968c 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2352,7 +2352,7 @@ void assign_menu(void) { ruksi->blit(28 + lx, 45 + ly + lym[config.player_type[l]]); if (config.player_number[l] == -2) { - frost->printf(46 + lx, 82 + ly, "(don't save in roster)"); + frost->printf(46 + lx, 82 + ly, "Anonymous pilot"); } else if (config.player_number[l] != -1) { frost->printf(46 + lx, 82 + ly, roster[config.player_number[l]].pilotname); } From 92cfdbfc62dcc3a7d1484d21a98c35426adafbd4 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 5 Nov 2017 19:00:03 +0200 Subject: [PATCH 25/54] Fix minor memory leak --- src/triplane.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/triplane.cpp b/src/triplane.cpp index d7e1fad..adbf8b2 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -2518,6 +2518,7 @@ void load_up(void) { explox[l][l2] = new Bitmap(1 + l2 * 10, 1 + l * 10, 9, 9, plane1); delete plane1; + wfree(point1); loading_text("Loading AA-MG animations"); From e083d134c2a0788686ddc91d19ca52be8f722075 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 5 Nov 2017 19:01:41 +0200 Subject: [PATCH 26/54] Update build instructions --- README.install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.install b/README.install index 3461016..8449daa 100644 --- a/README.install +++ b/README.install @@ -5,8 +5,8 @@ Dependencies ------------ * g++ -* SDL 1.2 -* SDL mixer 1.2 +* SDL 2.0 +* SDL mixer 2.0 * GNU make Build procedure From 2808eab6e628a883ea3de0e36525c57b8bb70564 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 5 Nov 2017 19:02:52 +0200 Subject: [PATCH 27/54] Update TODO list --- doc/TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/TODO b/doc/TODO index 4477e4f..5a30993 100644 --- a/doc/TODO +++ b/doc/TODO @@ -6,5 +6,4 @@ PDF manual Support for more than two joysticks Network game src/gfx/extra.cpp: embedded graphics, should read from data file instead -investigate if http://scale2x.sourceforge.net/ could be used for scaling From 226a31db4ecc5d603efdeff12bd6eb7d2c4b8193 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 24 Aug 2013 19:25:00 +0300 Subject: [PATCH 28/54] Basic network game support Support for playing over the network (LAN or Internet, but the server needs to be able to receive incoming TCP connections). It is controlled via a new Network game menu and new parts of the Assign players menu. The network game works, but is a bit unpolished, and there is no documentation yet. The network game uses the zlib compression library, which is now needed to compile Triplane. This commit adds new network game-related fields to the end of the Triplane configuration file triplane.cfg. Old configuration files are read and converted automatically. --- Makefile | 8 +- data/fokker.lst | 1 + data/menus/netmenu.pgd | Bin 0 -> 42737 bytes data_src/pcx.lst | 1 + data_src/pcx/menus/netmenu.pcx | Bin 0 -> 43696 bytes src/gfx/bitmap.cpp | 64 +- src/gfx/bitmap.h | 6 + src/gfx/fades.cpp | 15 +- src/gfx/fades.h | 1 + src/io/chat.cpp | 163 +++++ src/io/chat.h | 32 + src/io/netclient.cpp | 816 +++++++++++++++++++++ src/io/netclient.h | 28 + src/io/network.cpp | 1231 ++++++++++++++++++++++++++++++++ src/io/network.h | 293 ++++++++ src/io/sdl_compat.cpp | 89 ++- src/io/sdl_compat.h | 5 + src/io/timing.cpp | 4 + src/io/video.cpp | 32 + src/io/video.h | 1 + src/menus/tripmenu.cpp | 573 ++++++++++++++- src/menus/tripmenu.h | 1 + src/settings.cpp | 34 + src/settings.h | 11 +- src/triplane.cpp | 81 ++- src/util/wutil.cpp | 25 + src/util/wutil.h | 4 + 27 files changed, 3477 insertions(+), 42 deletions(-) create mode 100644 data/menus/netmenu.pgd create mode 100644 data_src/pcx/menus/netmenu.pcx create mode 100644 src/io/chat.cpp create mode 100644 src/io/chat.h create mode 100644 src/io/netclient.cpp create mode 100644 src/io/netclient.h create mode 100644 src/io/network.cpp create mode 100644 src/io/network.h diff --git a/Makefile b/Makefile index 8f21193..4cd76e3 100644 --- a/Makefile +++ b/Makefile @@ -7,20 +7,20 @@ SDL_CONFIG ?= sdl2-config VERSION = 1.0.8 CFLAGS := -Wall -Isrc $(OPTIFLAG) $(CFLAGS_NOSDL) `$(SDL_CONFIG) --cflags` -DHAVE_SDL_MIXER "-DTRIPLANE_DATA=\"$(PREFIX)/share/games/triplane\"" "-DTRIPLANE_VERSION=\"$(VERSION)\"" LDFLAGS = -LIBS := `$(SDL_CONFIG) --libs` -lSDL2_mixer -lm +LIBS := `$(SDL_CONFIG) --libs` -lSDL2_mixer -lz -lm INSTALL_DATA ?= install -m 644 INSTALL_PROGRAM ?= install COMMON_OBJS = src/gfx/bitmap.o src/gfx/font.o \ src/gfx/gfx.o src/util/wutil.o src/util/random.o \ - src/io/sdl_compat.o src/io/video.o \ - src/io/mouse.o src/io/dksfile.o src/io/timing.o + src/io/sdl_compat.o src/io/video.o src/io/network.o \ + src/io/chat.o src/io/mouse.o src/io/dksfile.o src/io/timing.o TRIPLANE_OBJS = src/triplane.o src/world/tripai.o \ src/world/tripmis.o src/gfx/fades.o \ src/menus/menusupport.o src/menus/tripmenu.o \ src/world/terrain.o src/world/fobjects.o src/world/tmexept.o \ src/gfx/extra.o src/settings.o src/world/plane.o src/io/joystick.o src/io/sound.o \ - src/world/tripaudio.o + src/world/tripaudio.o src/io/netclient.o LVLEDIT_OBJS = src/tools/lvledit/lvledit.o PGDVIEW_OBJS = src/tools/pgdview/pgdview.o PCX2PGD_OBJS = src/tools/pcx2pgd/pcx2pgd.o diff --git a/data/fokker.lst b/data/fokker.lst index 1ebd4dd..27a6832 100644 --- a/data/fokker.lst +++ b/data/fokker.lst @@ -68,6 +68,7 @@ data/menus/roster.pgd data/misc/score.pgd data/misc/lappu1.pgd data/misc/lappu2.pgd +data/menus/netmenu.pgd ;SFX data/sfx/aargh1.wav Die1 data/sfx/aargh2.wav Die2 diff --git a/data/menus/netmenu.pgd b/data/menus/netmenu.pgd new file mode 100644 index 0000000000000000000000000000000000000000..6d391ec5bcabbb3fc14f007a3abe38872884501f GIT binary patch literal 42737 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ⓈTk0+%3+@AhX9eBg`19d%g$KgJ2ii>YROM#wE7%-xSiPFdGlu(Iqk@^UK(3 z+UHFo$bD8;G&Q*n7=7-yW7F(`06aE-L+u_jQ}tQ=>+g2AKsLQLwflU@DzYjZ-|b1v^@(cajO!V# zG%*Zj4SBAKw|dHRY^fVeIGRt!&g7Gx>ZdYhvC;c(A2jCnP_av#>v6x1MaSlz(99X7 z(K}v$)XDj%g4v6xiD}}amFMwE?8Tv6 zbH88&-R*hBmskX)ah`E}WiIwc#e%Wku!4mX>QqX`t6|)oi^Ac(-P7gZZs!mi`LFNi zvr3!wc4FH8VYKLuMx##doU2W09&|?jwX%!Ni1q5kx1pUmB%LdggAK40OI+(%&|xe# zYyDXsSzSuy-`BF=JD-lHe~CL7>3?jAoVx&P%B+jc(@YaBY%T_R)(-kb z?Iq+H~iNB#NyvTdp>aHbL5Mo z);)~zS%wF7Fx@$?qV(qogJJDCv zV|2}P|8k7X(y9B6#UA?3%>A=0Db~qiG}vRzTlqsQ0RBEL}C@D<5**@`c{c~n(`u9pVZs_;e7Ys&Y5GiW<0Ip z=+xuZr8DEFeZBkOMldC`Z2s_L%$t(gQ|39BJC^RB=HCDj7cxe7I*Ytd&-^<=0JW5nsjT7bm75C^fdOolkeZBt2&t-^rwiM&)ipynd&tY z-E$UOEQ8dRPwMJ-KWDP25fLqD6k{3VDx$USsise(>d-F&?OSqeez$Ih@)EtZJCAlM ze2dTid#uK7K1SSR*17hDS^r`mzWK>~GojpROcLp2gRX;>F*_1m@l(y}-e!bB)W|^HXacwZ2A-Ljo-2952_o%FT$21!K9RN;IdkXs^?c zjOm%4TOstA#D)j6SLMn{|HcXfYFR_@Bz#TeK21(6@!8dC#x*^xJ>%>(rA0xmia;v` zWY1w$JsvBp^TJZOBmN>|uWv@=q4Cwr9G&?7#O%9%wIk|*oxHw@8-oyYs^ov2pKuz zHDhlN&wV#tqTJMSz@xvo6(cXuZlRa<)W zkUGgBo{nS}UT&06k#K#AM7iRfX>%zGtReHBepv3@8K>NollmU~w$BREFP`$S84FWS zT$yZa=V_j@kJZC1)BsXYI+M>EJVvIsvMmL^vQDK$C zcf9l(FOHadH0I=N*5H<{;({03^GMc~3_a7|>?EzN+O;AxJ1A3!SmT%GAmbeO8}ylh z@NRu8J5pQXo7{r`oQKZ2moS!al(9sW=*9@w>c=xrKN4G5k?0$%8pJ&N@r>WcPdzST z_M7f~jj218C(Lk_4C^B!YOPCct1I1Su)}LuJB6it>^7n>9vre0J_m1XH?!K6nLP_G z_!Dc+0kK{K^Z3&L#shg&E;EyF&Rl&PvwoB%-ou%*k$29_pHJhEeO0;AoL zNp&BueLg^*)~m}3uOGhn0xl>{d9nQ|fm-ewj6?sJnRUu{6(lI#;Wd(tv|F0Y(1<}x zGy@mt*{&ZYVa^y}3_GDxpsjw~{b+UY;tPlF$;&gjg_)6&xDihanmR^Vj^}@96tRk#yb9Q-P{TXI(9IF|Bv z>X^h4SYW0NP3%R&(HiYl0`+GYNVkB;qOms*^pc-A%*-}lX+Jj<)rosS)g{wn%Oa0o z$;kTQuU0Qy`KML9OM%6|+66XLmDN+qFQuj5Y1`h6jB-*sso=d^ zc+yG?y=j0uzgiu71eHf@Htd>s{?#QJwF7a|v-`zlm0LvW{?7uiAf*XXffL#1QRjQut+|A&^);<(;mI*jH8VQ*Eiy%@#wRm9Pwn~6T|~S zm*mES#L@P3+Gf=Ht@nKW37+5`?-(7WkxV3|&LmniiwP-Z=KYUWSCuomPhKqo-5RIe^ z)llrQC6aqMVbU2OWpW@30EIz=UQar7X4@SKvQTt}D*(FEnOZs9+1o;%bF+zMzFM;@ z#FN#xmaEI39{g&BgD)R`9*EG$Ea3tyJT@)$rfVnil!1LtJ^OpeJo9F*Qk0Dzpvg4l z+qik(7T;%`Puh-j^9y9?LL0ftf`0hb%U3}0(&dYB&l+{QW?Afpv(021vJUnqwWsbG z2U7*4;0s1~U0LGiBcIqXj{*S`v1B#&VKVP5?Y~?Ozq)eqh2_HKJF7pXdpO%v6kL-d z^Bp6gfS})gQPv2;rYst%>?AmU$Pg`ppbiPrAJ}x=vwbl#XNO9rpgN68G(1Z?w6|RN zWO@0@hxqt~9S%>t>V~BqOs|wL2~G6eQ0{LA(|1ZjG?cvK6Dmw;{M|lyoRsmi{w1-T zg}fyfEB+-`i%Z{p@#TM9zIAaOB>m^gdjl&aW+}LLE!kBEaT7-QV z>M|svVlw)i5z~{hregmQ$Uj*>TC=P1MQ4BH`})z*6Er_Le9Y&gjP8#2#@3H-Bjp^V zoo610p7rA+&z${ujYh7`m-DoMt)c&O{%h>@p{-+=$JT;5&X$yTtO=vi=<~$W7$%K2avdI zqAaU+rj&OAmzMTXN|dB!pD^awRD>v$4!JZ^ieVa~Lg~9bGo<9YRF(3=I5rP)&IX~Z z?svA@^r{`uyE(7vg}Nl02{CDY-!HjoB3#Y4!9FugOxQP2Gi%*ocet9gk9{X+tY|7Ep7#)xQcr+K)?KyJsmpT1+M3u9V8of*X zSe+P|T8#foH?uFW3FrSW{&ID|dj2Jf1O$%ias;e6P1)CU)1^jWMzT1b{k<|xR*kp+ zDP5RN5`7VnqLJ~EU?!1H6!7McbEOb7c~xG!x{h_d;i$fOBtz$@+Pz{NAr`YA_;eva zX#+isH3p@fTd!g-Br^%V+U?c(DSt>FP|;NKcn|Q@2=lD-RtnO3N}7l5M6NFPr}^V_ zM%2&+y?*NZFZMkiHuRMS6gl;I2#?u z=O)oGx?g&>r5_K;mmFwzQ;3!jds4Bc2$M^hIg6QNJhD9WJhnOK44Lva#OK+OQr(J- zoin;JI0Goc0%b`kOK zt=lkzapKM@#++E?(;BQ_UElNsd@=*?;FL3D4Zh%^lEEG+rS%{jNp#M&&HJt9j4R$m z4zH9!w|vXO5gjtcXzT{sAF|9Fl}#M+ipWNmw9BzbssFywP8+u!QL?w^){ktQGrYOA ztDQD$Cs9_TN8j*d_>L}-H#4mPbI?4gv*`Kh??zfXT?Wu1{%AAC{sry!*9aM9TZ$dy z(lfoDSorBnySqvP%WUksyf!b|G6u15*VK-qr*0NpQp2R2tFgEzYaENQmcj)0UT9FZ zETpzQAALeu{3ObvsdsXrxl0v<34JQ zk32-Y?DA^Y0`t+`3M*QeApWiEvaJ8o+b)J=E)?Mpgm{-jfTukyy6lT3vGuHe20C*}u(&mRhw|8^_9~hV3b-Q$FQEcTnvo z-bPijJvMcU^t2-@%Nymi>UlYboVylwz3lf}Hf=^1z0#;aHQW?wDxXN+SF4Xppc7Aeofvz4R< zc3!=Ie|kLFI@6cy%!yksGn4hEgHXBj6By}UHb18c*Q(fW3EVEGY`(7 zQnO}SyWXd?ijyCZX-@6SUCUjM#nx5HclXH2C|r?UB5oD)Sk~2uQC1tq)WEyae73r$ zJYF;2y|J>sI-lc6xXL!qi2%QvpT4`pZq!6@^zb8RPYcFGo}#68v>(2oHkQA|jkWl! zhg0`6nIbau9Qik5&hb5Y2hFEOj);O?W01+Uv-4pbX3{+LcxrB~wx*xz?75Fg$EBky zJ=q|VSu^9Gns%$R2&cMd-f%lrie9op$b`s!X)y^AGCj z))`rl4FjjalJFjs=K3m*vs;RHGhMVDKkZ*9NX>;&q7Acyce`#;H!IExC*55Sz4gXy zm0{M9`GqS;^j*)w7Gi2g$#V^6S~R9b+1!gk-hw$*rSf9yU zcp!43SS&<&tfe6HR6hr??&Ym$IU~;V-*@2N4SkNgR<%>u%X<%|eRDZigUE-U&4*W; z^~^)Qy9D!-6@c2-8f^UIb1<+Ro!vg3Ix31(7VDchTOFt53jDbH0@Lo1vhsoJ$rZS{ zd$!{@gQ=Q4WibD(I5%)peG5n!E zL9=$v(7dBI-{-E*&-Qp~M)_8b{UCd;Agk^o-yH0^xwE>Kn@Q^}IU9K@JKQgu>(QIg z?EP9b!i+yYIZO4mM(v_z;Q!&BG(Vd%cmJu*Pj}FZ?zZwQ4R)jw<6HS~{67}|_a6VZ zy!W1Oe7x@A>;7-U|GmE1_?8b2fBW0te)ZK?$H&Jge!VOYm*wMS`CwUou`EAdmUoxsr_1v8 zvb?n{KU|hKmgNV_a(!7gj~{Q|e}8lH=H~6UH#crzNqn|H5oetK>5x69W* zT0VJmx%K*T^9Rdj^LVp)f3vx{*}T2^j~kocUElof+U9r5*N>M^?ku*bSAmRq-`LvJl_yn#bEKGU)24{U3h#y_;`-_wR1} #include @@ -39,7 +40,7 @@ #define RLE_REPETITION_MARK 192 -#define MAX_BITMAPS 8192 +#define MAX_BITMAPS 8192 /* must be <= 65536 for netsend */ Bitmap *all_bitmaps[MAX_BITMAPS]; int all_bitmaps_last_alloc = -1; @@ -80,6 +81,26 @@ void all_bitmaps_refresh(void) { } } +void all_bitmaps_resend_if_sent(void) { + int i; + + for (i = 0; i < MAX_BITMAPS; i++) { + if (all_bitmaps[i] == NULL) + continue; + all_bitmaps[i]->resend_bitmapdata(); + } +} + +void all_bitmaps_send_now(void) { + int i; + + for (i = 0; i < MAX_BITMAPS; i++) { + if (all_bitmaps[i] == NULL) + continue; + all_bitmaps[i]->send_bitmapdata(); + } +} + void Bitmap::refresh_sdlsurface() { SDL_Surface *tmps; @@ -115,6 +136,22 @@ void Bitmap::refresh_sdlsurface() { SDL_FreePalette(tmppal); } +void Bitmap::send_bitmapdata() { + if (!data_sent) { + data_sent = netsend_bitmapdata(id, width, height, hastransparency, image_data); + } +} + +void Bitmap::resend_bitmapdata() { + if (data_sent) { + netsend_bitmapdata(id, width, height, hastransparency, image_data); + } +} + +void Bitmap::clear_data_sent() { + data_sent = 0; +} + Bitmap::Bitmap(const char *image_name, int transparent) { unsigned int xx, yy, lask, lask2; int laskx; @@ -183,6 +220,7 @@ Bitmap::Bitmap(const char *image_name, int transparent) { name = image_name; hastransparency = transparent; sdlsurface = NULL; + data_sent = 0; id = all_bitmaps_add(this); refresh_sdlsurface(); } @@ -198,6 +236,7 @@ Bitmap::Bitmap(int width, int height, this->name = name; this->hastransparency = hastransparency; this->sdlsurface = NULL; + this->data_sent = 0; if (copy_image_data) { this->image_data = (unsigned char *) walloc(width * height); @@ -221,6 +260,7 @@ Bitmap::~Bitmap() { } if (!external_image_data) free(image_data); + netsend_bitmapdel(id); } void Bitmap::blit_fullscreen(void) { @@ -228,6 +268,9 @@ void Bitmap::blit_fullscreen(void) { assert(!hastransparency); pointti = image_data; + send_bitmapdata(); + netsend_bitmapblitfs(id); + if (update_vircr_mode) memcpy(vircr, image_data, 320 * 200); @@ -274,6 +317,18 @@ void Bitmap::blit(int xx, int yy, int rx, int ry, int rx2, int ry2) { if (xx + width <= rx || xx > rx2 || yy + height <= ry || yy > ry2) return; /* no part is inside the clip rectangle */ + send_bitmapdata(); + if ((rx == 0 && /* no clip rectangle set */ + ry == 0 && + rx2 == ((current_mode == SVGA_MODE) ? 799 : 319) && + ry2 == ((current_mode == SVGA_MODE) ? 599 : 199)) || + /* or all of the bitmap is inside the clip rectangle */ + (xx >= rx && xx + width - 1 <= rx2 && + yy >= ry && yy + height - 1 <= ry2)) + netsend_bitmapblit(id, xx, yy); + else + netsend_bitmapblitclipped(id, xx, yy, rx, ry, rx2, ry2); + if (update_vircr_mode) { fromminy = (yy >= ry) ? 0 : ry - yy; fromminx = (xx >= rx) ? 0 : rx - xx; @@ -345,6 +400,7 @@ Bitmap::Bitmap(int x1, int y1, int xl, int yl, Bitmap * source_image) { name = source_image->name; hastransparency = source_image->hastransparency; sdlsurface = NULL; + data_sent = 0; id = all_bitmaps_add(this); refresh_sdlsurface(); } @@ -367,6 +423,7 @@ Bitmap::Bitmap(int x, int y, int w, int h) { name = "from_vircr"; hastransparency = 0; sdlsurface = NULL; + data_sent = 0; id = all_bitmaps_add(this); refresh_sdlsurface(); } @@ -393,6 +450,10 @@ void Bitmap::blit_to_bitmap(Bitmap * to, int xx, int yy) { to_point[laskx + xx + (lasky + yy) * kokox] = image_data[laskx + lasky * width]; } + if (to->data_sent) { + send_bitmapdata(); + netsend_blittobitmap(id, to->id, xx, yy); + } to->refresh_sdlsurface(); } @@ -430,6 +491,7 @@ Bitmap *rotate_bitmap(Bitmap * picture, int degrees) { } free(temp_data); + picture2->clear_data_sent(); picture2->refresh_sdlsurface(); return picture2; } diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index 41899e1..0210675 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -33,6 +33,7 @@ class Bitmap { int hastransparency; int id; // a unique ID for this Bitmap SDL_Texture *sdlsurface; // the field name derives from SDL 1.x legacy + int data_sent; // has image_data been sent to the network? public: Bitmap(const char *image_name, int transparent = 1); @@ -50,9 +51,14 @@ class Bitmap { void blit_to_bitmap(Bitmap * to, int xx, int yy); unsigned char *info(int *width = NULL, int *height = NULL); void refresh_sdlsurface(); + void clear_data_sent(); + void send_bitmapdata(); + void resend_bitmapdata(); }; void all_bitmaps_refresh(void); +void all_bitmaps_resend_if_sent(void); +void all_bitmaps_send_now(void); Bitmap *rotate_bitmap(Bitmap * picture, int degrees); int bitmap_exists(const char *name); diff --git a/src/gfx/fades.cpp b/src/gfx/fades.cpp index 088ec35..dcf72fd 100644 --- a/src/gfx/fades.cpp +++ b/src/gfx/fades.cpp @@ -21,6 +21,7 @@ #include #include "gfx/fades.h" #include "gfx/gfx.h" +#include "io/network.h" #include "util/wutil.h" static void horisontal_split(void) { @@ -133,16 +134,18 @@ static void partial_fade(void) { do_all(); } -void random_fade_out(void) { - int t; +void selected_fade_out(int t) { + int display_was_enabled; - t = wrandom(5); + netsend_fade_out(t); if (current_mode == SVGA_MODE) { do_all_clear(); return; } + display_was_enabled = network_display_enable(0); + switch (t) { case 0: horisontal_split(); @@ -165,6 +168,12 @@ void random_fade_out(void) { break; } + network_display_enable(display_was_enabled); +} +void random_fade_out(void) { + int t; + t = wrandom(5); + selected_fade_out(t); } diff --git a/src/gfx/fades.h b/src/gfx/fades.h index 3a0b26f..6b9f71c 100644 --- a/src/gfx/fades.h +++ b/src/gfx/fades.h @@ -21,6 +21,7 @@ #ifndef FADES_H #define FADES_H +void selected_fade_out(int t); void random_fade_out(void); #endif diff --git a/src/io/chat.cpp b/src/io/chat.cpp new file mode 100644 index 0000000..6f5519d --- /dev/null +++ b/src/io/chat.cpp @@ -0,0 +1,163 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +/******************************************************************************* + + Purpose: + Writing and sending chat messages in networked game + +*******************************************************************************/ + +#include "triplane.h" +#include "gfx/gfx.h" +#include "gfx/font.h" +#include "io/network.h" +#include "util/wutil.h" + +// Maximum length of a chat message in the line editor, not including +// the terminating NUL. Must be smaller than length of +// NET_PKTTYPE_C_CHATMSG msg. +#define EDITLINELEN 70 + +static const char chat_f6_message[] = "Help!"; +static const char chat_f7_message[] = "Yes, ok"; +static const char chat_f8_message[] = "No"; + +static Font *chat_font = NULL; +static void (*chat_sender)(const char *) = NULL; +static int chat_overlay_on = 0; +static char chat_text[EDITLINELEN + 1] = ""; +static int chat_textpos = 0; +static uint32_t chat_last_message_time = 0; + +static void chat_send(const char *msg) { + if (SDL_GetTicks() < chat_last_message_time + 1000) + return; // we are chatting too fast, ignore + + chat_last_message_time = SDL_GetTicks(); + + if (chat_sender != NULL) + chat_sender(msg); +} + +static void chat_erase_text(void) { + chat_text[0] = '\0'; + chat_textpos = 0; +} + +static void chat_overlay_start(void) { + chat_overlay_on = 1; +} + +static void chat_overlay_stop(void) { + chat_overlay_on = 0; +} + +void chat_overlay_init(Font *font) { + chat_font = font; +} + +void chat_set_sender(void (*new_sender)(const char *)) { + chat_sender = new_sender; +} + +void chat_draw_overlay(void) { + int display_was_enabled; + + if (!chat_overlay_on) + return; + + if (chat_sender == NULL) { + chat_overlay_stop(); + return; + } + + // don't display this to the clients, if we are the server + display_was_enabled = network_display_enable(0); + + // FIXME draw a bitmap for the background (load in chat_overlay_init()) + fill_vircr(20, 50, 300, 80, 14); + chat_font->printf(30, 55, "Message for chat (Enter=done, Esc=exit):"); + chat_font->printf(30, 65, "%s_", chat_text); + + network_display_enable(display_was_enabled); +} + +static int chat_edit_key(int keysym) { + switch (keysym) { + case SDLK_RETURN: + chat_overlay_stop(); + if (chat_text[0] != '\0') + chat_send(chat_text); + chat_erase_text(); + return 1; + case SDLK_ESCAPE: + chat_overlay_stop(); + chat_erase_text(); + return 1; + case SDLK_F5: + chat_overlay_stop(); + return 1; + case SDLK_BACKSPACE: + if (chat_textpos > 0) { + chat_text[--chat_textpos] = '\0'; + } + return 1; + default: + if (printable_char(keysym)) { + if (chat_textpos < EDITLINELEN) { + chat_text[chat_textpos++] = keysym; + chat_text[chat_textpos] = '\0'; + } + return 1; + } + } + + return 0; +} + +// returns 1 if keysym was consumed by the chat code +int chat_input_key(int keysym) { + if (chat_sender == NULL) + return 0; + + if (chat_overlay_on) { + return chat_edit_key(keysym); + } else { + if (keysym == SDLK_F5) { + chat_overlay_start(); + return 1; + } else if (keysym == SDLK_F6) { + chat_send(chat_f6_message); + return 1; + } else if (keysym == SDLK_F7) { + chat_send(chat_f7_message); + return 1; + } else if (keysym == SDLK_F8) { + chat_send(chat_f8_message); + return 1; + } + } + return 0; +} + +int chat_overlay_is_on(void) { + return chat_overlay_on; +} diff --git a/src/io/chat.h b/src/io/chat.h new file mode 100644 index 0000000..1cdb83f --- /dev/null +++ b/src/io/chat.h @@ -0,0 +1,32 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +#ifndef CHAT_H +#define CHAT_H + +#include "gfx/font.h" + +void chat_overlay_init(Font *font); +void chat_set_sender(void (*new_sender)(const char *)); +void chat_draw_overlay(void); +int chat_input_key(int keysym); +int chat_overlay_is_on(void); + +#endif diff --git a/src/io/netclient.cpp b/src/io/netclient.cpp new file mode 100644 index 0000000..9632f60 --- /dev/null +++ b/src/io/netclient.cpp @@ -0,0 +1,816 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +/******************************************************************************* + + Purpose: + Client of networked game + +*******************************************************************************/ + +#include "triplane.h" +#include "util/wutil.h" +#include "io/network.h" +#include "io/chat.h" +#include "io/netclient.h" +#include "io/joystick.h" +#include "io/sdl_compat.h" +#include "io/video.h" +#include "gfx/bitmap.h" +#include "gfx/fades.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NETC_SEND_BUFFER_SIZE 512 +#define NETC_RECVC_BUFFER_SIZE 100*1024 +#define NETC_RECVU_BUFFER_SIZE 1024*1024 + +// must be >= 12+2400*200 and <= NETC_RECVU_BUFFER_SIZE +#define MAXIMUM_S_PACKET_LENGTH 512*1024 + +#define NETCINFO_LINES 8 + +static int8_t netc_controls_country = -1; + +static int netc_socket = -1; +static char netc_sendbuf[NETC_SEND_BUFFER_SIZE]; +static unsigned int netc_sendbufend = 0; +static char netc_recvcbuf[NETC_RECVC_BUFFER_SIZE]; +static unsigned int netc_recvcbufend = 0; +static char netc_recvubuf[NETC_RECVU_BUFFER_SIZE]; +static unsigned int netc_recvubufend = 0; +static int netc_game_mode = 0; +static uint32_t netc_last_endofframe = 0, netc_last_warn = 0; + +static z_stream netc_recvzs; + +static Bitmap *netc_bitmaps[65536]; + +static char netcinfo_texts[NETCINFO_LINES][400]; +static uint32_t netcinfo_times[NETCINFO_LINES]; +static int netcinfo_next = 0; + +static void netc_printf(const char *fmt, ...) { + char teksti[400]; + va_list arg; + + va_start(arg, fmt); + vsprintf(teksti, fmt, arg); + va_end(arg); + + printf("Net: %s\n", teksti); + + strcpy(netcinfo_texts[netcinfo_next], teksti); + netcinfo_times[netcinfo_next] = SDL_GetTicks(); + netcinfo_next = (netcinfo_next + 1) % NETCINFO_LINES; +} + +static void netc_print_texts(void) { + int x = 12, y = 7, linesep = 10; + int i; + uint32_t oldesttime; + + frost->printf(x, y, "Network game client"); + y += linesep; + + // don't display info older than 10 seconds + oldesttime = SDL_GetTicks() - 10000; + + i = (netcinfo_next + 1) % NETCINFO_LINES; + do { + if (netcinfo_times[i] != 0 && netcinfo_times[i] > oldesttime) { + frost->printf(x, y, "%s", netcinfo_texts[i]); + y += linesep; + } + i = (i + 1) % NETCINFO_LINES; + } while (i != (netcinfo_next + 1) % NETCINFO_LINES); +} + +static char *netc_printed_text(const char *header) { + static char result[(1+NETCINFO_LINES)*400]; + int i; + + strcpy(result, header); + + i = (netcinfo_next + 1) % NETCINFO_LINES; + do { + if (netcinfo_texts[i][0] != '\0') { + strcat(result, netcinfo_texts[i]); + strcat(result, "\n"); + } + i = (i + 1) % NETCINFO_LINES; + } while (i != (netcinfo_next + 1) % NETCINFO_LINES); + + return result; +} + +static void netc_clear_printed_text(void) { + int i; + for (i = 0; i < NETCINFO_LINES; i++) + netcinfo_texts[i][0] = '\0'; + netcinfo_next = 0; +} + +static void netcbitmap_init(void) { + memset(netc_bitmaps, 0, 65536 * sizeof(Bitmap *)); +} + +static void netcbitmap_free_all(void) { + int bid; + for (bid = 0; bid < 65536; bid++) + if (netc_bitmaps[bid] != NULL) + delete netc_bitmaps[bid]; + memset(netc_bitmaps, 0, 65536 * sizeof(Bitmap *)); +} + +static void netcbitmap_maybefree(uint16_t bitmapid) { + if (netc_bitmaps[bitmapid] != NULL) { + delete netc_bitmaps[bitmapid]; + netc_bitmaps[bitmapid] = NULL; + } +} + +static void netc_close(void) { + if (netc_socket >= 0) { + close(netc_socket); + netc_socket = -1; + } +} + +static int16_t read_from_net_s16(const char *data) { + int16_t val; + memcpy(&val, data, sizeof(int16_t)); + val = SDL_SwapBE16(val); + return val; +} + +static uint16_t read_from_net_16(const char *data) { + uint16_t val; + memcpy(&val, data, sizeof(uint16_t)); + val = SDL_SwapBE16(val); + return val; +} + +static void netc_send_packet(uint32_t length, uint8_t type, void *data) { + if (NETC_SEND_BUFFER_SIZE - netc_sendbufend < length) { + netc_printf("Error: send buffer full (connection stalled?)"); + netc_close(); + return; + } + + uint32_t length_c = SDL_SwapBE32(length); + memcpy(&netc_sendbuf[netc_sendbufend], &length_c, 4); + memcpy(&netc_sendbuf[netc_sendbufend + 4], &type, 1); + if (length > 5) + memcpy(&netc_sendbuf[netc_sendbufend + 5], data, length - 5); + + netc_sendbufend += length; +} + +static void netc_send_greeting(uint8_t clientversion, + const char *playername, + const char *password) { + char data[43]; + memset(data, 0, 43); + memcpy(data, &clientversion, 1); + strcpy(data + 1, playername); + strcpy(data + 22, password); + netc_send_packet(48, NET_PKTTYPE_C_GREETING, data); +} + +static void netc_send_quit(void) { + netc_send_packet(5, NET_PKTTYPE_C_QUIT, NULL); +} + +static void netc_send_pong(uint8_t pingid) { + netc_send_packet(6, NET_PKTTYPE_C_PONG, &pingid); +} + +static void netc_send_wantcontrols(uint8_t playernum) { + netc_send_packet(6, NET_PKTTYPE_C_WANTCONTROLS, &playernum); +} + +static void netc_send_disablecontrols() { + netc_send_packet(5, NET_PKTTYPE_C_DISABLECONTROLS, NULL); +} + +static void netc_send_setcontrols(uint8_t controls) { + netc_send_packet(6, NET_PKTTYPE_C_SETCONTROLS, &controls); +} + +static void netc_send_chatmsg(const char *msg) { + char data[71]; + memset(data, 0, 71); + strcpy(data, msg); + netc_send_packet(76, NET_PKTTYPE_C_CHATMSG, data); +} + +static void netc_recv_quit(void) { + netc_printf("Server quitting"); + netc_close(); +} + +static void netc_recv_videomode(uint8_t new_mode) { + // the palette is a dummy, it will be updated right after this packet + if (new_mode == 0) + init_vga("PALET5"); + else + init_vesa("PALET5"); +} + +static void netc_recv_setpal_range(uint8_t firstcolor, uint16_t n, + const char *palette) { + int i; + char pal[256][3]; + + for (i = 0; i < n; i++) { + pal[i][0] = *palette++ / 4; + pal[i][1] = *palette++ / 4; + pal[i][2] = *palette++ / 4; + } + + setpal_range(pal, firstcolor, n, 0); +} + +static void netc_recv_setpal_range_black(uint8_t firstcolor, uint16_t n) { + setpal_range(NULL, firstcolor, n, 0); +} + +static void netc_endframe(void) { + netc_print_texts(); + do_all(); +} + +static void netc_recv_endofframe(void) { + netc_endframe(); + netc_last_endofframe = SDL_GetTicks(); +} + +static void netc_recv_ping(uint8_t pingid) { + netc_send_pong(pingid); +} + +static void netc_recv_gamemode(uint8_t new_mode) { + netc_game_mode = new_mode; +} + +static void netc_recv_infomsg(const char *msg) { + netc_printf("%s", msg); +} + +static void netc_recv_chatmsg(const char *sender, const char *msg) { + netc_printf("<%s> %s", (sender[0] == '\0' ? "Host" : sender), msg); +} + +static void netc_recv_fillrect(uint16_t x, uint16_t y, + uint16_t w, uint16_t h, + uint8_t color) { + fillrect(x, y, w, h, color); +} + +static void netc_recv_bitmapdata(uint16_t bitmapid, + uint16_t width, uint16_t height, + uint8_t hastransparency, + char *new_image_data) { + netcbitmap_maybefree(bitmapid); + netc_bitmaps[bitmapid] = new Bitmap(width, height, + (unsigned char *) new_image_data, + "fromserver", + hastransparency, + 1); +} + +static void netc_recv_bitmapdel(uint16_t bitmapid) { + netcbitmap_maybefree(bitmapid); +} + +static void netc_recv_bitmapblitfs(uint16_t bitmapid) { + if (netc_bitmaps[bitmapid] != NULL) { + netc_bitmaps[bitmapid]->blit_fullscreen(); + } +} + +static void netc_recv_bitmapblit(uint16_t bitmapid, int16_t xx, int16_t yy) { + if (netc_bitmaps[bitmapid] != NULL) { + netc_bitmaps[bitmapid]->blit(xx, yy); + } +} + +static void netc_recv_bitmapblitclipped(uint16_t bitmapid, + int16_t xx, int16_t yy, + uint16_t rx, uint16_t ry, + uint16_t rx2, uint16_t ry2) { + if (netc_bitmaps[bitmapid] != NULL) { + netc_bitmaps[bitmapid]->blit(xx, yy, rx, ry, rx2, ry2); + } +} + +static void netc_recv_blittobitmap(uint16_t source_bitmapid, + uint16_t target_bitmapid, + int16_t xx, + int16_t yy) { + if (netc_bitmaps[source_bitmapid] != NULL && + netc_bitmaps[target_bitmapid] != NULL) { + netc_bitmaps[source_bitmapid]-> + blit_to_bitmap(netc_bitmaps[target_bitmapid], xx, yy); + } +} + +static void netc_recv_fade_out(uint8_t type) { + selected_fade_out(type); +} + +static void netc_recv_play_music(char *modname) { + sdl_play_music_named(modname); +} + +static void netc_recv_stop_music(void) { + sdl_stop_music(); +} + +static void netc_recv_play_sample(char *samplename, + uint8_t leftvol, uint8_t rightvol, + uint8_t looping) { + sdl_play_sample_named(samplename, leftvol, rightvol, looping); +} + +static void netc_recv_stop_all_samples(void) { + sdl_stop_all_samples(); +} + +// dispatcher to process an incoming packet from server +// returns 1 if it was processed successfully, 0 if not +static int netc_receive_packet(uint32_t length, uint8_t type, void *data) { + char *cdata = (char *)data; + + if (type == NET_PKTTYPE_QUIT && length == 5) { + netc_recv_quit(); + } else if (type == NET_PKTTYPE_VIDEOMODE && length == 6) { + uint8_t new_mode = *(uint8_t *)cdata; + if (new_mode > 1) + return 0; + netc_recv_videomode(new_mode); + } else if (type == NET_PKTTYPE_SETPAL_RANGE && length >= 8) { + uint8_t firstcolor = *(uint8_t *)cdata; + uint16_t n = read_from_net_16(cdata + 1); + if (n == 0 || firstcolor + n > 256 || length != 8+((unsigned int)n)*3) + return 0; + netc_recv_setpal_range(firstcolor, n, cdata + 3); + } else if (type == NET_PKTTYPE_SETPAL_RANGE_BLACK && length == 8) { + uint8_t firstcolor = *(uint8_t *)cdata; + uint16_t n = read_from_net_16(cdata + 1); + if (n == 0 || firstcolor + n > 256) + return 0; + netc_recv_setpal_range_black(firstcolor, n); + } else if (type == NET_PKTTYPE_ENDOFFRAME && length == 5) { + netc_recv_endofframe(); + } else if (type == NET_PKTTYPE_PING && length == 6) { + uint8_t pingid = *(uint8_t *)cdata; + netc_recv_ping(pingid); + } else if (type == NET_PKTTYPE_GAMEMODE && length == 6) { + uint8_t new_mode = *(uint8_t *)cdata; + if (new_mode > 1) + return 0; + netc_recv_gamemode(new_mode); + } else if (type == NET_PKTTYPE_INFOMSG && length == 76) { + if (!check_printable_string(cdata, 71)) + return 0; + netc_recv_infomsg(cdata); + } else if (type == NET_PKTTYPE_CHATMSG && length == 97) { + if (!check_strict_string(cdata, 21)) + return 0; + if (!check_printable_string(cdata + 21, 71)) + return 0; + netc_recv_chatmsg(cdata, cdata + 21); + } else if (type == NET_PKTTYPE_FILLRECT && length == 14) { + uint16_t x = read_from_net_16(cdata); + uint16_t y = read_from_net_16(cdata + 2); + uint16_t w = read_from_net_16(cdata + 4); + uint16_t h = read_from_net_16(cdata + 6); + uint8_t color = *(uint8_t *)(cdata + 8); + + if (w == 0 || h == 0 || + x + w > ((current_mode == VGA_MODE) ? 320 : 800) || + y + h > ((current_mode == VGA_MODE) ? 200 : 600)) + return 0; + netc_recv_fillrect(x, y, w, h, color); + } else if (type == NET_PKTTYPE_BITMAPDATA && length >= 12) { + uint16_t bitmapid = read_from_net_16(cdata); + uint16_t width = read_from_net_16(cdata + 2); + uint16_t height = read_from_net_16(cdata + 4); + uint8_t hastransparency = *(uint8_t *)(cdata + 6); + if (width > 2400 || height > 600 || hastransparency > 1 || + length != 12+((unsigned int)width)*((unsigned int)height)) + return 0; + netc_recv_bitmapdata(bitmapid, width, height, hastransparency, + cdata + 7); + } else if (type == NET_PKTTYPE_BITMAPDEL && length == 7) { + uint16_t bitmapid = read_from_net_16(cdata); + netc_recv_bitmapdel(bitmapid); + } else if (type == NET_PKTTYPE_BITMAPBLITFS && length == 7) { + uint16_t bitmapid = read_from_net_16(cdata); + netc_recv_bitmapblitfs(bitmapid); + } else if (type == NET_PKTTYPE_BITMAPBLIT && length == 11) { + uint16_t bitmapid = read_from_net_16(cdata); + int16_t xx = read_from_net_s16(cdata + 2); + int16_t yy = read_from_net_s16(cdata + 4); + if (xx < -4000 || xx > 4000 || yy < -4000 || yy > 4000) + return 0; + netc_recv_bitmapblit(bitmapid, xx, yy); + } else if (type == NET_PKTTYPE_BITMAPBLITCLIPPED && length == 19) { + uint16_t bitmapid = read_from_net_16(cdata); + int16_t xx = read_from_net_s16(cdata + 2); + int16_t yy = read_from_net_s16(cdata + 4); + uint16_t rx = read_from_net_16(cdata + 6); + uint16_t ry = read_from_net_16(cdata + 8); + uint16_t rx2 = read_from_net_16(cdata + 10); + uint16_t ry2 = read_from_net_16(cdata + 12); + if (xx < -4000 || xx > 4000 || yy < -4000 || yy > 4000 || + rx > rx2 || ry > ry2 || rx2 > 799 || ry2 > 599) + return 0; + netc_recv_bitmapblitclipped(bitmapid, xx, yy, rx, ry, rx2, ry2); + } else if (type == NET_PKTTYPE_BLITTOBITMAP && length == 13) { + uint16_t source_bitmapid = read_from_net_16(cdata); + uint16_t target_bitmapid = read_from_net_16(cdata + 2); + int16_t xx = read_from_net_s16(cdata + 4); + int16_t yy = read_from_net_s16(cdata + 6); + if (xx < -4000 || xx > 4000 || yy < -4000 || yy > 4000) + return 0; + netc_recv_blittobitmap(source_bitmapid, target_bitmapid, xx, yy); + } else if (type == NET_PKTTYPE_FADE_OUT && length == 6) { + uint8_t type = *(uint8_t *)cdata; + if (type > 4) + return 0; + netc_recv_fade_out(type); + } else if (type == NET_PKTTYPE_PLAY_MUSIC && length == 12) { + if (!check_strict_string(cdata, 7)) + return 0; + netc_recv_play_music(cdata); + } else if (type == NET_PKTTYPE_STOP_MUSIC && length == 5) { + netc_recv_stop_music(); + } else if (type == NET_PKTTYPE_PLAY_SAMPLE && length == 15) { + if (!check_strict_string(cdata, 7)) + return 0; + uint8_t leftvol = *(uint8_t *)(cdata + 7); + uint8_t rightvol = *(uint8_t *)(cdata + 8); + uint8_t looping = *(uint8_t *)(cdata + 9); + if (leftvol > 32 || rightvol > 32 || looping > 1) + return 0; + netc_recv_play_sample(cdata, leftvol, rightvol, looping); + } else if (type == NET_PKTTYPE_STOP_ALL_SAMPLES && length == 5) { + netc_recv_stop_all_samples(); + } else { + return 0; + } + + return 1; // ok +} + +/* process possible incoming data in netc_recvubuf */ +static void netc_maybe_receive(void) { + uint32_t length; + uint8_t type; + char *data = netc_recvubuf; + uint32_t left = netc_recvubufend; + + while (left >= 5) { /* 5 bytes is the minimum packet size */ + memcpy(&length, data, 4); + length = SDL_SwapBE32(length); + if (length < 5 || length > MAXIMUM_S_PACKET_LENGTH) { + netc_printf("Error: invalid data from server (packet length %u)", + length); + netc_close(); + return; + } + if (left < length) + break; /* not a full packet */ + + memcpy(&type, &data[4], 1); + + if (!netc_receive_packet(length, type, &data[5])) { + netc_printf("Unknown data from server (type %u length %u)", + type, length); + return; + } + if (netc_socket == -1) + return; + + data += length; + left -= length; + } + + if (left == 0) { + netc_recvubufend = 0; + } else { + memmove(netc_recvubuf, data, left); + netc_recvubufend = left; + } +} + +static void netc_uncompress_init(void) { + int r; + + netc_recvzs.next_in = Z_NULL; + netc_recvzs.avail_in = 0; + netc_recvzs.next_out = Z_NULL; + netc_recvzs.avail_out = 0; + netc_recvzs.zalloc = Z_NULL; + netc_recvzs.zfree = Z_NULL; + netc_recvzs.opaque = Z_NULL; + + r = inflateInit(&netc_recvzs); + assert(r == Z_OK); +} + +static void netc_uncompress_deinit(void) { + inflateEnd(&netc_recvzs); +} + +/* + * Uncompresses the data in netc_recvcbuf into netc_recvubuf and calls + * netc_maybe_receive() for any new uncompressed data. + */ +static void netc_uncompress_and_receive(void) { + int r; + + do { + netc_recvzs.next_in = (Bytef *) netc_recvcbuf; + netc_recvzs.avail_in = netc_recvcbufend; + netc_recvzs.next_out = (Bytef *) &netc_recvubuf[netc_recvubufend]; + netc_recvzs.avail_out = NETC_RECVU_BUFFER_SIZE - netc_recvubufend; + + r = inflate(&netc_recvzs, Z_SYNC_FLUSH); + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + netc_printf("Error %d uncompressing data from server", r); + netc_close(); + return; + } + + if (netc_recvzs.avail_in == 0) { + netc_recvcbufend = 0; + } else { + memmove(netc_recvcbuf, + netc_recvzs.next_in, + netc_recvzs.avail_in); + netc_recvcbufend = netc_recvzs.avail_in; + } + + netc_recvubufend = NETC_RECVU_BUFFER_SIZE - netc_recvzs.avail_out; + + netc_maybe_receive(); + + if (r == Z_STREAM_END) { + inflateReset(&netc_recvzs); + } + } while (r == Z_STREAM_END || (r == Z_OK && netc_recvzs.avail_out == 0)); +} + +static int netc_connect(const char *host, int port) { + struct sockaddr_in sin; + struct hostent *he; + + netc_sendbufend = 0; + netc_recvubufend = 0; + netc_recvcbufend = 0; + + netc_printf("Connecting to %s port %d", host, port); + + he = gethostbyname(host); + if (he == NULL) { + netc_printf("gethostbyname: %s", hstrerror(h_errno)); + return 0; + } + + netc_socket = socket(PF_INET, SOCK_STREAM, 0); + if (netc_socket < 0) { + perror("socket"); + exit(1); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr = *(struct in_addr *)he->h_addr; + + if (connect(netc_socket, + (struct sockaddr *)&sin, + sizeof(struct sockaddr_in)) < 0) { + netc_printf("connect: %s", strerror(errno)); + close(netc_socket); + netc_socket = -1; + return 0; + } + + netc_printf("Connected to server"); + return 1; +} + +static void netc_doselect(void) { + fd_set readfds, writefds, exceptfds; + struct timeval timeout; + + assert(netc_socket >= 0); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(netc_socket, &readfds); + FD_SET(netc_socket, &exceptfds); + if (netc_sendbufend > 0) + FD_SET(netc_socket, &writefds); + + timeout.tv_sec = 0; + timeout.tv_usec = 20000; // 1/50 seconds (less than half a frame) + + if (select(netc_socket + 1, + &readfds, &writefds, &exceptfds, + &timeout) < 0) { + perror("select"); + exit(1); + } + + if (FD_ISSET(netc_socket, &exceptfds)) { + netc_printf("Server connection exception"); + netc_close(); + return; + } + + if (FD_ISSET(netc_socket, &readfds)) { + int r = read(netc_socket, + &netc_recvcbuf[netc_recvcbufend], + NETC_RECVC_BUFFER_SIZE - netc_recvcbufend); + if (r < 0) { + netc_printf("Read error from server: %s", strerror(errno)); + netc_close(); + return; + } else if (r == 0) { /* EOF */ + netc_printf("EOF from server"); + netc_close(); + return; + } else { + netc_recvcbufend += r; + netc_uncompress_and_receive(); + if (netc_socket == -1) + return; + } + } + + if (FD_ISSET(netc_socket, &writefds) && netc_sendbufend > 0) { + int w = write(netc_socket, netc_sendbuf, netc_sendbufend); + if (w < 0) { + netc_printf("Write error to server: %s", strerror(errno)); + netc_close(); + return; + } else if (w == 0) { + ; /* will retry later */ + } else if (((unsigned int)w) < netc_sendbufend) { + memmove(netc_sendbuf, &netc_sendbuf[w], netc_sendbufend - w); + netc_sendbufend -= w; + } else { + netc_sendbufend = 0; + } + } +} + +static void netc_controls(void) { + uint8_t controls; + int down, up, roll, guns, bombs; + static int power = 0; // stored for on/off power + static uint8_t last_sent_controls = 255; + + if (netc_game_mode != 1) { + last_sent_controls = 255; + return; + } + + if (netc_controls_country >= 0) { + get_controls_for_player(netc_controls_country, + &down, &up, &power, + &roll, &guns, &bombs); + + controls = 0; + if (down) controls |= 1; + if (up) controls |= 2; + if (power) controls |= 4; + if (roll) controls |= 8; + if (guns) controls |= 16; + if (bombs) controls |= 32; + + if (controls != last_sent_controls) { + netc_send_setcontrols(controls); + last_sent_controls = controls; + } + } +} + +/* + * Activates or deactivates (countrynum==-1) controls for countrynum, + * using solo controls of player rosternum. + */ +void netclient_activate_controls(int countrynum, int rosternum) { + if (countrynum >= 0) { + set_keys_from_roster(countrynum, rosternum); + if (netc_socket != -1 && netc_controls_country != countrynum) + netc_send_wantcontrols(countrynum); + netc_controls_country = countrynum; + } else { + if (netc_socket != -1 && netc_controls_country >= 0) + netc_send_disablecontrols(); + netc_controls_country = -1; + } +} + +static void netclient_chat_send(const char *msg) { + netc_send_chatmsg(msg); +} + +void netclient_loop(const char *host, int port, + const char *playername, const char *password) { + int client_exit = 0; + + netc_game_mode = 0; + netc_clear_printed_text(); + netcbitmap_init(); + netc_uncompress_init(); + + if (!netc_connect(host, port)) + goto netclient_loop_end; + + netc_send_greeting(1, playername, password); + + if (netc_controls_country >= 0) + netc_send_wantcontrols(netc_controls_country); + + chat_set_sender(netclient_chat_send); + + netc_last_endofframe = SDL_GetTicks(); + + while (netc_socket != -1) { + netc_doselect(); + if (netc_socket == -1) + break; + if (netc_last_endofframe + 2000 < SDL_GetTicks()) { + if (netc_last_warn + 3000 < SDL_GetTicks()) { + netc_printf("No data from server for %d seconds", + (SDL_GetTicks() - netc_last_endofframe)/1000); + netc_last_warn = SDL_GetTicks(); + } + netc_endframe(); // draw things on screen + } + + update_key_state(); + if (key && key[SDL_SCANCODE_ESCAPE]) { + client_exit = 1; + wait_relase(); + break; + } + netc_controls(); + } + + chat_set_sender(NULL); + + if (netc_socket != -1) { + netc_printf("Client quitting"); + netc_send_quit(); + netc_doselect(); + netc_close(); + } + + netclient_loop_end: + netc_uncompress_deinit(); + netcbitmap_free_all(); + + if (!client_exit) { + small_warning(netc_printed_text("Network game client:\n")); + } +} diff --git a/src/io/netclient.h b/src/io/netclient.h new file mode 100644 index 0000000..a494839 --- /dev/null +++ b/src/io/netclient.h @@ -0,0 +1,28 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +#ifndef NETCLIENT_H +#define NETCLIENT_H + +void netclient_activate_controls(int countrynum, int rosternum); +void netclient_loop(const char *host, int port, + const char *playername, const char *password); + +#endif diff --git a/src/io/network.cpp b/src/io/network.cpp new file mode 100644 index 0000000..9807f8f --- /dev/null +++ b/src/io/network.cpp @@ -0,0 +1,1231 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +/******************************************************************************* + + Purpose: + Networked game server + +*******************************************************************************/ + +#include "util/wutil.h" +#include "io/network.h" +#include "io/chat.h" +#include "io/video.h" +#include "gfx/bitmap.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// maximum number of simultaneous (accept()ed) connections +#define MAX_CONNECTIONS 20 +// buffer sizes for sending +// uncompressed: needs to fit at least a maximum-sized packet +#define SENDU_BUFFER_SIZE 512*1024 +// compressed: needs to at least fit all data of a single frame +// this is allocated for every connection +#define SENDC_BUFFER_SIZE 4*1024*1024 +// receive buffer size, allocated for every connection +#define RECEIVE_BUFFER_SIZE 2048 +// maximum possible packet length from client to host +#define MAXIMUM_C_PACKET_LENGTH 80 +// max. number of lines of network information and chat to display on screen +#define NETINFO_LINES 15 + +static int network_host_active = 0, network_display_enabled = 1; +static int listen_socket = -1; +static char net_password[21]; +static int game_mode = 0, last_pingid = 0; +static uint32_t last_ping_end = 0; + +// values for clients[i].state +#define CS_UNUSED 0 +#define CS_CONNECTED 1 +#define CS_ACCEPTED 2 + +static struct clientdata { + uint8_t state; // values CS_* + // fields below are invalid if state == CS_UNUSED + int socket; + struct sockaddr_in addr; + uint32_t connecttime; + char rbuffer[RECEIVE_BUFFER_SIZE]; + int rbufend; + // fields below are invalid if state < CS_ACCEPTED + // FIXME make cbuffer a ring buffer? + char cbuffer[SENDC_BUFFER_SIZE]; + int cbufend; + z_stream zs; + char name[21]; + int last_pingid; + // which country the client has sent WANTCONTROLS for, -1 if none + int wanted_controls; +} clients[MAX_CONNECTIONS]; + +// which clientname is allowed to have the controls for each player if +// it wants to (empty string = host only) +static char controls_clientname[4][21]; +// which clientid currently has the controls +static int controls_active_for_client[4]; + +static uint8_t net_controls[4]; // the current controls (in bits 0-5) + +// uncompressed packets to be compressed and moved to client buffers +static char netsend_ubuffer[SENDU_BUFFER_SIZE]; +static int netsend_ubufend = 0; +static int netsend_target = -1; + +static char netinfo_texts[NETINFO_LINES][400]; +static uint32_t netinfo_times[NETINFO_LINES]; +static int netinfo_next = 0; +static Font *netinfo_font; + +/* + * Prints a message on screen in the host and possibly the clients + * to_clients: 0 = to host only, 1 = to host and all clients + * fmt, ...: printf()-style format string and arguments + * Please don't include newlines in fmt (this prints exactly one + * line). + */ +static void netinfo_printf(uint8_t to_clients, const char *fmt, ...) { + char teksti[400]; + va_list arg; + + va_start(arg, fmt); + vsprintf(teksti, fmt, arg); + va_end(arg); + + printf("Net: %s\n", teksti); + + if (to_clients) + netsend_infomsg(teksti); + + strcpy(netinfo_texts[netinfo_next], teksti); + netinfo_times[netinfo_next] = SDL_GetTicks(); + netinfo_next = (netinfo_next + 1) % NETINFO_LINES; +} + +void network_print_serverinfo(void) { + int x = 12, y = 7, linesep = 10; + int i, nclients = 0; + uint32_t oldesttime; + int display_was_enabled; + + if (!network_host_active) + return; + + // don't show this info in the client windows + display_was_enabled = network_display_enable(0); + + for (i = 0; i < MAX_CONNECTIONS; i++) + if (clients[i].state >= CS_ACCEPTED) + nclients++; + + netinfo_font->printf(x, y, "Network host active (%d client%s)", + nclients, nclients==1 ? "" : "s"); + y += linesep; + + // don't display info older than 10 seconds + oldesttime = SDL_GetTicks() - 10000; + + i = (netinfo_next + 1) % NETINFO_LINES; + do { + if (netinfo_times[i] != 0 && netinfo_times[i] > oldesttime) { + netinfo_font->printf(x, y, "%s", netinfo_texts[i]); + y += linesep; + } + i = (i + 1) % NETINFO_LINES; + } while (i != (netinfo_next + 1) % NETINFO_LINES); + + network_display_enable(display_was_enabled); +} + +static const char *inetaddr_str(const struct sockaddr_in *addr) { + static char buf[100]; + if (addr->sin_family != AF_INET) + return "?unknown address family?"; + sprintf(buf, "%s:%d", + inet_ntoa(addr->sin_addr), + ntohs(addr->sin_port)); + return buf; +} + +static const char *clientaddr_str(int clientid) { + return inetaddr_str(&clients[clientid].addr); +} + +/* Return id of (accepted) client having name, or -1 if none */ +static int clientid_of_name(const char *clientname) { + int i; + + for (i = 0; i < MAX_CONNECTIONS; i++) { + if (clients[i].state < CS_ACCEPTED) + continue; + if (strcmp(clients[i].name, clientname) == 0) + return i; + } + + return -1; +} + +static void compress_init(z_stream *zs) { + int r; + + zs->next_in = Z_NULL; + zs->avail_in = 0; + zs->next_out = Z_NULL; + zs->avail_out = 0; + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + + r = deflateInit(zs, 1); + assert(r == Z_OK); +} + +static void compress_deinit(z_stream *zs) { + deflateEnd(zs); +} + +static void client_close(int clientid) { + int i; + + netinfo_printf(0, "Closing connection to client #%d", clientid); + + close(clients[clientid].socket); + if (clients[clientid].state >= CS_ACCEPTED) + compress_deinit(&clients[clientid].zs); + clients[clientid].state = CS_UNUSED; + clients[clientid].socket = -1; + + for (i = 0; i < 4; i++) { + if (controls_active_for_client[i] == clientid) { + network_reallocate_controls(); + break; + } + } +} + +/* does not remove compressed data from netsend_ubuffer */ +static void do_compress(int clientid, int flushmode) { + struct clientdata *client = &clients[clientid]; + int r; + + if (client->state < CS_ACCEPTED) + return; + + client->zs.next_in = (Bytef *) netsend_ubuffer; + client->zs.avail_in = netsend_ubufend; + + client->zs.next_out = (Bytef *) + &client->cbuffer[client->cbufend]; + client->zs.avail_out = SENDC_BUFFER_SIZE - client->cbufend; + + r = deflate(&client->zs, flushmode); + assert(r == Z_OK || r == Z_BUF_ERROR || r == Z_STREAM_END); + + if (client->zs.avail_out == 0) { + // client cbuffer is full + netinfo_printf(0, "Error: %s did not keep up with sent data", + clients[clientid].name); + client_close(clientid); + return; + } + + client->cbufend = SENDC_BUFFER_SIZE - client->zs.avail_out; + + assert(client->zs.avail_in == 0); // all data was compressed + + if (flushmode == Z_FINISH) { + assert(r == Z_STREAM_END); + deflateReset(&client->zs); + } +} + +static void net_do_compress(int flushmode) { + if (netsend_target == -1) { + int i; + for (i = 0; i < MAX_CONNECTIONS; i++) + do_compress(i, flushmode); + } else { + do_compress(netsend_target, flushmode); + } + netsend_ubufend = 0; +} + +static void write_to_net(const void *data, int bytes) { + assert(bytes <= SENDU_BUFFER_SIZE); + + if (bytes > SENDU_BUFFER_SIZE - netsend_ubufend) + net_do_compress(Z_NO_FLUSH); + + assert(bytes <= SENDU_BUFFER_SIZE - netsend_ubufend); + memcpy(&netsend_ubuffer[netsend_ubufend], data, bytes); + netsend_ubufend += bytes; +} + +static void write_to_net_8(uint8_t value) { + write_to_net(&value, sizeof(uint8_t)); +} + +static void write_to_net_s16(int16_t value) { + int16_t be_value = SDL_SwapBE16(value); + write_to_net(&be_value, sizeof(int16_t)); +} + +static void write_to_net_16(uint16_t value) { + uint16_t be_value = SDL_SwapBE16(value); + write_to_net(&be_value, sizeof(uint16_t)); +} + +static void write_to_net_32(uint32_t value) { + uint32_t be_value = SDL_SwapBE32(value); + write_to_net(&be_value, sizeof(uint32_t)); +} + +static void write_to_net_fixedstring(const char *s, int length) { + char buf[300]; + assert(length <= 300); + memset(buf, 0, length); + strncpy(buf, s, length-1); + write_to_net(buf, length); +} + +static void write_to_net_hdr(uint32_t length, uint8_t type) { + write_to_net_32(length); + write_to_net_8(type); +} + +// changes target of future netsend_* commands +// newtarget = clientid or -1 = all +// returns old target +static int netsend_change_target(int newtarget) { + int oldtarget = netsend_target; + + if (newtarget == oldtarget) + return oldtarget; + + assert(newtarget >= -1 && newtarget < MAX_CONNECTIONS); + + net_do_compress(Z_PARTIAL_FLUSH); + assert(netsend_ubufend == 0); + + netsend_target = newtarget; + + return oldtarget; +} + +void netsend_videomode(char mode) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(6, NET_PKTTYPE_VIDEOMODE); + write_to_net_8(mode); +} + +// oper = 0: send as is +// oper = 1: reverse order and multiply by 4 +// oper = 2: multiply by 4 +void netsend_setpal_range(const char pal[][3], + int firstcolor, int n, int oper) { + int i; + + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(8+n*3, NET_PKTTYPE_SETPAL_RANGE); + write_to_net_8(firstcolor); + write_to_net_16(n); + + if (oper == 1) { + for (i = n - 1; i >= 0; i--) { + write_to_net_8(4 * pal[i][0]); + write_to_net_8(4 * pal[i][1]); + write_to_net_8(4 * pal[i][2]); + } + } else if (oper == 2) { + for (i = 0; i < n; i++) { + write_to_net_8(4 * pal[i][0]); + write_to_net_8(4 * pal[i][1]); + write_to_net_8(4 * pal[i][2]); + } + } else { + for (i = 0; i < n; i++) { + write_to_net_8(pal[i][0]); + write_to_net_8(pal[i][1]); + write_to_net_8(pal[i][2]); + } + } +} + +void netsend_setpal_range_black(int firstcolor, int n) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(8, NET_PKTTYPE_SETPAL_RANGE_BLACK); + write_to_net_8(firstcolor); + write_to_net_16(n); +} + +void netsend_endofframe(void) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(5, NET_PKTTYPE_ENDOFFRAME); + net_do_compress(Z_PARTIAL_FLUSH); +} + +void netsend_ping(int pingid) { + if (!network_host_active) + return; + + write_to_net_hdr(6, NET_PKTTYPE_PING); + write_to_net_8(pingid); + net_do_compress(Z_PARTIAL_FLUSH); +} + +void netsend_gamemode(char mode) { + if (!network_host_active) + return; + + write_to_net_hdr(6, NET_PKTTYPE_GAMEMODE); + write_to_net_8(mode); +} + +void netsend_infomsg(const char *msg) { + if (!network_host_active) + return; + + write_to_net_hdr(76, NET_PKTTYPE_INFOMSG); + write_to_net_fixedstring(msg, 71); +} + +void netsend_chatmsg(const char *sender, const char *msg) { + if (!network_host_active) + return; + + write_to_net_hdr(97, NET_PKTTYPE_CHATMSG); + write_to_net_fixedstring(sender, 21); + write_to_net_fixedstring(msg, 71); +} + +void netsend_fillrect(int x, int y, int w, int h, int c) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(14, NET_PKTTYPE_FILLRECT); + write_to_net_16(x); + write_to_net_16(y); + write_to_net_16(w); + write_to_net_16(h); + write_to_net_8(c); +} + +int netsend_bitmapdata(int bitmapid, + int width, int height, int hastransparency, + const unsigned char *image_data) { + if (!network_host_active || !network_display_enabled) + return 0; + + write_to_net_hdr(12+width*height, NET_PKTTYPE_BITMAPDATA); + write_to_net_16(bitmapid); + write_to_net_16(width); + write_to_net_16(height); + write_to_net_8(hastransparency ? 1 : 0); + write_to_net(image_data, width*height); + + return 1; +} + +void netsend_bitmapdel(int bitmapid) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(7, NET_PKTTYPE_BITMAPDEL); + write_to_net_16(bitmapid); +} + +void netsend_bitmapblitfs(int bitmapid) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(7, NET_PKTTYPE_BITMAPBLITFS); + write_to_net_16(bitmapid); +} + +void netsend_bitmapblit(int bitmapid, int xx, int yy) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(11, NET_PKTTYPE_BITMAPBLIT); + write_to_net_16(bitmapid); + write_to_net_s16(xx); + write_to_net_s16(yy); +} + +void netsend_bitmapblitclipped(int bitmapid, int xx, int yy, + int rx, int ry, int rx2, int ry2) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(19, NET_PKTTYPE_BITMAPBLITCLIPPED); + write_to_net_16(bitmapid); + write_to_net_s16(xx); + write_to_net_s16(yy); + write_to_net_16(rx); + write_to_net_16(ry); + write_to_net_16(rx2); + write_to_net_16(ry2); +} + +void netsend_blittobitmap(int source_bitmapid, int target_bitmapid, + int xx, int yy) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(13, NET_PKTTYPE_BLITTOBITMAP); + write_to_net_16(source_bitmapid); + write_to_net_16(target_bitmapid); + write_to_net_s16(xx); + write_to_net_s16(yy); +} + +void netsend_fade_out(int type) { + if (!network_host_active || !network_display_enabled) + return; + + write_to_net_hdr(6, NET_PKTTYPE_FADE_OUT); + write_to_net_8(type); +} + +void netsend_play_music(const char *modname) { + if (!network_host_active) + return; + + write_to_net_hdr(12, NET_PKTTYPE_PLAY_MUSIC); + write_to_net_fixedstring(modname, 7); +} + +void netsend_stop_music() { + if (!network_host_active) + return; + + write_to_net_hdr(5, NET_PKTTYPE_STOP_MUSIC); +} + +void netsend_play_sample(const char *samplename, int leftvol, + int rightvol, int looping) { + if (!network_host_active) + return; + + write_to_net_hdr(15, NET_PKTTYPE_PLAY_SAMPLE); + write_to_net_fixedstring(samplename, 7); + write_to_net_8(leftvol); + write_to_net_8(rightvol); + write_to_net_8(looping ? 1 : 0); +} + +void netsend_stop_all_samples(void) { + if (!network_host_active) + return; + + write_to_net_hdr(5, NET_PKTTYPE_STOP_ALL_SAMPLES); +} + +/* sends any initial packets to a newly accepted client */ +static void send_initial_data(int clientid) { + int oldtarget = netsend_change_target(clientid); + + netsend_gamemode(game_mode); + netsend_mode_and_curpal(); + all_bitmaps_resend_if_sent(); + /* FIXME send any currently playing sounds and music */ + + netsend_change_target(oldtarget); +} + +/* send chat message originating from host */ +static void network_chat_send(const char *msg) { + netinfo_printf(0, " %s", msg); + netsend_chatmsg("", msg); +} + +void network_activate_host(const char *listenaddr, + int port, + const char *password, + Font *info_font) { + struct sockaddr_in sin; + int i; + + strncpy(net_password, password, 20); + net_password[20] = 0; + + network_host_active = 1; + netsend_ubufend = 0; + last_pingid = 0; + game_mode = 0; + netinfo_next = 0; + netinfo_font = info_font; + + for (i = 0; i < MAX_CONNECTIONS; i++) { + clients[i].state = CS_UNUSED; + clients[i].socket = -1; + } + + for (i = 0; i < NETINFO_LINES; i++) { + netinfo_times[i] = 0; + } + + for (i = 0; i < 4; i++) { + /* + * The default is to allow no controls for network players + * (otherwise we might need to check config.player_type to see + * which players exist). + */ + controls_clientname[i][0] = '\0'; + controls_active_for_client[i] = -1; + } + + listen_socket = socket(PF_INET, SOCK_STREAM, 0); + if (listen_socket < 0) { + perror("socket"); + exit(1); + } + + int val = 1; + setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)); + /* don't care if this failed */ + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (listenaddr[0] != '\0') + inet_aton(listenaddr, &sin.sin_addr); + sin.sin_port = htons(port); + + if (bind(listen_socket, (sockaddr *)&sin, sizeof(sin)) < 0) { + perror("bind"); + exit(1); + } + + if (listen(listen_socket, 5) < 0) { + perror("listen"); + exit(1); + } + + netinfo_printf(0, "Network host listening on TCP port %d", port); + + chat_set_sender(network_chat_send); +} + +static void net_recv_quit(int client) { + netinfo_printf(0, "%s has disconnected", clients[client].name); + client_close(client); +} + +static void net_recv_pong(int client, uint8_t pingid) { + clients[client].last_pingid = pingid; +} + +static void net_recv_want_controls(int client, uint8_t playernum) { + clients[client].wanted_controls = playernum; + network_reallocate_controls(); +} + +static void net_recv_disable_controls(int client) { + int i; + + clients[client].wanted_controls = -1; + + for (i = 0; i < 4; i++) { + if (controls_active_for_client[i] == client) { + network_reallocate_controls(); + break; + } + } +} + +static void net_recv_set_controls(int client, + uint8_t controls) { + int i; + + if (game_mode != 1) + return; /* and ignore the packet */ + + for (i = 0; i < 4; i++) { + if (controls_active_for_client[i] == client) { + net_controls[i] = controls; + break; + } + } + /* else just ignore the packet */ +} + +static void net_recv_chatmsg(int client, const char *msg) { + netinfo_printf(0, "<%s> %s", clients[client].name, msg); + netsend_chatmsg(clients[client].name, msg); +} + +/* + * Possibly sets the controls for playernum from the network. This + * function either leaves the arguments alone, or changes them to + * contain values from the network. + */ +void network_controls_for_player(int playernum, + int *down, int *up, + int *power, int *roll, + int *guns, int *bombs) { + if (!network_host_active) + return; + + if (controls_active_for_client[playernum] == -1) + return; /* no network controls for this player */ + + uint8_t controls = net_controls[playernum]; + *down = (controls & 1) ? 1 : 0; + *up = (controls & 2) ? 1 : 0; + *power = (controls & 4) ? 1 : 0; + *roll = (controls & 8) ? 1 : 0; + *guns = (controls & 16) ? 1 : 0; + *bombs = (controls & 32) ? 1 : 0; +} + +/* dispatcher to process an incoming packet from client */ +static void net_receive_packet(int client, + uint32_t length, uint8_t type, void *data) { + /* + * Note that no endianness conversions are required, since all + * data except the length is 8-bit. + */ + if (clients[client].state == CS_CONNECTED) { /* has not greeted yet */ + if (type != NET_PKTTYPE_C_GREETING || + length != 48 || + ((uint8_t *)data)[0] != 1) { + netinfo_printf(0, "Error: invalid first packet from client #%d", client); + client_close(client); + return; + } + + char *c_password = &((char *)data)[22]; + c_password[20] = 0; + if (strcmp(c_password, net_password) != 0) { + netinfo_printf(0, "Error: invalid password from client #%d", + client); + client_close(client); + return; + } + + char *c_playername = &((char *)data)[1]; + c_playername[20] = 0; + if (!check_strict_string(c_playername, 21)) { + netinfo_printf(0, "Error: invalid player name from client #%d", + client); + client_close(client); + return; + } + if (clientid_of_name(c_playername) != -1) { + // c_playername is already in use! + netinfo_printf(0, "Error: name of client #%d (%s) is already in use", + client, c_playername); + client_close(client); + return; + } + strcpy(clients[client].name, c_playername); + + netinfo_printf(0, "New client #%d is %s", + client, clients[client].name); + + clients[client].cbufend = 0; + compress_init(&clients[client].zs); + clients[client].last_pingid = last_pingid; + clients[client].wanted_controls = -1; + clients[client].state = CS_ACCEPTED; + send_initial_data(client); + } else if (type == NET_PKTTYPE_C_QUIT) { + if (length != 5) + return; + net_recv_quit(client); + } else if (type == NET_PKTTYPE_C_PONG) { + if (length != 6) + return; + uint8_t pingid = ((uint8_t *)data)[0]; + net_recv_pong(client, pingid); + } else if (type == NET_PKTTYPE_C_WANTCONTROLS) { + if (length != 6) + return; + uint8_t playernum = ((uint8_t *)data)[0]; + if (playernum > 3) + return; + net_recv_want_controls(client, playernum); + } else if (type == NET_PKTTYPE_C_DISABLECONTROLS) { + if (length != 5) + return; + net_recv_disable_controls(client); + } else if (type == NET_PKTTYPE_C_SETCONTROLS) { + if (length != 6) + return; + uint8_t controls = *((uint8_t *)data); + controls &= 0x3f; /* only 6 lower bits are used */ + net_recv_set_controls(client, controls); + } else if (type == NET_PKTTYPE_C_CHATMSG) { + if (length != 76) + return; + char *msg = &((char *)data)[0]; + msg[50] = 0; + if (!check_printable_string(msg, 71)) { + netinfo_printf(0, "Error: invalid chat message from client #%d", + client); + client_close(client); + return; + } + net_recv_chatmsg(client, msg); + } else return; /* ignores unknown packet types */ +} + +/* process possible incoming data in clients[client].rbuffer */ +static void net_maybe_receive(int client) { + uint32_t length; + uint8_t type; + char *clientdata = clients[client].rbuffer; + uint32_t left = clients[client].rbufend; + + while (left >= 5) { /* 5 bytes is the minimum packet size */ + memcpy(&length, clientdata, 4); + length = SDL_SwapBE32(length); + if (length < 5 || length > MAXIMUM_C_PACKET_LENGTH) { + netinfo_printf(0, "Error: invalid data from client #%d (packet length %u)", + client, length); + client_close(client); + return; + } + if (left < length) + break; /* not a full packet */ + + memcpy(&type, &clientdata[4], 1); + + net_receive_packet(client, length, type, &clientdata[5]); + if (clients[client].state == CS_UNUSED) + return; + + clientdata += length; + left -= length; + } + + if (left == 0) { + clients[client].rbufend = 0; + } else { + memmove(clients[client].rbuffer, clientdata, left); + clients[client].rbufend = left; + } +} + +int network_display_enable(int enable) { // returns previous value + int oldval = network_display_enabled; + network_display_enabled = enable; + return oldval; +} + +/* + * Finds the color that clientname wishes to control, if any. + * Returns the color (0-3) or -1 if none. + */ +int network_find_preferred_color(const char *clientname) { + int i = clientid_of_name(clientname); + if (i == -1) + return -1; + else + return clients[i].wanted_controls; +} + +/* + * Finds the next or previous client that wants to control something. + * Finds a new clientname starting from clientname, and writes the + * result back into the variable. + * + * A choice can be selected by giving the name to + * network_set_allowed_controls. This function also cycles through the + * special case of an empty client name. + * + * If previous = 1, finds the previous one, otherwise finds the next + * one (in alphabetical order of client names). If the given + * clientname is empty, finds the last or first one instead. + * + * clientname should have space for at least 21 bytes. + */ +void network_find_next_controls(int previous, char *clientname) { + int i, clientid = -1; + + if (!network_host_active) { + clientname[0] = '\0'; + return; + } + + /* + * Find a client that wants to control something and has the + * next-smaller or next-larger clientname + */ + for (clientid = -1, i = 0; i < MAX_CONNECTIONS; i++) { + if (clients[i].state < CS_ACCEPTED) + continue; + if (clients[i].wanted_controls != -1 && + /* in the right direction from clientname */ + (clientname[0] == '\0' || + (previous ? + strcmp(clients[i].name, clientname) < 0 : + strcmp(clients[i].name, clientname) > 0)) && + (clientid == -1 || /* no existing candidate */ + /* closer to the existing candidate */ + (previous ? + strcmp(clients[i].name, clients[clientid].name) > 0 : + strcmp(clients[i].name, clients[clientid].name) < 0))) { + /* i is a new candidate */ + clientid = i; + } + } + + if (clientid == -1) { /* no next/previous client */ + clientname[0] = '\0'; + return; + } + + strcpy(clientname, clients[clientid].name); +} + +/* + * Sets which client is allowed to control playernum (0-3). + * clientname: client name to allow (NULL or "" = allow host only) + * + * Call network_reallocate_controls after this to have the current + * situation reflect the change! + */ +void network_set_allowed_controls(int playernum, const char *clientname) { + if (clientname == NULL || clientname[0] == '\0') + controls_clientname[playernum][0] = '\0'; + else + strcpy(controls_clientname[playernum], clientname); +} + +/* Stores current values of allowed controls for playernum into *_ret */ +void network_get_allowed_controls(int playernum, char *clientname_ret) { + strcpy(clientname_ret, controls_clientname[playernum]); +} + +/* Selects controlling players again using current "allowed" settings */ +void network_reallocate_controls(void) { + int i, shouldbe; + + if (!network_host_active) + return; + + for (i = 0; i < 4; i++) { + if (controls_clientname[i][0] == '\0') + shouldbe = -1; + else + shouldbe = clientid_of_name(controls_clientname[i]); + if (shouldbe != -1 && clients[shouldbe].wanted_controls == -1) + shouldbe = -1; + if (controls_active_for_client[i] != shouldbe) { + controls_active_for_client[i] = shouldbe; + // FIXME notify the players of the change? + } + } +} + +/* Returns a string describing who controls playernum */ +const char *network_controlling_player_string(int playernum) { + static char buf[100]; + + if (!network_host_active) + return "no network game"; + + if (controls_clientname[playernum][0] == '\0') { + return "played by host"; + } else if (controls_active_for_client[playernum] == -1) { + sprintf(buf, "%s should play", + controls_clientname[playernum]); + } else { + sprintf(buf, "%s plays", + clients[controls_active_for_client[playernum]].name); + } + return buf; +} + +void network_change_game_mode(int newmode) { + char buf[200]; + + game_mode = newmode; + + if (!network_host_active) + return; + + netsend_gamemode(newmode); + + if (newmode == 1) { + strcpy(buf, "Controls: R: "); + strcat(buf, network_controlling_player_string(0)); + strcat(buf, " B: "); + strcat(buf, network_controlling_player_string(1)); + netinfo_printf(1, "%s", buf); + strcpy(buf, "Controls: G: "); + strcat(buf, network_controlling_player_string(2)); + strcat(buf, " Y: "); + strcat(buf, network_controlling_player_string(3)); + netinfo_printf(1, "%s", buf); + } +} + +/* this is called periodically (once or twice every frame) */ +void network_update(void) { + fd_set readfds, writefds, exceptfds; + struct timeval timeout; + int i; + int maxfd = listen_socket; + int retryselect = 1, canretryselect = 1; + + if (!network_host_active) + return; + + net_do_compress(Z_NO_FLUSH); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + + if (listen_socket != -1) + FD_SET(listen_socket, &readfds); + + for (i = 0; i < MAX_CONNECTIONS; i++) { + if (clients[i].state > CS_UNUSED) { + if (maxfd < clients[i].socket) + maxfd = clients[i].socket; + FD_SET(clients[i].socket, &readfds); + FD_SET(clients[i].socket, &exceptfds); + if (clients[i].state >= CS_ACCEPTED && + clients[i].cbufend > 0) + FD_SET(clients[i].socket, &writefds); + } + } + + while (retryselect && canretryselect) { + /* + * FIXME It would be better to use the select timeout instead + * of SDL_Delay in nopeuskontrolli() when network_host_active. + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if (select(maxfd + 1, &readfds, &writefds, &exceptfds, &timeout) < 0) { + perror("select"); + exit(1); + } + + retryselect = 0; + + if (FD_ISSET(listen_socket, &readfds)) { + for (i = 0; i < MAX_CONNECTIONS; i++) + if (clients[i].state == CS_UNUSED) + break; + if (i == MAX_CONNECTIONS) { + struct sockaddr_in saddr; + socklen_t size = sizeof(struct sockaddr_in); + int sock = accept(listen_socket, + (struct sockaddr *)&saddr, + &size); + close(sock); + netinfo_printf(0, "Error: too many connections, rejecting %s", + inetaddr_str(&saddr)); + } else { + socklen_t size = sizeof(struct sockaddr_in); + assert(clients[i].state == CS_UNUSED); + clients[i].socket = accept(listen_socket, + (struct sockaddr *)&clients[i].addr, + &size); + if (clients[i].socket < 0) { + netinfo_printf(0, "Error when accepting new client: %s", + strerror(errno)); + clients[i].state = CS_UNUSED; + clients[i].socket = -1; + /* but don't quit */ + } else { + clients[i].state = CS_CONNECTED; + clients[i].connecttime = SDL_GetTicks(); + clients[i].rbufend = 0; + netinfo_printf(0, "New client #%d from %s", + i, clientaddr_str(i)); + } + } + } + + for (i = 0; i < MAX_CONNECTIONS; i++) { + if (clients[i].state > CS_UNUSED) { + if (FD_ISSET(clients[i].socket, &exceptfds)) { + netinfo_printf(0, "Exception from client #%d", i); + client_close(i); + canretryselect = 0; + continue; + } + + if (FD_ISSET(clients[i].socket, &readfds)) { + int r = read(clients[i].socket, + &clients[i].rbuffer[clients[i].rbufend], + RECEIVE_BUFFER_SIZE - clients[i].rbufend); + if (r == RECEIVE_BUFFER_SIZE - clients[i].rbufend) + retryselect = 1; + if (r < 0) { + netinfo_printf(0, "Read error from client #%d: %s", + i, strerror(errno)); + client_close(i); + canretryselect = 0; + continue; + } else if (r == 0) { /* EOF */ + netinfo_printf(0, "EOF from client #%d", i); + client_close(i); + canretryselect = 0; + continue; + } + clients[i].rbufend += r; + net_maybe_receive(i); + } + + if (FD_ISSET(clients[i].socket, &writefds) && + clients[i].state >= CS_ACCEPTED && + clients[i].cbufend > 0) { + + int w = write(clients[i].socket, + clients[i].cbuffer, + clients[i].cbufend); + if (w < 0) { + netinfo_printf(0, "Write error to client #%d: %s", + i, strerror(errno)); + client_close(i); + canretryselect = 0; + continue; + } else if (w < clients[i].cbufend) { + retryselect = 1; + if (w > 0) { + memmove(clients[i].cbuffer, + clients[i].cbuffer + w, + clients[i].cbufend - w); + clients[i].cbufend -= w; + } + } else { /* all sent */ + clients[i].cbufend = 0; + } + } + } + } + } +} + +/* prepare to quit */ +void network_quit(void) { + int i; + + if (!network_host_active) + return; + + chat_set_sender(NULL); + + netinfo_printf(1, "Preparing to quit, closing all connections"); + + write_to_net_hdr(5, NET_PKTTYPE_QUIT); + network_update(); + + if (listen_socket != -1) { + close(listen_socket); + listen_socket = -1; + } + + for (i = 0; i < MAX_CONNECTIONS; i++) + if (clients[i].state > CS_UNUSED) + client_close(i); + + network_host_active = 0; +} + +int network_is_active(void) { + return network_host_active; +} + +/* + * Pings all current clients. After this, network_last_ping_done() + * will return 0 until either all clients have replied or the given + * number of seconds has passed. + */ +void network_ping(int seconds) { + if (!network_host_active) + return; + + last_pingid++; + if (last_pingid > 255) + last_pingid = 0; + + netsend_ping(last_pingid); + + if (seconds > 0) + last_ping_end = SDL_GetTicks() + 1000*seconds; + else + last_ping_end = 0; +} + +int network_last_ping_done(void) { + int i; + + if (!network_host_active) + return 1; + + if (last_ping_end == 0) + return 1; + + uint32_t time = SDL_GetTicks(); + + if (time > last_ping_end) { + last_ping_end = 0; + + for (i = 0; i < MAX_CONNECTIONS; i++) + if (clients[i].state >= CS_ACCEPTED && + clients[i].last_pingid != last_pingid) + netinfo_printf(0, "%s did not reply to a ping in time", + clients[i].name); + + return 1; + } + + for (i = 0; i < MAX_CONNECTIONS; i++) + if (clients[i].state >= CS_ACCEPTED && + clients[i].last_pingid != last_pingid) + break; /* no reply yet */ + + if (i == MAX_CONNECTIONS) { /* all have replied */ + last_ping_end = 0; + return 1; + } + + return 0; +} diff --git a/src/io/network.h b/src/io/network.h new file mode 100644 index 0000000..063debb --- /dev/null +++ b/src/io/network.h @@ -0,0 +1,293 @@ +/* + * Triplane Classic - a side-scrolling dogfighting game. + * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy + * + * 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 3 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, see . + * + * tjt@users.sourceforge.net + */ + +#ifndef NETWORK_H +#define NETWORK_H + +#include "gfx/font.h" + +/* + * Network traffic is a stream of packets (not the underlying IP + * packets), each of which contains: + * 1. Length (uint32_t) + * 2. Type (uint8_t) + * 3. Data (length - 5 bytes) + * + * All numbers are in network byte order (i.e., big-endian), most of + * them unsigned. Below, char foo[N] denotes an ASCII string padded to + * the right with NULs to fill N bytes (the Nth byte must be NUL). + * + * All data sent from the server to the clients is compressed using + * zlib (the above-mentioned packets are in the decompressed data). + * Data sent from the clients to the server is not compressed. + * + * If the compressed data stream contains metadata that indicates the + * end of the stream (i.e., zlib's inflate() returns Z_STREAM_END), + * the client needs to restart the decompression. The server may use + * this to reset the compression (though currently it is never used). + * + * When sending NET_PKTTYPE_ENDOFFRAME and NET_PKTTYPE_PING packets, + * the server ensures that enough compressed data to be able to + * decompress the packet is sent immediately. The client does not need + * to care about this. + * + * The valid packets sent from the server to the clients are: + * + * type=NET_PKTTYPE_QUIT: the server is quitting now + * length=5 + * + * type=NET_PKTTYPE_VIDEOMODE: + * uint8_t new_mode VGA_MODE=0=320x200 SVGA_MODE=1=800x600 + * length=6 + * (always followed by a SETPAL_RANGE packet containing all colors + * and BITMAPDATA packets for all bitmaps) + * + * type=NET_PKTTYPE_SETPAL_RANGE: sets palette[firstcolor .. firstcolor+n-1] + * uint8_t firstcolor + * uint16_t n + * uint8_t palette[n][3] r,g,b values 0..255 + * length=8+n*3 + * + * type=NET_PKTTYPE_SETPAL_RANGE_BLACK: + * uint8_t firstcolor + * uint16_t n + * length=8 + * (sets palette[firstcolor .. firstcolor+n-1] to black) + * + * type=NET_PKTTYPE_ENDOFFRAME: end of frame (please display it now) + * length=5 + * + * type=NET_PKTTYPE_PING: should reply with C_PONG packet + * uint8_t pingid copy this into your reply + * length=6 + * (this is used to wait for all clients just before starting a game; + * if you don't reply, the server waits longer and displays a warning, + * but the game will work) + * + * type=NET_PKTTYPE_GAMEMODE: + * uint8_t new_mode 0=in menus now, 1=a game is in progress + * length=6 + * + * type=NET_PKTTYPE_INFOMSG: informational message from host + * char msg[71] only [a-zA-Z0-9] characters + * length=76 + * (should be displayed somewhere on screen for a while) + * + * type=NET_PKTTYPE_CHATMSG: received chat message + * char sender[21] client player name, or empty for host + * char msg[71] only [a-zA-Z0-9] characters + * length=97 + * (should be displayed somewhere on screen for a while) + * + * type=NET_PKTTYPE_FILLRECT: + * uint16_t x, y, w, h coordinates are always inside the screen + * uint8_t color + * length=14 + * + * type=NET_PKTTYPE_BITMAPDATA: new or changed bitmap data for bitmapid + * uint16_t bitmapid + * uint16_t width + * uint16_t height + * uint8_t hastransparency 0=no, 1=yes + * uint8_t new_image_data[width*height] row-major order + * length=12+width*height + * + * type=NET_PKTTYPE_BITMAPDEL: can delete bitmap with bitmapid now + * uint16_t bitmapid + * length=7 + * + * type=NET_PKTTYPE_BITMAPBLITFS: Bitmap::blit_fullscreen() + * uint16_t bitmapid + * length=7 + * + * type=NET_PKTTYPE_BITMAPBLIT: Bitmap::blit() with no clipping + * uint16_t bitmapid + * int16_t xx, yy + * length=11 + * + * type=NET_PKTTYPE_BITMAPBLITCLIPPED: Bitmap::blit() with a clip rectangle + * uint16_t bitmapid + * int16_t xx, yy + * uint16_t rx, ry, rx2, ry2 + * length=19 + * + * type=NET_PKTTYPE_BLITTOBITMAP: Bitmap::blit_to_bitmap() + * uint16_t source_bitmapid + * uint16_t target_bitmapid + * int16_t xx, yy top-left position in target + * length=13 + * + * type=NET_PKTTYPE_FADE_OUT: do a screen fade effect + * uint8_t type 0-4 as in random_fade_out() + * length=6 + * (this packet can safely just be ignored) + * + * type=NET_PKTTYPE_PLAY_MUSIC: + * char modname[7] + * length=12 + * + * type=NET_PKTTYPE_STOP_MUSIC: + * length=5 + * + * type=NET_PKTTYPE_PLAY_SAMPLE: + * char samplename[7] + * uint8_t leftvol, rightvol both 0 to 32 + * uint8_t looping 0=no, 1=yes + * length=15 + * + * type=NET_PKTTYPE_STOP_ALL_SAMPLES: + * length=5 + * + * The valid packets sent from the clients to the server are: + * + * type=NET_PKTTYPE_C_GREETING: must be sent as the first packet + * uint8_t clientversion always 1 for now + * char playername[21] only [a-zA-Z0-9] characters are accepted + * char password[21] must be the one set by the server + * length=48 + * (the server starts sending game data after receiving this packet) + * + * type=NET_PKTTYPE_C_QUIT: this client will quit now + * length=5 + * (it is not mandatory to send this before closing the connection) + * + * type=NET_PKTTYPE_C_PONG: a reply to NET_PKTTYPE_PING + * uint8_t pingid same as in the ping + * length=6 + * + * type=NET_PKTTYPE_C_WANTCONTROLS: + * uint8_t playernum 0-3 + * length=6 + * (this client wants to set controls for playernum) + * FIXME there is currently no confirmation for this + * + * type=NET_PKTTYPE_C_DISABLECONTROLS: + * length=5 + * (this client no longer wants to set controls) + * + * type=NET_PKTTYPE_C_SETCONTROLS: + * uint8_t controls bits 6&7 = 0, bits 0-5 = the controls + * length=6 + * (set the player controls for the current frame; + * bits of controls: 0=down, 1=up, 2=power, 3=roll, 4=guns, 5=bombs; + * zero bit = not pressed, one bit = pressed; + * the client should send these packets only when the game mode set by + * NET_PKTTYPE_GAMEMODE is 1) + * + * type=NET_PKTTYPE_C_CHATMSG: public oneline chat message (shown to all) + * char msg[71] only [a-zA-Z0-9] characters are accepted + * length=76 + * + * A very simple dummy client for debugging and seeing how much data + * is sent (the echo command sends a C_GREETING packet using the default + * password): + * echo -ne "\0\0\0\x30\xc8\x01debugtestingxtesting\0triplane\0\0\0\0\0\0\0\0\0\0\0\0\0" | nc localhost 9763 | pv >/dev/null + * or to see some of the data: + * echo -ne "\0\0\0\x30\xc8\x01debugtestingxtesting\0triplane\0\0\0\0\0\0\0\0\0\0\0\0\0" | nc localhost 9763 | perl -e 'use IO::Uncompress::Inflate qw(inflate); inflate("-" => "-") while !eof();' | hd | head -100 + */ + +#define NET_PKTTYPE_QUIT 1 +#define NET_PKTTYPE_VIDEOMODE 2 +#define NET_PKTTYPE_SETPAL_RANGE 3 +#define NET_PKTTYPE_SETPAL_RANGE_BLACK 4 +#define NET_PKTTYPE_ENDOFFRAME 5 +#define NET_PKTTYPE_PING 6 +#define NET_PKTTYPE_GAMEMODE 7 +#define NET_PKTTYPE_INFOMSG 8 +#define NET_PKTTYPE_CHATMSG 9 + +#define NET_PKTTYPE_FILLRECT 10 +#define NET_PKTTYPE_BITMAPDATA 11 +#define NET_PKTTYPE_BITMAPDEL 12 +#define NET_PKTTYPE_BITMAPBLITFS 13 +#define NET_PKTTYPE_BITMAPBLIT 14 +#define NET_PKTTYPE_BITMAPBLITCLIPPED 15 +#define NET_PKTTYPE_BLITTOBITMAP 16 + +#define NET_PKTTYPE_FADE_OUT 21 + +#define NET_PKTTYPE_PLAY_MUSIC 31 +#define NET_PKTTYPE_STOP_MUSIC 32 +#define NET_PKTTYPE_PLAY_SAMPLE 33 +#define NET_PKTTYPE_STOP_ALL_SAMPLES 34 + +#define NET_PKTTYPE_C_GREETING 200 +#define NET_PKTTYPE_C_QUIT 201 +#define NET_PKTTYPE_C_PONG 202 + +#define NET_PKTTYPE_C_WANTCONTROLS 211 +#define NET_PKTTYPE_C_DISABLECONTROLS 212 +#define NET_PKTTYPE_C_SETCONTROLS 213 + +#define NET_PKTTYPE_C_CHATMSG 220 + +void netsend_videomode(char new_mode); +void netsend_setpal_range(const char pal[][3], + int firstcolor, int n, int oper); +void netsend_setpal_range_black(int firstcolor, int n); +void netsend_endofframe(void); +void netsend_ping(int pingid); +void netsend_fillrect(int x, int y, int w, int h, int c); +void netsend_infomsg(const char *msg); +void netsend_chatmsg(const char *sender, const char *msg); +int netsend_bitmapdata(int bitmapid, + int width, int height, int hastransparency, + const unsigned char *image_data); +void netsend_bitmapdel(int bitmapid); +void netsend_bitmapblitfs(int bitmapid); +void netsend_bitmapblit(int bitmapid, int xx, int yy); +void netsend_bitmapblitclipped(int bitmapid, int xx, int yy, + int rx, int ry, int rx2, int ry2); +void netsend_blittobitmap(int source_bitmapid, int target_bitmapid, + int xx, int yy); + +void netsend_fade_out(int type); + +void netsend_play_music(const char *modname); +void netsend_stop_music(); +void netsend_play_sample(const char *samplename, int leftvol, + int rightvol, int looping); +void netsend_stop_all_samples(void); + +void network_controls_for_player(int playernum, + int *down, int *up, + int *power, int *roll, + int *guns, int *bombs); + +void network_print_serverinfo(void); +int network_display_enable(int enable); +int network_find_preferred_color(const char *clientname); +void network_find_next_controls(int previous, char *clientname); +void network_set_allowed_controls(int playernum, const char *clientname); +void network_get_allowed_controls(int playernum, char *clientname_ret); +void network_reallocate_controls(void); +const char *network_controlling_player_string(int playernum); +void network_change_game_mode(int newmode); +void network_activate_host(const char *listenaddr, + int port, + const char *password, + Font *info_font); +void network_update(void); +void network_quit(void); +int network_is_active(void); +void network_ping(int seconds); +int network_last_ping_done(void); + +#endif diff --git a/src/io/sdl_compat.cpp b/src/io/sdl_compat.cpp index f88718a..7075db8 100644 --- a/src/io/sdl_compat.cpp +++ b/src/io/sdl_compat.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include "io/network.h" #include "io/video.h" #ifdef HAVE_SDL_MIXER @@ -34,9 +36,18 @@ #include #include "sdl_compat.h" #include "io/dksfile.h" +#include "io/chat.h" #include "util/wutil.h" #include "io/timing.h" +#define MAX_LOADED_SAMPLES 50 +#define MAX_LOADED_MUSICS 10 + +static sb_sample *loaded_samples[MAX_LOADED_SAMPLES]; +static int next_free_sample = 0; +static sb_mod_file *loaded_musics[MAX_LOADED_MUSICS]; +static int next_free_music = 0; + const Uint8 *key = NULL; int key_size = 0; @@ -77,7 +88,8 @@ int getch(void) { s = toupper(s); } } - return s; + if (!chat_input_key(s)) + return s; } } @@ -88,7 +100,10 @@ void update_key_state(void) { while (getch() != 0) ; - key = SDL_GetKeyboardState(&key_size); + if (chat_overlay_is_on()) + key = NULL; + else + key = SDL_GetKeyboardState(&key_size); } void wait_relase(void) { @@ -98,6 +113,8 @@ void wait_relase(void) { nopeuskontrolli(); do_all(); update_key_state(); + if (!key) + continue; for (c = 0; c < key_size; c++) if (key[c]) @@ -159,6 +176,24 @@ void sdl_play_sample(sb_sample * sample, int looping) { } Mix_SetPanning(ch, l, r); + netsend_play_sample(sample->name, sample->left_volume, + sample->right_volume, looping); +#endif +} + +void sdl_play_sample_named(const char *samplename, + int leftvol, int rightvol, int looping) { +#ifdef HAVE_SDL_MIXER + int i; + + for (i = 0; i < next_free_sample; i++) { + if (strcmp(loaded_samples[i]->name, samplename) == 0) { + loaded_samples[i]->left_volume = leftvol; + loaded_samples[i]->right_volume = rightvol; + sdl_play_sample(loaded_samples[i], looping); + break; + } + } #endif } @@ -166,6 +201,7 @@ void sdl_play_sample(sb_sample * sample, int looping) { void sdl_stop_all_samples(void) { #ifdef HAVE_SDL_MIXER Mix_HaltChannel(-1); + netsend_stop_all_samples(); #endif } @@ -194,6 +230,8 @@ sb_sample *sdl_sample_load(const char *name) { dksclose(); sample = (sb_sample *) walloc(sizeof(sb_sample)); + memset(sample->name, 0, 7); + strncpy(sample->name, name, 6); sample->chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(p, len), 1); if (sample->chunk == NULL) { @@ -203,6 +241,9 @@ sb_sample *sdl_sample_load(const char *name) { free(p); + assert(next_free_sample < MAX_LOADED_SAMPLES); + loaded_samples[next_free_sample++] = sample; + return sample; #else return NULL; @@ -215,8 +256,20 @@ sb_sample *sdl_sample_load(const char *name) { */ void sdl_free_sample(sb_sample * sample) { #ifdef HAVE_SDL_MIXER + int i; + Mix_FreeChunk(sample->chunk); free(sample); + + for (i = 0; i < next_free_sample; i++) { + if (loaded_samples[i] == sample) { + memmove(&loaded_samples[i], + &loaded_samples[i+1], + (next_free_sample - i - 1) * sizeof(sb_sample *)); + next_free_sample--; + break; + } + } #endif } @@ -241,6 +294,8 @@ sb_mod_file *sdl_load_mod_file(const char *name) { dksclose(); mod = (sb_mod_file *) walloc(sizeof(sb_mod_file)); + memset(mod->name, 0, 7); + strncpy(mod->name, name, 6); rwops = SDL_RWFromConstMem(p, len); mod->music = Mix_LoadMUSType_RW(rwops, MUS_MOD, SDL_TRUE); @@ -251,6 +306,9 @@ sb_mod_file *sdl_load_mod_file(const char *name) { free(p); + assert(next_free_music < MAX_LOADED_MUSICS); + loaded_musics[next_free_music++] = mod; + return mod; #else return NULL; @@ -259,8 +317,20 @@ sb_mod_file *sdl_load_mod_file(const char *name) { void sdl_free_mod_file(sb_mod_file * mod) { #ifdef HAVE_SDL_MIXER + int i; + Mix_FreeMusic(mod->music); free(mod); + + for (i = 0; i < next_free_music; i++) { + if (loaded_musics[i] == mod) { + memmove(&loaded_musics[i], + &loaded_musics[i+1], + (next_free_music - i - 1) * sizeof(sb_mod_file *)); + next_free_music--; + break; + } + } #endif } @@ -268,11 +338,26 @@ void sdl_play_music(sb_mod_file * mod) { #ifdef HAVE_SDL_MIXER sdl_stop_all_samples(); Mix_PlayMusic(mod->music, 1); + netsend_play_music(mod->name); +#endif +} + +void sdl_play_music_named(const char *modname) { +#ifdef HAVE_SDL_MIXER + int i; + + for (i = 0; i < next_free_music; i++) { + if (strcmp(loaded_musics[i]->name, modname) == 0) { + sdl_play_music(loaded_musics[i]); + break; + } + } #endif } void sdl_stop_music(void) { #ifdef HAVE_SDL_MIXER Mix_HaltMusic(); + netsend_stop_music(); #endif } diff --git a/src/io/sdl_compat.h b/src/io/sdl_compat.h index 5800539..0e607ad 100644 --- a/src/io/sdl_compat.h +++ b/src/io/sdl_compat.h @@ -33,6 +33,7 @@ #define SOUNDCARD_SDL 3 typedef struct { + char name[7]; int right_volume, left_volume; #ifdef HAVE_SDL_MIXER Mix_Chunk *chunk; @@ -40,6 +41,7 @@ typedef struct { } sb_sample; typedef struct { + char name[7]; #ifdef HAVE_SDL_MIXER Mix_Music *music; #endif @@ -56,6 +58,8 @@ void wait_relase(void); int sdl_init_sounds(void); void sdl_uninit_sounds(void); void sdl_play_sample(sb_sample * sample, int looping = 0); +void sdl_play_sample_named(const char *samplename, + int leftvol, int rightvol, int looping); void sdl_stop_all_samples(void); sb_sample *sdl_sample_load(const char *name); void sdl_free_sample(sb_sample * sample); @@ -63,6 +67,7 @@ void sdl_free_sample(sb_sample * sample); sb_mod_file *sdl_load_mod_file(const char *name); void sdl_free_mod_file(sb_mod_file * mod); void sdl_play_music(sb_mod_file * mod); +void sdl_play_music_named(const char *modname); void sdl_stop_music(void); #endif diff --git a/src/io/timing.cpp b/src/io/timing.cpp index cb3dffa..d7ef768 100644 --- a/src/io/timing.cpp +++ b/src/io/timing.cpp @@ -20,6 +20,7 @@ #include #include +#include "io/network.h" #include "sdl_compat.h" static int enabled = 1; @@ -29,6 +30,7 @@ void nopeuskontrolli(int fps) { uint32_t target_tick; if (!enabled) { + network_update(); return; } @@ -46,6 +48,8 @@ void nopeuskontrolli(int fps) { if (SDL_GetTicks() > viimeinen_aika + 1000 / fps) { viimeinen_aika = SDL_GetTicks(); } + + network_update(); } void nopeuskontrolli_enable(int enable) { diff --git a/src/io/video.cpp b/src/io/video.cpp index 6338ced..6b72cdc 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -20,6 +20,8 @@ #include "io/video.h" #include "io/dksfile.h" +#include "io/network.h" +#include "io/chat.h" #include "util/wutil.h" #include #include @@ -49,6 +51,11 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { SDL_Color *cc = (SDL_Color *) walloc(n * sizeof(SDL_Color)); int i, from = (reverse ? n - 1 : 0); + if (pal == NULL) + netsend_setpal_range_black(firstcolor, n); + else + netsend_setpal_range(pal, firstcolor, n, reverse ? 1 : 2); + for (i = 0; i < n; i++) { if (pal == NULL) { cc[i].r = cc[i].g = cc[i].b = 0; @@ -71,9 +78,26 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { all_bitmaps_refresh(); } +void netsend_mode_and_curpal(void) { + char pal[256][3]; + int i; + + netsend_videomode(current_mode); + + for (i = 0; i < 256; i++) { + pal[i][0] = curpal[i].r; + pal[i][1] = curpal[i].g; + pal[i][2] = curpal[i].b; + } + + netsend_setpal_range(pal, 0, 256, 0); +} + void fillrect(int x, int y, int w, int h, int c) { SDL_Rect r; + netsend_fillrect(x, y, w, h, c); + if (update_vircr_mode) { int screenw = (current_mode == VGA_MODE) ? 320 : 800; if (w == 1 && h == 1) { @@ -101,6 +125,10 @@ void fillrect(int x, int y, int w, int h, int c) { } void do_all(int do_retrace) { + // this code is called at the end of every displayed frame + network_print_serverinfo(); + chat_draw_overlay(); + if (draw_with_vircr_mode) { int w = (current_mode == VGA_MODE) ? 320 : 800; int wh = (current_mode == VGA_MODE) ? 320 * 200 : 800 * 600; @@ -128,6 +156,9 @@ void do_all(int do_retrace) { SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); SDL_RenderClear(video_state.renderer); } + + netsend_endofframe(); + network_update(); } static void sigint_handler(int dummy) { @@ -202,6 +233,7 @@ static int init_mode(int new_mode, const char *paletname) { dksclose(); + netsend_videomode(new_mode); setpal_range(ruutu.paletti, 0, 256); current_mode = new_mode; diff --git a/src/io/video.h b/src/io/video.h index 2a928f7..e7095de 100644 --- a/src/io/video.h +++ b/src/io/video.h @@ -47,6 +47,7 @@ extern SDL_Color curpal[256]; void alusta_naytto(const char *paletin_nimi); void setpal(int vari, char R, char G, char B); void setpal_range(const char pal[][3], int firstcolor, int n, int reverse = 0); +void netsend_mode_and_curpal(void); void fillrect(int x, int y, int w, int h, int c); void do_all(int do_retrace = 0); int init_vesa(const char *paletname); diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 846968c..9bb8212 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -27,8 +27,11 @@ #include #include #include +#include #include "io/joystick.h" #include "io/sdl_compat.h" +#include "io/network.h" +#include "io/netclient.h" #include "util/wutil.h" #include "world/plane.h" #include "world/tripaudio.h" @@ -2283,6 +2286,40 @@ void controls_menu(void) { } +/* Guess an appropriate roster entry for network client clientname */ +static void set_roster_from_clientname(int playernum, + const char *clientname) { + int i, l; + + if (clientname[0] == '\0') { + if (config.player_type[playernum] == 3) + config.player_number[playernum] = -2; + return; + } + + for (i = 0; i < MAX_PLAYERS_IN_ROSTER; i++) { + if (roster[i].pilotname[0] == '\0') + break; + + if (strcmp(clientname, roster[i].pilotname) == 0) { + /* clientname found at i; check that it is not already used */ + for (l = 0; l < 4; l++) { + if (l == playernum) + continue; + if (config.player_number[l] == i) + break; + } + if (l == 4) { + config.player_number[playernum] = i; + return; + } + } + } + + /* clientname was not found */ + if (config.player_type[playernum] == 3) + config.player_number[playernum] = -2; +} void assign_menu(void) { int exit_flag = 0; @@ -2297,18 +2334,22 @@ void assign_menu(void) { int lym[4] = { 0, 11, 24, 36 }; int response; int help_on = 0; + int plane_colors[4] = { 32, 144, 96, 71 }; + char clientname[4][21] = { "", "", "", "" }; + int clientcolor[4] = { -1, -1, -1, -1 }; menu_position positions[] = { + /* those with active=0 are for network_is_active() */ { 100, 15, 1 }, { 32, 48+lym[0], 1 }, { 185, 48+lym[0], 1 }, { 32, 48+lym[1], 1 }, { 185, 48+lym[1], 1 }, - { 32, 48+lym[2], 1 }, - { 185, 48+lym[2], 1 }, + { 32, 48+lym[2], 1 }, { 138, 48+lym[2], 0 }, { 147, 48+lym[2], 0 }, + { 185, 48+lym[2], 1 }, { 291, 48+lym[2], 0 }, { 300, 48+lym[2], 0 }, { 32, 48+lym[3], 1 }, { 138, 48+lym[3], 1 }, { 147, 48+lym[3], 1 }, { 185, 48+lym[3], 1 }, { 291, 48+lym[3], 1 }, { 300, 48+lym[3], 1 }, { 32, 121+lym[0], 1 }, { 185, 121+lym[0], 1 }, { 32, 121+lym[1], 1 }, { 185, 121+lym[1], 1 }, - { 32, 121+lym[2], 1 }, - { 185, 121+lym[2], 1 }, + { 32, 121+lym[2], 1 }, { 138, 121+lym[2], 0 }, { 147, 121+lym[2], 0 }, + { 185, 121+lym[2], 1 }, { 291, 121+lym[2], 0 }, { 300, 121+lym[2], 0 }, { 32, 121+lym[3], 1 }, { 138, 121+lym[3], 1 }, { 147, 121+lym[3], 1 }, { 185, 121+lym[3], 1 }, { 291, 121+lym[3], 1 }, { 300, 121+lym[3], 1 }, { 0, 0, -1 } }; @@ -2330,6 +2371,33 @@ void assign_menu(void) { ruksi = new Bitmap("RUKSI"); help = new Bitmap("HELP5"); + if (network_is_active()) { + for (l = 0; l < 4; l++) { + network_get_allowed_controls(l, clientname[l]); + clientcolor[l] = network_find_preferred_color(clientname[l]); + } + } + + if (network_is_active()) { + positions[6].active = 1; + positions[7].active = 1; + positions[9].active = 1; + positions[10].active = 1; + positions[22].active = 1; + positions[23].active = 1; + positions[25].active = 1; + positions[26].active = 1; + } else { + positions[6].active = 0; + positions[7].active = 0; + positions[9].active = 0; + positions[10].active = 0; + positions[22].active = 0; + positions[23].active = 0; + positions[25].active = 0; + positions[26].active = 0; + } + while (!exit_flag) { menu_keys(&exit_flag, &help_on); @@ -2381,6 +2449,38 @@ void assign_menu(void) { } } + if (network_is_active()) { + for (l = 0; l < 4; l++) { + ly = (l / 2) * 73; + lx = (l % 2) ? 153 : 0; + + if (config.player_type[l] != 1 && config.player_type[l] != 3) + continue; + + if (clientname[l][0] == '\0') { + frost->printf(46 + lx, 70 + ly, "Controlled by host only"); + } else { + frost->printf(46 + lx, 70 + ly, "Net: %s", clientname[l]); + if (clientcolor[l] >= 0 && clientcolor[l] <= 3) + fill_vircr(46 + lx + 16, 70 + ly + 1, + 46 + lx + 18, 70 + ly + 3, + plane_colors[clientcolor[l]]); + } + + if (x >= (134 + lx) && x < (143 + lx) && y >= (67 + ly) && y <= (77 + ly)) { + menusubselect1 = l; + menuselect = 5; + //frost->printf(82,177,"Select next network player"); + } + + if (x >= (143 + lx) && x <= (151 + lx) && y >= (67 + ly) && y <= (77 + ly)) { + menusubselect1 = l; + menuselect = 6; + //frost->printf(82,177,"Select previous network player"); + } + } + } + cursor->blit(x - 10, y - 10); if (help_on) help->blit(0, 0); @@ -2511,6 +2611,14 @@ void assign_menu(void) { config.player_number[menusubselect1] = l2; break; + case 5: case 6: + network_find_next_controls((menuselect == 6), + clientname[menusubselect1]); + clientcolor[menusubselect1] = + network_find_preferred_color(clientname[menusubselect1]); + set_roster_from_clientname(menusubselect1, + clientname[menusubselect1]); + break; } } @@ -2528,8 +2636,28 @@ void assign_menu(void) { config.player_type[l] = 0; config.player_number[l] = -1; } + if (network_is_active()) { + // check for and remove duplicate network players + if (config.player_type[l] == 3 && clientname[l][0] != '\0') { + for (l2 = l + 1; l2 < 4; l2++) { + if (config.player_type[l2] == 3 && + strcmp(clientname[l], clientname[l2]) == 0) { + config.player_type[l2] = 0; + config.player_number[l2] = -1; + } + } + } + + if (config.player_type[l] == 1 || config.player_type[l] == 3) + network_set_allowed_controls(l, clientname[l]); + else + network_set_allowed_controls(l, ""); + } } + if (network_is_active()) + network_reallocate_controls(); + delete acesme; delete ruksi; delete help; @@ -2979,6 +3107,362 @@ void letter_menu(void) { } } +void netgame_menu(void) { + int help_on = 0; + int exit_flag = 0; + int i, x, y, n1, n2, menuselect; + Bitmap *netmenu, *help, *right; + Bitmap *napp[4]; + Bitmap *controlme; + Bitmap *upnapp[4]; + Bitmap *assignme, *playerselect; + char str[100]; + menu_position positions[] = { + { 40, 43, 1 }, { 168, 43, 1 }, + { 82, 53, 1 }, { 210, 53, 1 }, + { 40, 73, 1 }, { 168, 73, 1 }, + { 37, 108, 1 }, { 37+1*18, 108, 1 }, + { 37+2*18, 108, 1 }, { 37+3*18, 108, 1 }, + { 114, 130, 0 /* (config.netc_controlplanes != 0) */ }, + { 122, 130, 0 /* (config.netc_controlplanes != 0) */ }, + { 40, 153, 1 }, + { 67, 174, 1 }, { 195, 174, 1 }, { 284, 180, 1 }, + { 0, 0, -1 } }; + + if (config.netc_solo_controls < 0 || + !roster[config.netc_solo_controls].pilotname[0]) + config.netc_solo_controls = 0; + + netmenu = new Bitmap("NETMEN"); + help = new Bitmap("HELP4"); // FIXME no help bitmap yet + right = new Bitmap("RIGHT"); + napp[0] = new Bitmap("NAPPRE"); + napp[1] = new Bitmap("NAPPBL"); + napp[2] = new Bitmap("NAPPGR"); + napp[3] = new Bitmap("NAPPYL"); + controlme = new Bitmap("NAPPIS"); + upnapp[0] = new Bitmap(11, 23, 12, 11, controlme); + upnapp[1] = new Bitmap(38, 23, 12, 11, controlme); + upnapp[2] = new Bitmap(11, 46, 12, 11, controlme); + upnapp[3] = new Bitmap(38, 46, 12, 11, controlme); + assignme = new Bitmap("ASSIGN"); + playerselect = new Bitmap(42, 79, 111, 11, assignme); + + while (!exit_flag) { + menu_keys(&exit_flag, &help_on); + + // can control only 0 or 1 planes + for (i = 0; i < 4; i++) { + if (config.netc_controlplanes & (1<blit(0, 0); + + frost->printf(20+4, 11, "NETWORK GAME CLIENT"); + + frost->printf(20, 30, "Address of host:"); + frost->printf(20+10, 40, "%s", config.netc_host); + frost->printf(20, 50, "Port number:"); + frost->printf(20+55, 50, "%d", config.netc_port); + // FIXME hide password? + frost->printf(20, 60, "Game password:"); + frost->printf(20+10, 70, "%s", config.netc_password); + + frost->printf(20, 80, "Which plane would you\n" + "like to control, if any?\n" + "(the host can change it)"); + for (i = 0; i < 4; i++) + if (config.netc_controlplanes & (1<blit(20+12 + i*18 - 1, 103 - 1); + else + upnapp[i]->blit(20+12 + i*18, 103); + + if (config.netc_controlplanes != 0) { + frost->printf(20, 118, "Use solo controls of:"); + playerselect->blit(16, 125); + if (roster[config.netc_solo_controls].pilotname[0]) + frost->printf(20, 128, "%s", + roster[config.netc_solo_controls].pilotname); + } + + frost->printf(20, 140, "Your player name:"); + frost->printf(20+10, 150, "%s", config.netc_playername); + + // FIXME draw the button in the bitmap? + frost->printf(20+5, 170, "[ START THE CLIENT ]"); + + frost->printf(148+4, 11, "HOST A NETWORK GAME"); + + frost->printf(148, 30, "Listen address:"); + frost->printf(148+10, 40, "%s", config.neth_listenaddr); + frost->printf(148, 50, "Port number:"); + frost->printf(148+55, 50, "%d", config.neth_listenport); + // FIXME hide password? + frost->printf(148, 60, "Game password:"); + frost->printf(148+10, 70, "%s", config.neth_password); + + // FIXME draw the button in the bitmap? + if (network_is_active()) { + frost->printf(148, 160, "The host is already active"); + frost->printf(148+5, 170, "[ DEACTIVATE HOST ]"); + } else { + frost->printf(148+3, 170, "[ ACTIVATE THE HOST ]"); + } + + if (help_on) + help->blit(0, 0); + + menuselect = 0; + + if (x >= 267 && x <= 301 && y >= 155 && y <= 190) { + menuselect = 1; + } + + if (x >= 20 && x <= 115 && y >= 168 && y <= 182) { + menuselect = 2; + } + + if (x >= 148 && x <= 243 && y >= 168 && y <= 182) { + menuselect = 3; + } + + if (x >= 20+12 && x < 20+12+4*18 && y >= 103 && y <= 103+15) { + menuselect = 10 + (x - 20 - 12) / 18; + } + + if (x >= 110 && x <= 125 && y >= 126 && y <= 126+7) { + menuselect = 15 + (x >= 118); + } + + if (x >= 20+10 && x <= 115 && y >= 35 && y <= 49) { + menuselect = 20; + } + + if (x >= 20+55 && x <= 115 && y >= 50 && y <= 62) { + menuselect = 21; + } + + if (x >= 20+10 && x <= 115 && y >= 65 && y <= 79) { + menuselect = 22; + } + + if (x >= 20+10 && x <= 115 && y >= 145 && y <= 159) { + menuselect = 23; + } + + if (x >= 148+10 && x <= 243 && y >= 35 && y <= 49) { + menuselect = 30; + } + + if (x >= 148+55 && x <= 243 && y >= 50 && y <= 62) { + menuselect = 31; + } + + if (x >= 148+10 && x <= 243 && y >= 65 && y <= 80) { + menuselect = 32; + } + + cursor->blit(x - 10, y - 10); + do_all(); + + if (n1 || n2) { + switch (menuselect) { + + case 1: // Exit menu + if (n1) + random_fade_out(); + else { + tyhjaa_vircr(); + do_all(); + } + exit_flag = 1; + break; + + case 2: // Start the client + // FIXME display a "Connecting" text instead of the fade? + if (n1) + random_fade_out(); + else { + tyhjaa_vircr(); + do_all(); + } + if (network_is_active()) { + small_warning("You cannot be both host and client\n" + "at the same time.\n" + "\n" + "Please deactivate the host first."); + break; + } + if (config.netc_controlplanes != 0 && + !roster[config.netc_solo_controls].pilotname[0]) { + small_warning("Your roster is empty, but you need a\n" + "player in the roster to set your keys.\n" + "\n" + "Please go to the roster menu and\n" + "create a player."); + break; + } + + + set_keys_none(); + if (config.netc_controlplanes == 0) { + netclient_activate_controls(-1, 0); + } else { + for (i = 0; i < 4; i++) { + if (config.netc_controlplanes & (1<= MAX_PLAYERS_IN_ROSTER || + !roster[config.netc_solo_controls].pilotname[0]) { + config.netc_solo_controls = 0; + } + if (roster[config.netc_solo_controls].pilotname[0] && + check_strict_string(roster[config.netc_solo_controls].pilotname, 21)) + strcpy(config.netc_playername, + roster[config.netc_solo_controls].pilotname); + wait_mouse_relase(); // so this does not repeat + break; + + case 16: // Solo controls down arrow + if (config.netc_solo_controls <= 0) { + for (config.netc_solo_controls = MAX_PLAYERS_IN_ROSTER - 1; + /* finally select 0 if no others are ok */ + config.netc_solo_controls > 0; + config.netc_solo_controls--) + if (roster[config.netc_solo_controls].pilotname[0]) + break; + } else { + config.netc_solo_controls--; + } + if (roster[config.netc_solo_controls].pilotname[0] && + check_strict_string(roster[config.netc_solo_controls].pilotname, 21)) + strcpy(config.netc_playername, + roster[config.netc_solo_controls].pilotname); + wait_mouse_relase(); // so this does not repeat + break; + + case 20: // Address of host (client) + netmenu->blit(0, 0, 20+10, 40, 145, 49); + frost->scanf(20+10, 40, config.netc_host, 79); + break; + + case 21: // Port number (client) + netmenu->blit(0, 0, 20+55, 50, 145, 59); + sprintf(str, "%d", config.netc_port); + frost->scanf(20+55, 50, str, 15); + config.netc_port = atoi(str); + if (config.netc_port < 1 || config.netc_port > 65535) + config.netc_port = 9763; + break; + + case 22: // Game password (client) + netmenu->blit(0, 0, 20+10, 70, 145, 79); + frost->scanf(20+10, 70, config.netc_password, 20); + if (config.netc_password[0] == '\0') + strcpy(config.netc_password, "triplane"); + break; + + case 23: // Player name (client) + netmenu->blit(0, 0, 20+10, 90, 145, 99); + frost->scanf(20+10, 150, config.netc_playername, 20); + if (config.netc_playername[0] == '\0' || + !check_strict_string(config.netc_playername, 21)) + strcpy(config.netc_playername, "netplayer"); + break; + + case 30: // Listen address (host) + netmenu->blit(0, 0, 148+10, 40, 255, 49); + frost->scanf(148+10, 40, config.neth_listenaddr, 79); + if (config.neth_listenaddr[0] == '\0') + strcpy(config.neth_listenaddr, "0.0.0.0"); + break; + + case 31: // Listen port (host) + netmenu->blit(0, 0, 148+55, 50, 255, 59); + sprintf(str, "%d", config.neth_listenport); + frost->scanf(148+55, 50, str, 15); + config.neth_listenport = atoi(str); + if (config.neth_listenport < 1 || config.neth_listenport > 65535) + config.neth_listenport = 9763; + break; + + case 32: // Game password (host) + netmenu->blit(0, 0, 148+10, 70, 255, 79); + frost->scanf(148+10, 70, config.neth_password, 20); + if (config.neth_password[0] == '\0') + strcpy(config.neth_password, "triplane"); + break; + + default: + break; + } + } + } + + for (i = 0; i < 4; i++) { + delete upnapp[i]; + delete napp[i]; + } + + delete playerselect; + delete assignme; + delete controlme; + delete help; + delete right; + delete netmenu; + + wait_mouse_relase(); +} + void main_menu(void) { int exit_flag = 0; int x, y, n1, n2; @@ -2989,8 +3473,8 @@ void main_menu(void) { menu_position positions[] = { { 51, 40, 1 }, { 103, 40, 1 }, { 156, 40, 1 }, { 214, 40, 1 }, - { 268, 40, 1 }, { 51, 77, 1 }, { 51, 120, 1 }, - { 254, 120, 1 }, { 268, 169, 1 }, { 0, 0, -1 } }; + { 268, 40, 1 }, { 51, 77, 1 }, { 51, 120, 1 }, { 254, 120, 1 }, + { 51, 164, 1 }, { 268, 169, 1 }, { 0, 0, -1 } }; help = new Bitmap("HELP1"); @@ -3008,7 +3492,9 @@ void main_menu(void) { menu_mouse(&x, &y, &n1, &n2, positions); menu1->blit(0, 0); // 0,0,799,599 - grid2->printf(34, 156, "Press F1\nfor Help"); + // FIXME write this in a nicer way in the menu1 bitmap data? + // FIXME fix this part of the help bitmap + grid2->printf(39, 157, "NETWORK\n GAME"); for (l = 0; l < 4; l++) if (config.player_type[l] == 1) @@ -3051,38 +3537,49 @@ void main_menu(void) { } - for (l = 0; l < 4; l++) { - if (config.player_type[l] == 3) { - frost->printf(110, 130 + l * 11, "%d. %s", - l + 1, - (config.player_number[l] == -2) - ? "Anonymous pilot" - : roster[config.player_number[l]].pilotname); - } - - if (config.player_type[l] == 2) { - frost->printf(110, 130 + l * 11, "%d. Computer pilot", l + 1); - + if (network_is_active()) { + for (l = 0; l < 4; l++) { + if (config.player_type[l] == 3) { + frost->printf(100, 121 + l * 15, "%d. %s", + l + 1, + (config.player_number[l] == -2) + ? "Anonymous pilot" + : roster[config.player_number[l]].pilotname); + frost->printf(109, 121 + l * 15 + 7, + "(%s)", + network_controlling_player_string(l)); + } else if (config.player_type[l] == 2) { + frost->printf(100, 121 + l * 15, + "%d. Computer pilot", l + 1); + } else if (config.player_type[l] == 0) { + frost->printf(100, 121 + l * 15, + "%d. Not active", l + 1); + } } - - if (config.player_type[l] == 0) { - frost->printf(110, 130 + l * 11, "%d. Not active", l + 1); - - + } else { + for (l = 0; l < 4; l++) { + if (config.player_type[l] == 3) { + frost->printf(110, 130 + l * 11, "%d. %s", + l + 1, + (config.player_number[l] == -2) + ? "Anonymous pilot" + : roster[config.player_number[l]].pilotname); + } else if (config.player_type[l] == 2) { + frost->printf(110, 130 + l * 11, "%d. Computer pilot", l + 1); + } else if (config.player_type[l] == 0) { + frost->printf(110, 130 + l * 11, "%d. Not active", l + 1); + } } - - - } - } - } else { frost->printf(100, 100, "Sologame active"); frost->printf(100, 110, "%s selected", plane_name[l]); frost->printf(110, 130, "%s flying", roster[config.player_number[l]].pilotname); - + if (network_is_active()) + frost->printf(110, 140, "(%s)", + network_controlling_player_string(l)); } if (help_on) @@ -3134,6 +3631,15 @@ void main_menu(void) { menuselect = 9; } + if (x >= 30 && x <= 73 && y >= 147 && y <= 182) { + frost->printf(245, 32, "Start/join a\nnetwork game"); + menuselect = 10; + } + + if (menuselect == 0) { + frost->printf(247, 32, "Press F1\n for Help"); + } + cursor->blit(x - 10, y - 10); @@ -3351,6 +3857,13 @@ void main_menu(void) { break; + case 10: + if (n1) + random_fade_out(); + wait_mouse_relase(); + netgame_menu(); + + break; } } diff --git a/src/menus/tripmenu.h b/src/menus/tripmenu.h index 6c722ad..9f49b3a 100644 --- a/src/menus/tripmenu.h +++ b/src/menus/tripmenu.h @@ -40,6 +40,7 @@ void options_menu(void); void controls_menu(void); int kangas_menu(void); int solo_player_menu(void); +void netgame_menu(void); void print_clear_roster(Bitmap * rosteri); void print_filled_roster(int number); diff --git a/src/settings.cpp b/src/settings.cpp index 9689924..4412af0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -372,6 +372,14 @@ void save_roster(void) { swap_roster_endianes(); } +static void clean_string(char *s, int len) { + int sl; + + s[len - 1] = '\0'; + sl = strlen(s); + memset(s + sl, 0, len - sl); +} + void swap_config_endianes(void) { int i; config.current_multilevel = SDL_SwapLE32(config.current_multilevel); @@ -421,6 +429,17 @@ void swap_config_endianes(void) { config.joystick[1] = SDL_SwapLE32(config.joystick[1]); config.joystick_calibrated[1] = SDL_SwapLE32(config.joystick_calibrated[1]); + + clean_string(config.netc_host, 80); + config.netc_port = SDL_SwapLE32(config.netc_port); + clean_string(config.netc_password, 40); + clean_string(config.netc_playername, 40); + config.netc_solo_controls = SDL_SwapLE32(config.netc_solo_controls); + config.netc_controlplanes = SDL_SwapLE32(config.netc_controlplanes); + + clean_string(config.neth_listenaddr, 80); + config.neth_listenport = SDL_SwapLE32(config.neth_listenport); + clean_string(config.neth_password, 40); } void load_config(void) { @@ -477,6 +496,21 @@ void load_config(void) { config.joystick[1] = -1; config.joystick_calibrated[1] = 0; + memset(config.netc_host, 0, 80); + config.netc_port = 9763; + memset(config.netc_password, 0, 40); + strcpy(config.netc_password, "triplane"); + memset(config.netc_playername, 0, 40); + strcpy(config.netc_playername, "netplayer"); + config.netc_solo_controls = 1; + config.netc_controlplanes = 0; + + memset(config.neth_listenaddr, 0, 80); + strcpy(config.neth_listenaddr, "0.0.0.0"); + config.neth_listenport = 9763; + memset(config.neth_password, 0, 40); + strcpy(config.neth_password, "triplane"); + faili = settings_open(CONFIGURATION_FILENAME, "rb"); if (faili != NULL) { diff --git a/src/settings.h b/src/settings.h index 2e508d7..f6eee81 100644 --- a/src/settings.h +++ b/src/settings.h @@ -156,7 +156,16 @@ struct configuration { int32_t joystick[2]; int32_t joystick_calibrated[2]; - + char netc_host[80]; + int32_t netc_port; + char netc_password[40]; + char netc_playername[40]; + int32_t netc_solo_controls; + int32_t netc_controlplanes; + + char neth_listenaddr[80]; + int32_t neth_listenport; + char neth_password[40]; }; extern configuration config; diff --git a/src/triplane.cpp b/src/triplane.cpp index adbf8b2..3b4c13d 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -26,6 +26,8 @@ #include "triplane.h" #include "io/joystick.h" +#include "io/netclient.h" +#include "io/chat.h" #include "gfx/gfx.h" #include "menus/menusupport.h" #include "menus/tripmenu.h" @@ -41,6 +43,7 @@ #include #include #include "io/trip_io.h" +#include "io/network.h" #include "io/sdl_compat.h" #include "settings.h" @@ -1056,6 +1059,8 @@ void get_controls_for_player(int player, *roll = 0; *guns = 0; } + + network_controls_for_player(player, down, up, power, roll, guns, bombs); } void controls(void) { @@ -1715,6 +1720,15 @@ void main_engine(void) { if (!draw_with_vircr_mode) update_vircr_mode = 0; + all_bitmaps_send_now(); + + network_ping(15); + while (!network_last_ping_done()) { + nopeuskontrolli(); + } + + network_change_game_mode(1); + while (flag) { update_key_state(); @@ -1926,6 +1940,8 @@ void main_engine(void) { } } + network_change_game_mode(0); + if (!draw_with_vircr_mode) update_vircr_mode = 1; @@ -2943,6 +2959,8 @@ void load_up(void) { loading_text("Loading mouse cursor."); cursor = new Bitmap("CURSOR"); + loading_text("Loading data for chat support."); + chat_overlay_init(frost); } @@ -3742,7 +3760,19 @@ int main(int argc, char *argv[]) { loading_text("\nData loading started."); load_up(); - + if (findparameter("-networkhost")) { + // frost was loaded in load_up() above + network_activate_host((findparameter_arg("-listenaddr") != NULL) + ? findparameter_arg("-listenaddr") + : config.neth_listenaddr, + (findparameter_arg("-listenport") != NULL) + ? atoi(findparameter_arg("-listenport")) + : config.neth_listenport, + (findparameter_arg("-netpassword") != NULL) + ? findparameter_arg("-netpassword") + : config.neth_password, + frost); + } if (loading_texts) { printf("\nLoading complete. Press a key to continue."); @@ -3780,6 +3810,53 @@ int main(int argc, char *argv[]) { wait_mouse_relase(); + if (findparameter_arg("-netclient") != NULL) { + const char *host = findparameter_arg("-netclient"); + int port = config.netc_port; + char playername[21]; + char password[21]; + + strncpy(playername, config.netc_playername, 20); + playername[20] = 0; + strncpy(password, config.netc_password, 20); + password[20] = 0; + + if (findparameter_arg("-port") != NULL) { + port = atoi(findparameter_arg("-port")); + } + if (findparameter_arg("-netplayer") != NULL) { + strncpy(playername, findparameter_arg("-netplayer"), 20); + playername[20] = 0; + } + if (findparameter_arg("-netpassword") != NULL) { + strncpy(password, findparameter_arg("-netpassword"), 20); + password[20] = 0; + } + + if (findparameter_arg("-netcontrol") != NULL) { + if (config.netc_solo_controls < 0 || + !roster[config.netc_solo_controls].pilotname[0]) + config.netc_solo_controls = 0; + if (!roster[config.netc_solo_controls].pilotname[0]) { + fprintf(stderr, "Please add a pilot to the roster for the -netcontol option!\n"); + } else { + if (strcmp(findparameter_arg("-netcontrol"), "r") == 0) + netclient_activate_controls(0, config.netc_solo_controls); + else if (strcmp(findparameter_arg("-netcontrol"), "b") == 0) + netclient_activate_controls(1, config.netc_solo_controls); + else if (strcmp(findparameter_arg("-netcontrol"), "g") == 0) + netclient_activate_controls(2, config.netc_solo_controls); + else if (strcmp(findparameter_arg("-netcontrol"), "y") == 0) + netclient_activate_controls(3, config.netc_solo_controls); + } + } + + netclient_loop(host, port, playername, password); + + clean_memory(); + return 0; + } + main_menu(); save_roster(); @@ -3826,6 +3903,8 @@ int main(int argc, char *argv[]) { save_config(); + network_quit(); + clean_memory(); return 0; diff --git a/src/util/wutil.cpp b/src/util/wutil.cpp index 5181e70..b96ff72 100644 --- a/src/util/wutil.cpp +++ b/src/util/wutil.cpp @@ -270,3 +270,28 @@ const char *findparameter_arg(const char *jono) { else return NULL; } + +// Is ch a normal printable character? +// (used to limit network player names and chat messages) +// should be a subset of the characters accepted by frost->scanf +int printable_char(int ch) { + return (ch >= 32 && ch <= 126); +} + +// Is s an alphanumeric string of length 0 to len-1? +int check_strict_string(const char *s, int len) { + int i; + for (i = 0; i < len && s[i] != '\0'; i++) + if (!isalnum(s[i])) + return 0; + return (i < len); +} + +// Is s a printable string of length 0 to len-1? +int check_printable_string(const char *s, int len) { + int i; + for (i = 0; i < len && s[i] != '\0'; i++) + if (!printable_char(s[i])) + return 0; + return (i < len); +} diff --git a/src/util/wutil.h b/src/util/wutil.h index 7d6570b..492fef7 100644 --- a/src/util/wutil.h +++ b/src/util/wutil.h @@ -44,4 +44,8 @@ void findparameter_init(int argc, char **argv); int findparameter(const char *jono); const char *findparameter_arg(const char *jono); +int printable_char(int ch); +int check_strict_string(const char *s, int len); +int check_printable_string(const char *s, int len); + #endif From ddb472cc1aa283c5dd894e57d8fd3e18558cbfaa Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 5 Apr 2014 19:13:28 +0300 Subject: [PATCH 29/54] Minor bugfixes to network game UI A nonexistent player could sometimes reserve a network game client for itself. When the network game menu asked for the client name, it did not clear the previous name from the screen. --- src/menus/tripmenu.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 9bb8212..4f9a9f6 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2549,7 +2549,14 @@ void assign_menu(void) { } } - + if (network_is_active()) { + for (l = 0; l < 4; l++) { + if (config.player_type[l] == 0 || config.player_type[l] == 2) { + clientname[l][0] = '\0'; + clientcolor[l] = -1; + } + } + } break; @@ -3412,7 +3419,7 @@ void netgame_menu(void) { break; case 23: // Player name (client) - netmenu->blit(0, 0, 20+10, 90, 145, 99); + netmenu->blit(0, 0, 20+10, 150, 145, 159); frost->scanf(20+10, 150, config.netc_playername, 20); if (config.netc_playername[0] == '\0' || !check_strict_string(config.netc_playername, 21)) From 84cd9e2a3c669d45bebcad0462d60300f0c0b6db Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 5 Apr 2014 19:19:46 +0300 Subject: [PATCH 30/54] Auto-allocate network game controls The Assign Players menu now tries to auto-allocate players for the network game based on their preferences. --- src/menus/tripmenu.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 4f9a9f6..ac55bef 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2337,6 +2337,9 @@ void assign_menu(void) { int plane_colors[4] = { 32, 144, 96, 71 }; char clientname[4][21] = { "", "", "", "" }; int clientcolor[4] = { -1, -1, -1, -1 }; + char tmpname[21] = ""; + char unallocated[200] = ""; + bool autoallocate[4] = { true, true, true, true }; menu_position positions[] = { /* those with active=0 are for network_is_active() */ { 100, 15, 1 }, @@ -2399,6 +2402,58 @@ void assign_menu(void) { } while (!exit_flag) { + if (network_is_active()) { + // try to auto-allocate controls + unallocated[0] = '\0'; + tmpname[0] = '\0'; + for (;;) { + network_find_next_controls(0, tmpname); + if (tmpname[0] == '\0') + break; // no more players wanting controls + for (l = 0; l < 4; l++) + if (strcmp(clientname[l], tmpname) == 0) + break; + if (l != 4) // tmpname already controls something + continue; + l = network_find_preferred_color(tmpname); + if (l == -1) + continue; // doesn't want controls (shouldn't happen) + if (!autoallocate[l]) + goto notallocated; + if (clientname[l][0] != '\0') + goto notallocated; // l is already network-controlled + // handle a solo player, if any + for (l2 = 0; l2 < 4; l2++) { + if (config.player_type[l2] == 1) { + if (l2 != l) { + if (!autoallocate[l2] || clientname[l2][0] != '\0') + // we should not change current solo player + goto notallocated; + config.player_type[l2] = 0; + config.player_number[l2] = -1; + } + // auto-allocate tmpname for solo country l + config.player_type[l] = 1; + goto allocate; + } + } + // we are not in solo mode + // auto-allocate tmpname for country l + config.player_type[l] = 3; + allocate: + strcpy(clientname[l], tmpname); + clientcolor[l] = l; + set_roster_from_clientname(l, clientname[l]); + continue; + notallocated: + l2 = strlen(unallocated); + snprintf(&unallocated[l2], 200-l2, "%s%s (%c)", + l2==0 ? "" : ", ", tmpname, + // FIXME add colored boxes to font to make this look nice? + "GFEJ"[l]); + } + } + menu_keys(&exit_flag, &help_on); menu_mouse(&x, &y, &n1, &n2, positions); @@ -2479,6 +2534,11 @@ void assign_menu(void) { //frost->printf(82,177,"Select previous network player"); } } + + if (unallocated[0] != '\0') { + frost->printf(30, 175, "Network players wanting controls:"); + frost->printf(45, 185, "%s", unallocated); + } } cursor->blit(x - 10, y - 10); @@ -2487,6 +2547,11 @@ void assign_menu(void) { do_all(); if ((n1 || n2) && menuselect) { + // disable automatic network player allocation for a + // country that has been touched manually + if (menuselect != 2 && menusubselect1 >= 0 && menusubselect1 < 4) + autoallocate[menusubselect1] = false; + switch (menuselect) { case 1: From 66477c30f7643a7fe80c377f100727de2835541b Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 6 Apr 2014 12:22:38 +0300 Subject: [PATCH 31/54] Minor UI improvement in netgame menu Initialize player name when solo controls selection first becomes visible. --- src/menus/tripmenu.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index ac55bef..f638703 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3424,6 +3424,14 @@ void netgame_menu(void) { break; case 10: case 11: case 12: case 13: // netc_controlplanes + if (config.netc_controlplanes == 0 && + strcmp(config.netc_playername, "netplayer") == 0 && + roster[config.netc_solo_controls].pilotname[0] && + check_strict_string(roster[config.netc_solo_controls].pilotname, 21)) + // Solo controls part becomes visible probably for + // the first time + strcpy(config.netc_playername, + roster[config.netc_solo_controls].pilotname); if (config.netc_controlplanes == 1 << (menuselect - 10)) config.netc_controlplanes = 0; else From 7aedeea3093e8d55fe9aba32e20c0df59cddff50 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 6 Apr 2014 12:37:14 +0300 Subject: [PATCH 32/54] Add big_warning function An image for a bigger warning dialog already existed, now we also have a function that uses it. --- src/triplane.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/triplane.h | 1 + 2 files changed, 48 insertions(+) diff --git a/src/triplane.cpp b/src/triplane.cpp index 3b4c13d..94c42c4 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -604,6 +604,53 @@ void init_sologame(void) { init_mission(solo_country, solo_mission); } +int big_warning(const char *message) { + Bitmap *warnkuva; + int flag = 1, exit_flag = 0; + int x, y, n1, n2; + int response = 0; + menu_position positions[] = { + { 61, 167, 1 }, { 259, 167, 1 }, { 0, 0, -1 } }; + + warnkuva = new Bitmap("WARN1"); + + wait_mouse_relase(); // ensure that exiting requires a click + + while (flag) { + menu_keys(&exit_flag, NULL); + menu_mouse(&x, &y, &n1, &n2, positions); + + if (exit_flag) { + flag = 0; + response = 0; + } + + tyhjaa_vircr(); + warnkuva->blit(37, 19); + frost->printf(41, 37, message); + cursor->blit(x - 10, y - 10); + + do_all(); + + if (n1 || n2) { + if (x >= 42 && x <= 81 && y >= 158 && y <= 177) { + flag = 0; + response = 1; + } + + if (x >= 240 && x <= 279 && y >= 158 && y <= 177) { + flag = 0; + response = 0; + } + } + } + + wait_mouse_relase(); + + delete warnkuva; + + return response; +} int small_warning(const char *message) { Bitmap *warnkuva; diff --git a/src/triplane.h b/src/triplane.h index 02a9670..cf3ec62 100644 --- a/src/triplane.h +++ b/src/triplane.h @@ -313,6 +313,7 @@ extern int main_engine_random_seed; /***************************** Functions **************************************/ +extern int big_warning(const char *message); extern int small_warning(const char *message); extern void kangas_terrain_to_screen(int leftx); extern void main_engine(void); From 0c9c291a0e53836460701053a854ebbc6235c875 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 6 Apr 2014 12:38:14 +0300 Subject: [PATCH 33/54] Make network game client use big_warning Warning/error messages from the network game client can be large enough not to fit in the small_warning dialog, so use the bigger one. --- src/io/netclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/netclient.cpp b/src/io/netclient.cpp index 9632f60..2e94818 100644 --- a/src/io/netclient.cpp +++ b/src/io/netclient.cpp @@ -811,6 +811,6 @@ void netclient_loop(const char *host, int port, netcbitmap_free_all(); if (!client_exit) { - small_warning(netc_printed_text("Network game client:\n")); + big_warning(netc_printed_text("Network game client:\n")); } } From 6f6552ec144337b6b21df5a767381f32d32f419f Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 4 May 2014 17:35:13 +0300 Subject: [PATCH 34/54] Asynchronous connect in network game client Connecting to the server can now be aborted with Esc. --- src/io/netclient.cpp | 64 +++++++++++++++++++++++++++++++++++++++--- src/menus/tripmenu.cpp | 10 ++----- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/io/netclient.cpp b/src/io/netclient.cpp index 2e94818..ebb0cc1 100644 --- a/src/io/netclient.cpp +++ b/src/io/netclient.cpp @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -599,6 +601,9 @@ static void netc_uncompress_and_receive(void) { static int netc_connect(const char *host, int port) { struct sockaddr_in sin; struct hostent *he; + int flags; + fd_set writefds; + struct timeval timeout; netc_sendbufend = 0; netc_recvubufend = 0; @@ -618,21 +623,72 @@ static int netc_connect(const char *host, int port) { exit(1); } + flags = fcntl(netc_socket, F_GETFL); + flags |= O_NONBLOCK; + if (fcntl(netc_socket, F_SETFL, flags) < 0) { + perror("fcntl"); + exit(1); + } + sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr = *(struct in_addr *)he->h_addr; if (connect(netc_socket, (struct sockaddr *)&sin, - sizeof(struct sockaddr_in)) < 0) { + sizeof(struct sockaddr_in)) == 0) + goto connect_ok; + if (errno != EINPROGRESS) { netc_printf("connect: %s", strerror(errno)); - close(netc_socket); - netc_socket = -1; - return 0; + goto connect_error; + } + + for (;;) { + FD_ZERO(&writefds); + FD_SET(netc_socket, &writefds); + timeout.tv_sec = 0; + timeout.tv_usec = 40000; // 1/25 seconds (less than a frame) + + if (select(netc_socket + 1, NULL, &writefds, NULL, &timeout) < 0) { + perror("select"); + exit(1); + } + + if (FD_ISSET(netc_socket, &writefds)) { + int val; + socklen_t len = sizeof(int); + if (getsockopt(netc_socket, SOL_SOCKET, SO_ERROR, + &val, &len) < 0) { + perror("getsockopt"); + exit(1); + } + if (val == 0) + goto connect_ok; + else if (val == EINPROGRESS) + continue; + else { + netc_printf("connect: %s", strerror(val)); + goto connect_error; + } + } + + netc_endframe(); + update_key_state(); + if (key && key[SDL_SCANCODE_ESCAPE]) { + netc_printf("Aborted"); + wait_relase(); + goto connect_error; + } } + connect_ok: netc_printf("Connected to server"); return 1; + + connect_error: + close(netc_socket); + netc_socket = -1; + return 0; } static void netc_doselect(void) { diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index f638703..e09812e 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3357,13 +3357,9 @@ void netgame_menu(void) { break; case 2: // Start the client - // FIXME display a "Connecting" text instead of the fade? - if (n1) - random_fade_out(); - else { - tyhjaa_vircr(); - do_all(); - } + netmenu->blit(0, 0, 20+5, 170, 145, 179); + frost->printf(20+5, 170, "Connecting... (Esc to abort)"); + do_all(); if (network_is_active()) { small_warning("You cannot be both host and client\n" "at the same time.\n" From 06b8e977f9df6977c35522c0fd7d820bc01fe687 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 4 May 2014 20:58:17 +0300 Subject: [PATCH 35/54] New font for network game overlay texts This new OVFONT should be readable on top of any screen of the game, and is now used for network game-related overlay texts. The current font file is just gridfont ("frost") converted to dark blue, which is only barely readable on some backgrounds. --- data/fokker.lst | 1 + data/font/ovfont.pgd | Bin 0 -> 6712 bytes data_src/pcx.lst | 1 + data_src/pcx/font/ovfont.pcx | Bin 0 -> 7887 bytes src/io/netclient.cpp | 4 ++-- src/menus/tripmenu.cpp | 2 +- src/triplane.cpp | 7 +++++-- src/triplane.h | 1 + 8 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 data/font/ovfont.pgd create mode 100644 data_src/pcx/font/ovfont.pcx diff --git a/data/fokker.lst b/data/fokker.lst index 27a6832..13623bf 100644 --- a/data/fokker.lst +++ b/data/fokker.lst @@ -147,6 +147,7 @@ data/font/acesfn.pgd data/font/grid3.pgd data/font/grid2.pgd data/font/gridfont.pgd +data/font/ovfont.pgd data/font/fontt/fontzero.pgd data/font/fontt/fontone.pgd data/font/fontt/fonttwo.pgd diff --git a/data/font/ovfont.pgd b/data/font/ovfont.pgd new file mode 100644 index 0000000000000000000000000000000000000000..47ca6c33e3b8f9a7d71aff16faa25d43bda34fb1 GIT binary patch literal 6712 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, y, "Network game client"); + foverlay->printf(x, y, "Network game client"); y += linesep; // don't display info older than 10 seconds @@ -111,7 +111,7 @@ static void netc_print_texts(void) { i = (netcinfo_next + 1) % NETCINFO_LINES; do { if (netcinfo_times[i] != 0 && netcinfo_times[i] > oldesttime) { - frost->printf(x, y, "%s", netcinfo_texts[i]); + foverlay->printf(x, y, "%s", netcinfo_texts[i]); y += linesep; } i = (i + 1) % NETCINFO_LINES; diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index e09812e..5c28970 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3409,7 +3409,7 @@ void netgame_menu(void) { network_activate_host(config.neth_listenaddr, config.neth_listenport, config.neth_password, - frost); + foverlay); // After activating the host, move to the assign // players menu (to select players) and then back // to the main menu diff --git a/src/triplane.cpp b/src/triplane.cpp index 94c42c4..29d5b99 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -147,6 +147,7 @@ int struct_heigth[MAX_STRUCTURES]; Font *fontti; Font *frost; Font *grid2; +Font *foverlay; //\ Shots control @@ -2483,6 +2484,8 @@ void load_up(void) { fontti = new Font("FONTT"); grid2 = new Font("G2FONT"); grid2->scale(); + foverlay = new Font("OVFONT"); + foverlay->scale(); } loading_text("Loading and initializing board-graphics."); @@ -3007,7 +3010,7 @@ void load_up(void) { cursor = new Bitmap("CURSOR"); loading_text("Loading data for chat support."); - chat_overlay_init(frost); + chat_overlay_init(frost); // not foverlay since a background is drawn } @@ -3818,7 +3821,7 @@ int main(int argc, char *argv[]) { (findparameter_arg("-netpassword") != NULL) ? findparameter_arg("-netpassword") : config.neth_password, - frost); + foverlay); } if (loading_texts) { diff --git a/src/triplane.h b/src/triplane.h index cf3ec62..b6740fd 100644 --- a/src/triplane.h +++ b/src/triplane.h @@ -119,6 +119,7 @@ extern int struct_heigth[MAX_STRUCTURES]; extern Font *fontti; extern Font *frost; extern Font *grid2; +extern Font *foverlay; //\ Shots control From 175fe3409eb8346baceba20ba83c3ecd1fd30f3c Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 4 May 2014 21:25:01 +0300 Subject: [PATCH 36/54] Clarify network client overlay text Now the client shows "Viewing screen from host" when in a menu, and no extra text (only possible chat messages etc.) when in the game. --- src/io/netclient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/io/netclient.cpp b/src/io/netclient.cpp index 17ea89b..80e20ef 100644 --- a/src/io/netclient.cpp +++ b/src/io/netclient.cpp @@ -102,7 +102,8 @@ static void netc_print_texts(void) { int i; uint32_t oldesttime; - foverlay->printf(x, y, "Network game client"); + if (netc_game_mode == 0) + foverlay->printf(x, y, "Viewing screen from host"); y += linesep; // don't display info older than 10 seconds From b7eea3e5c0acd3ce00cabea34a75163a7a2b6c4e Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sun, 4 May 2014 21:42:03 +0300 Subject: [PATCH 37/54] Default to multiplayer when starting host The network game host starts in the Assign Players menu, but now it also disables any existing solo players, so that auto-allocation of controls and players is performed for a multiplayer game. (Presumably network games are usually multiplayer.) --- src/menus/tripmenu.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 5c28970..16981cb 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3415,6 +3415,15 @@ void netgame_menu(void) { // to the main menu exit_flag = 1; wait_mouse_relase(); + // Unset any solo players, so that the assign + // players menu starts assigning players for a + // multiplayer game by default + for (i = 0; i < 4; i++) { + if (config.player_type[i] == 1) { + config.player_type[i] = 0; + config.player_number[i] = -1; + } + } assign_menu(); } break; From 10356cafd540208aded9de9aba3082d01066b10e Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Tue, 6 May 2014 00:20:00 +0300 Subject: [PATCH 38/54] Network game server: close connections with no data Close a new client connection if no greeting packet (which must be the first packet) is received in 15 seconds. --- src/io/network.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/io/network.cpp b/src/io/network.cpp index 9807f8f..da80228 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -1141,6 +1141,16 @@ void network_update(void) { } } } + + /* Close old non-accepted connections */ + uint32_t long_ago = SDL_GetTicks() - 15000; + for (i = 0; i < MAX_CONNECTIONS; i++) { + if (clients[i].state == CS_CONNECTED && + clients[i].connecttime < long_ago) { + netinfo_printf(0, "No data received from client #%d", i); + client_close(i); + } + } } /* prepare to quit */ From 409474bef2085d92b4282c7b117556699b70dc4d Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 10 May 2014 11:25:35 +0300 Subject: [PATCH 39/54] Adjustable character and line spacing in fonts --- src/gfx/font.cpp | 22 +++++++++++++++------- src/gfx/font.h | 3 +++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/gfx/font.cpp b/src/gfx/font.cpp index 904a2ed..d9cc132 100644 --- a/src/gfx/font.cpp +++ b/src/gfx/font.cpp @@ -32,7 +32,9 @@ Font::Font(const char *font_name) { Bitmap *valikuva; scaled = 0; - scaled_space = 3; + scaled_space = 2; + charspace = 1; + linespace = 0; for (temppi = 0; temppi < 256; temppi++) glyphs[temppi] = NULL; @@ -79,6 +81,11 @@ void Font::set_space(int space) { scaled_space = space; } +void Font::set_spacing(int charspace_, int linespace_) { + charspace = charspace_; + linespace = linespace_; +} + int Font::printf(int x, int y, const char *fmt, ...) { int temp = 0; int xkohta = 0; @@ -92,33 +99,33 @@ int Font::printf(int x, int y, const char *fmt, ...) { if (!scaled) { while (teksti[temp]) { if (teksti[temp] == '\n') { - y += height; + y += height + linespace; xkohta = 0; } else { if (glyphs[(unsigned char) teksti[temp]] != NULL) { glyphs[(unsigned char) teksti[temp]]->blit(x + xkohta, y); } - xkohta += width + 1; + xkohta += width + charspace; } temp++; } - return (x + temp * (width + 1)); + return (x + temp * (width + charspace)); } while (teksti[temp]) { if (teksti[temp] == '\n') { - y += height; + y += height + linespace; xkohta = 0; } else { if ((glyphs[(unsigned char) teksti[temp]] != NULL) && ((unsigned char) teksti[temp] != 32)) { glyphs[(unsigned char) teksti[temp]]->blit(x + xkohta - char_start[(unsigned char) teksti[temp]], y); - xkohta += char_width[(unsigned char) teksti[temp]]; + xkohta += char_width[(unsigned char) teksti[temp]] + charspace; } else { - xkohta += scaled_space; + xkohta += scaled_space + charspace; } @@ -214,6 +221,7 @@ void Font::count_scale(void) { char_width[temp] = valileveys - char_start[temp] + 2; } + char_width[temp]--; } } diff --git a/src/gfx/font.h b/src/gfx/font.h index 29dd060..55b65af 100644 --- a/src/gfx/font.h +++ b/src/gfx/font.h @@ -32,6 +32,8 @@ class Font { int char_start[256]; int scaled; int scaled_space; + int charspace; + int linespace; public: Font(const char *font_name); @@ -42,6 +44,7 @@ class Font { void unscale(void); void count_scale(void); void set_space(int space); + void set_spacing(int charspace_=1, int linespace_=0); }; #endif From 9e7d2f9b076a86a3564b84bf899027c4877565b8 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 10 May 2014 11:30:15 +0300 Subject: [PATCH 40/54] Support for recoloring and outlining of bitmaps New methods for changing colors and adding an outline to existing Bitmap objects. --- src/gfx/bitmap.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++ src/gfx/bitmap.h | 8 +++++++ 2 files changed, 63 insertions(+) diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index f8f9fb0..3af1790 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -457,6 +457,61 @@ void Bitmap::blit_to_bitmap(Bitmap * to, int xx, int yy) { to->refresh_sdlsurface(); } +void Bitmap::recolor(unsigned char oldcolor, unsigned char newcolor) { + int i; + + assert(!external_image_data); + + for (i = 0; i < width * height; i++) + if (image_data[i] == oldcolor) + image_data[i] = newcolor; + + data_sent = 0; + refresh_sdlsurface(); +} + +void Bitmap::blit_data(int tox, int toy, + const unsigned char *data, int w, int h, + int mask_color) { + int lx, ly; + + assert(tox + w <= width); + assert(toy + h <= height); + + for (lx = 0; lx < w; lx++) + for (ly = 0; ly < h; ly++) + if (data[ly * w + lx] != 255) + image_data[(toy + ly) * width + tox + lx] = + (mask_color == -1) ? data[ly * w + lx] : mask_color; +} + +void Bitmap::outline(unsigned char outlinecolor) { + unsigned char *old_data = image_data; + + assert(!external_image_data); + + width += 2; + height += 2; + hastransparency = 1; + image_data = (unsigned char *) walloc(width * height); + + memset(image_data, 255, width * height); + + blit_data(0, 0, old_data, width - 2, height - 2, outlinecolor); + blit_data(1, 0, old_data, width - 2, height - 2, outlinecolor); + blit_data(2, 0, old_data, width - 2, height - 2, outlinecolor); + blit_data(0, 1, old_data, width - 2, height - 2, outlinecolor); + blit_data(2, 1, old_data, width - 2, height - 2, outlinecolor); + blit_data(0, 2, old_data, width - 2, height - 2, outlinecolor); + blit_data(1, 2, old_data, width - 2, height - 2, outlinecolor); + blit_data(2, 2, old_data, width - 2, height - 2, outlinecolor); + blit_data(1, 1, old_data, width - 2, height - 2); + + wfree(old_data); + data_sent = 0; + refresh_sdlsurface(); +} + Bitmap *rotate_bitmap(Bitmap * picture, int degrees) { Bitmap *picture2; unsigned char *picture_data; diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index 0210675..e32d692 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -49,11 +49,19 @@ class Bitmap { void blit(int xx, int yy, int rx = 0, int ry = 0, int rx2 = 319, int ry2 = 199); void blit_fullscreen(void); void blit_to_bitmap(Bitmap * to, int xx, int yy); + void recolor(unsigned char oldcolor, unsigned char newcolor); + void outline(unsigned char outlinecolor); + unsigned char *info(int *width = NULL, int *height = NULL); void refresh_sdlsurface(); void clear_data_sent(); void send_bitmapdata(); void resend_bitmapdata(); + + private: + void blit_data(int tox, int toy, + const unsigned char *data, int w, int h, + int mask_color=-1); }; void all_bitmaps_refresh(void); From 7ca7bb7362230ea914f57422b8f182fee020dd9f Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 10 May 2014 11:33:05 +0300 Subject: [PATCH 41/54] Support for coloring and outlining fonts When creating a Font object from a bitmap, now the foreground color of a monochrome font can be changed, and characters in the font can be outlined with a provided color. --- src/gfx/font.cpp | 19 ++++++++++++++++++- src/gfx/font.h | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/gfx/font.cpp b/src/gfx/font.cpp index d9cc132..2f71929 100644 --- a/src/gfx/font.cpp +++ b/src/gfx/font.cpp @@ -24,7 +24,7 @@ #include #include -Font::Font(const char *font_name) { +Font::Font(const char *font_name, int fgcolor, int outlinecolor) { int temp; int temppi; int kokox, kokoy; @@ -63,6 +63,23 @@ Font::Font(const char *font_name) { glyphs[temp] = NULL; } + if (fgcolor != -1) { + for (temp = 0; temp < 256; temp++) + if (glyphs[temp] != NULL) + glyphs[temp]->recolor(0, fgcolor); + } + + if (outlinecolor != -1) { + for (temp = 0; temp < 256; temp++) + if (glyphs[temp] != NULL) + glyphs[temp]->outline(outlinecolor); + width += 2; + height += 2; + charspace--; + charspace--; // to compensate for count_scale ignoring outline + linespace--; + } + count_scale(); } diff --git a/src/gfx/font.h b/src/gfx/font.h index 55b65af..60a94bb 100644 --- a/src/gfx/font.h +++ b/src/gfx/font.h @@ -36,7 +36,7 @@ class Font { int linespace; public: - Font(const char *font_name); + Font(const char *font_name, int fgcolor=-1, int outlinecolor=-1); ~Font(); int printf(int x, int y, const char *fmt, ...); int scanf(int x, int y, char *str, int max_len); From bd2d4d8f2bfa4fd44fb7aeb09ddb8349a6a804ae Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Sat, 10 May 2014 11:42:21 +0300 Subject: [PATCH 42/54] Use outlining support for overlay font Make the overlay font in the network game use the new font recoloring and outlining support, instead of the hand-crafted font file that was introduced in commit 4231f3dd4213e9906ff8049f12fcb68c5701d706. Remove that font file, as an identical font can be created from gridfont with the new font recoloring support. --- data/fokker.lst | 1 - data/font/ovfont.pgd | Bin 6712 -> 0 bytes data_src/pcx.lst | 1 - data_src/pcx/font/ovfont.pcx | Bin 7887 -> 0 bytes src/io/netclient.cpp | 2 +- src/io/network.cpp | 2 +- src/triplane.cpp | 2 +- 7 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 data/font/ovfont.pgd delete mode 100644 data_src/pcx/font/ovfont.pcx diff --git a/data/fokker.lst b/data/fokker.lst index 13623bf..27a6832 100644 --- a/data/fokker.lst +++ b/data/fokker.lst @@ -147,7 +147,6 @@ data/font/acesfn.pgd data/font/grid3.pgd data/font/grid2.pgd data/font/gridfont.pgd -data/font/ovfont.pgd data/font/fontt/fontzero.pgd data/font/fontt/fontone.pgd data/font/fontt/fonttwo.pgd diff --git a/data/font/ovfont.pgd b/data/font/ovfont.pgd deleted file mode 100644 index 47ca6c33e3b8f9a7d71aff16faa25d43bda34fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6712 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 - foverlay = new Font("OVFONT"); + foverlay = new Font("GRFONT", 123, 116); foverlay->scale(); } From b353d481506347490703689d379e16e7c853e2e2 Mon Sep 17 00:00:00 2001 From: Riku Saikkonen Date: Tue, 30 Dec 2014 23:48:18 +0200 Subject: [PATCH 43/54] Check for ping timeouts in network games The server now send ping messages about every two seconds, and disconnects clients if they have not replied in about 20 seconds. This is done to get a more accurate view of which players are connected. More importantly, if a client or network connection gets stuck for some reason (without the TCP connection closing cleanly), the ping timeout allows the player to reconnect using the same player name (and thus continue controlling a plane) after waiting for the 20 seconds. --- src/io/network.cpp | 82 +++++++++++++++++++++++++++------------------- src/io/network.h | 9 +++-- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/io/network.cpp b/src/io/network.cpp index fde570d..316feab 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -763,6 +763,7 @@ static void net_receive_packet(int client, clients[client].cbufend = 0; compress_init(&clients[client].zs); + // Pretend the client has replied to a currently ongoing ping clients[client].last_pingid = last_pingid; clients[client].wanted_controls = -1; clients[client].state = CS_ACCEPTED; @@ -1000,6 +1001,36 @@ void network_change_game_mode(int newmode) { } } +static void network_handle_timeouts() { + static uint32_t last_periodic_ping = 0; + int i; + + uint32_t time = SDL_GetTicks(); + + if (time > last_periodic_ping + 2000) { + for (i = 0; i < MAX_CONNECTIONS; i++) { + /* Close old non-accepted connections */ + if (clients[i].state == CS_CONNECTED && + time > clients[i].connecttime + 15000) { + netinfo_printf(0, "No data received from client #%d", i); + client_close(i); + } + + /* Close connections that haven't replied to the last 10 pings */ + if (clients[i].state >= CS_ACCEPTED && + (256 + last_pingid - clients[i].last_pingid) % 256 > 10) { + netinfo_printf(0, "Ping timeout for %s", + clients[i].name); + client_close(i); + } + } + + /* Send pings every 2 seconds */ + network_ping(0); + last_periodic_ping = time; + } +} + /* this is called periodically (once or twice every frame) */ void network_update(void) { fd_set readfds, writefds, exceptfds; @@ -1142,15 +1173,7 @@ void network_update(void) { } } - /* Close old non-accepted connections */ - uint32_t long_ago = SDL_GetTicks() - 15000; - for (i = 0; i < MAX_CONNECTIONS; i++) { - if (clients[i].state == CS_CONNECTED && - clients[i].connecttime < long_ago) { - netinfo_printf(0, "No data received from client #%d", i); - client_close(i); - } - } + network_handle_timeouts(); } /* prepare to quit */ @@ -1184,9 +1207,9 @@ int network_is_active(void) { } /* - * Pings all current clients. After this, network_last_ping_done() - * will return 0 until either all clients have replied or the given - * number of seconds has passed. + * Pings all current clients. If seconds > 0, after this + * network_last_ping_done() will return 0 until either all clients + * have replied or the given number of seconds has passed. */ void network_ping(int seconds) { if (!network_host_active) @@ -1199,32 +1222,24 @@ void network_ping(int seconds) { netsend_ping(last_pingid); if (seconds > 0) - last_ping_end = SDL_GetTicks() + 1000*seconds; - else - last_ping_end = 0; + last_ping_end = SDL_GetTicks() + 1000 * seconds; } +/* + * Has everyone replied to the latest ping? + * Returns: 0 if a client has not replied to the latest ping and there + * is still time to wait; 1 if all clients have replied and there is + * still time to wait; 2 if the waiting time has run out. + */ int network_last_ping_done(void) { int i; if (!network_host_active) - return 1; - - if (last_ping_end == 0) - return 1; + return 1; // safe answer for how this is used - uint32_t time = SDL_GetTicks(); - - if (time > last_ping_end) { + if (last_ping_end != 0 && SDL_GetTicks() > last_ping_end) { last_ping_end = 0; - - for (i = 0; i < MAX_CONNECTIONS; i++) - if (clients[i].state >= CS_ACCEPTED && - clients[i].last_pingid != last_pingid) - netinfo_printf(0, "%s did not reply to a ping in time", - clients[i].name); - - return 1; + return 2; } for (i = 0; i < MAX_CONNECTIONS; i++) @@ -1232,10 +1247,11 @@ int network_last_ping_done(void) { clients[i].last_pingid != last_pingid) break; /* no reply yet */ - if (i == MAX_CONNECTIONS) { /* all have replied */ - last_ping_end = 0; + if (i == MAX_CONNECTIONS) /* all have replied */ return 1; - } + + if (last_ping_end == 0) + return 2; return 0; } diff --git a/src/io/network.h b/src/io/network.h index 063debb..f0328f3 100644 --- a/src/io/network.h +++ b/src/io/network.h @@ -77,9 +77,9 @@ * type=NET_PKTTYPE_PING: should reply with C_PONG packet * uint8_t pingid copy this into your reply * length=6 - * (this is used to wait for all clients just before starting a game; - * if you don't reply, the server waits longer and displays a warning, - * but the game will work) + * (this is used to check network connectivity and to wait for all + * clients just before starting a game; if you don't reply, the server + * will disconnect you after about 20 seconds) * * type=NET_PKTTYPE_GAMEMODE: * uint8_t new_mode 0=in menus now, 1=a game is in progress @@ -201,6 +201,9 @@ * echo -ne "\0\0\0\x30\xc8\x01debugtestingxtesting\0triplane\0\0\0\0\0\0\0\0\0\0\0\0\0" | nc localhost 9763 | pv >/dev/null * or to see some of the data: * echo -ne "\0\0\0\x30\xc8\x01debugtestingxtesting\0triplane\0\0\0\0\0\0\0\0\0\0\0\0\0" | nc localhost 9763 | perl -e 'use IO::Uncompress::Inflate qw(inflate); inflate("-" => "-") while !eof();' | hd | head -100 + * However, these dummy clients do not reply to pings, so they will + * only receive about 20 seconds of data before the server disconnects + * them. */ #define NET_PKTTYPE_QUIT 1 From 3c351bfc6b8cd2c69b7c70d0cf03807803971690 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 15:06:41 +0200 Subject: [PATCH 44/54] Remove undocumented -sdldraw mode --- src/gfx/bitmap.cpp | 121 ++++++--------------------------------------- src/gfx/bitmap.h | 3 -- src/gfx/fades.cpp | 2 - src/io/video.cpp | 99 +++++++++++++------------------------ src/io/video.h | 2 - src/triplane.cpp | 10 ---- 6 files changed, 50 insertions(+), 187 deletions(-) diff --git a/src/gfx/bitmap.cpp b/src/gfx/bitmap.cpp index 3af1790..f40be55 100644 --- a/src/gfx/bitmap.cpp +++ b/src/gfx/bitmap.cpp @@ -70,17 +70,6 @@ static void all_bitmaps_delete(int id) { all_bitmaps[id] = NULL; } -void all_bitmaps_refresh(void) { - int i; - - for (i = 0; i < MAX_BITMAPS; i++) { - if (all_bitmaps[i] == NULL) - continue; - if (!draw_with_vircr_mode) - all_bitmaps[i]->refresh_sdlsurface(); - } -} - void all_bitmaps_resend_if_sent(void) { int i; @@ -101,41 +90,6 @@ void all_bitmaps_send_now(void) { } } -void Bitmap::refresh_sdlsurface() { - SDL_Surface *tmps; - - if (sdlsurface != NULL) { - SDL_DestroyTexture(sdlsurface); - sdlsurface = NULL; - } - - if (draw_with_vircr_mode) - return; /* sdlsurfaces are not used */ - - tmps = SDL_CreateRGBSurfaceFrom(image_data, width, height, 8, width, 0, 0, 0, 0); - - if (tmps == NULL) { - fprintf(stderr, "SDL_CreateRGBSurfaceFrom: %s\n", SDL_GetError()); - exit(1); - } - - SDL_Palette *tmppal = SDL_AllocPalette(256); - SDL_SetPaletteColors(tmppal, curpal, 0, 256); - SDL_SetSurfacePalette(tmps, tmppal); - - if (hastransparency) - SDL_SetColorKey(tmps, SDL_TRUE, 0xff); - - sdlsurface = SDL_CreateTextureFromSurface(video_state.renderer, tmps); - if (sdlsurface == NULL) { - fprintf(stderr, "SDL_CreateTextureFromSurface: %s\n", SDL_GetError()); - exit(1); - } - - SDL_FreeSurface(tmps); - SDL_FreePalette(tmppal); -} - void Bitmap::send_bitmapdata() { if (!data_sent) { data_sent = netsend_bitmapdata(id, width, height, hastransparency, image_data); @@ -219,10 +173,8 @@ Bitmap::Bitmap(const char *image_name, int transparent) { name = image_name; hastransparency = transparent; - sdlsurface = NULL; data_sent = 0; id = all_bitmaps_add(this); - refresh_sdlsurface(); } @@ -235,7 +187,6 @@ Bitmap::Bitmap(int width, int height, this->height = height; this->name = name; this->hastransparency = hastransparency; - this->sdlsurface = NULL; this->data_sent = 0; if (copy_image_data) { @@ -248,16 +199,11 @@ Bitmap::Bitmap(int width, int height, } this->id = all_bitmaps_add(this); - refresh_sdlsurface(); } Bitmap::~Bitmap() { all_bitmaps_delete(id); - if (sdlsurface != NULL) { - SDL_DestroyTexture(sdlsurface); - sdlsurface = NULL; - } if (!external_image_data) free(image_data); netsend_bitmapdel(id); @@ -271,12 +217,7 @@ void Bitmap::blit_fullscreen(void) { send_bitmapdata(); netsend_bitmapblitfs(id); - if (update_vircr_mode) - memcpy(vircr, image_data, 320 * 200); - - if (!draw_with_vircr_mode) { - SDL_RenderCopy(video_state.renderer, sdlsurface, NULL, NULL); - } + memcpy(vircr, image_data, 320 * 200); } /* @@ -286,7 +227,6 @@ void Bitmap::blit_fullscreen(void) { void Bitmap::blit(int xx, int yy, int rx, int ry, int rx2, int ry2) { int fromminy, fromminx, frommaxy, frommaxx, bwidth; int xi, yi, tx, ty; - SDL_Rect clip, pos; if (current_mode == SVGA_MODE) { if (rx == 0 && ry == 0 && rx2 == 319 && ry2 == 199) { @@ -329,45 +269,26 @@ void Bitmap::blit(int xx, int yy, int rx, int ry, int rx2, int ry2) { else netsend_bitmapblitclipped(id, xx, yy, rx, ry, rx2, ry2); - if (update_vircr_mode) { - fromminy = (yy >= ry) ? 0 : ry - yy; - fromminx = (xx >= rx) ? 0 : rx - xx; - frommaxy = (yy + height - 1 <= ry2) ? height - 1 : ry2 - yy; - frommaxx = (xx + width - 1 <= rx2) ? width - 1 : rx2 - xx; - - if (fromminx <= frommaxx) { - if (hastransparency) { - for (yi = fromminy, ty = fromminy + yy; yi <= frommaxy; yi++, ty++) { - for (xi = fromminx, tx = fromminx + xx; xi <= frommaxx; xi++, tx++) { - unsigned char val = image_data[width * yi + xi]; - if (val != 0xff) { - vircr[bwidth * ty + tx] = val; - } + fromminy = (yy >= ry) ? 0 : ry - yy; + fromminx = (xx >= rx) ? 0 : rx - xx; + frommaxy = (yy + height - 1 <= ry2) ? height - 1 : ry2 - yy; + frommaxx = (xx + width - 1 <= rx2) ? width - 1 : rx2 - xx; + + if (fromminx <= frommaxx) { + if (hastransparency) { + for (yi = fromminy, ty = fromminy + yy; yi <= frommaxy; yi++, ty++) { + for (xi = fromminx, tx = fromminx + xx; xi <= frommaxx; xi++, tx++) { + unsigned char val = image_data[width * yi + xi]; + if (val != 0xff) { + vircr[bwidth * ty + tx] = val; } } - } else { /* can use memcpy without transparency */ - for (yi = fromminy, ty = fromminy + yy; yi <= frommaxy; yi++, ty++) - memcpy(&vircr[bwidth * ty + fromminx + xx], &image_data[width * yi + fromminx], frommaxx - fromminx + 1); } + } else { /* can use memcpy without transparency */ + for (yi = fromminy, ty = fromminy + yy; yi <= frommaxy; yi++, ty++) + memcpy(&vircr[bwidth * ty + fromminx + xx], &image_data[width * yi + fromminx], frommaxx - fromminx + 1); } } - - if (!draw_with_vircr_mode) { - clip.x = rx; - clip.y = ry; - clip.w = rx2 - rx + 1; - clip.h = ry2 - ry + 1; - pos.x = xx; - pos.y = yy; - pos.w = width; - pos.h = height; - SDL_RenderSetClipRect(video_state.renderer, &clip); - if (SDL_RenderCopy(video_state.renderer, sdlsurface, NULL, &pos) != 0) { - fprintf(stderr, "SDL_RenderCopy: %s\n", SDL_GetError()); - exit(1); - } - SDL_RenderSetClipRect(video_state.renderer, NULL); - } } unsigned char *Bitmap::info(int *width, int *height) { @@ -399,10 +320,8 @@ Bitmap::Bitmap(int x1, int y1, int xl, int yl, Bitmap * source_image) { name = source_image->name; hastransparency = source_image->hastransparency; - sdlsurface = NULL; data_sent = 0; id = all_bitmaps_add(this); - refresh_sdlsurface(); } /* Create a new Bitmap from the contents of vircr at (x,y) to (x+w,y+h) */ @@ -410,8 +329,6 @@ Bitmap::Bitmap(int x, int y, int w, int h) { int vircrw = (current_mode == VGA_MODE) ? 320 : 800; int fromy, toy; - assert(update_vircr_mode); /* otherwise vircr may not be valid */ - width = w; height = h; image_data = (unsigned char *) walloc(w * h); @@ -422,10 +339,8 @@ Bitmap::Bitmap(int x, int y, int w, int h) { name = "from_vircr"; hastransparency = 0; - sdlsurface = NULL; data_sent = 0; id = all_bitmaps_add(this); - refresh_sdlsurface(); } void Bitmap::blit_to_bitmap(Bitmap * to, int xx, int yy) { @@ -454,7 +369,6 @@ void Bitmap::blit_to_bitmap(Bitmap * to, int xx, int yy) { send_bitmapdata(); netsend_blittobitmap(id, to->id, xx, yy); } - to->refresh_sdlsurface(); } void Bitmap::recolor(unsigned char oldcolor, unsigned char newcolor) { @@ -467,7 +381,6 @@ void Bitmap::recolor(unsigned char oldcolor, unsigned char newcolor) { image_data[i] = newcolor; data_sent = 0; - refresh_sdlsurface(); } void Bitmap::blit_data(int tox, int toy, @@ -509,7 +422,6 @@ void Bitmap::outline(unsigned char outlinecolor) { wfree(old_data); data_sent = 0; - refresh_sdlsurface(); } Bitmap *rotate_bitmap(Bitmap * picture, int degrees) { @@ -547,7 +459,6 @@ Bitmap *rotate_bitmap(Bitmap * picture, int degrees) { free(temp_data); picture2->clear_data_sent(); - picture2->refresh_sdlsurface(); return picture2; } diff --git a/src/gfx/bitmap.h b/src/gfx/bitmap.h index e32d692..21b6ab7 100644 --- a/src/gfx/bitmap.h +++ b/src/gfx/bitmap.h @@ -32,7 +32,6 @@ class Bitmap { int external_image_data; // boolean: is image_data owned by this instance int hastransparency; int id; // a unique ID for this Bitmap - SDL_Texture *sdlsurface; // the field name derives from SDL 1.x legacy int data_sent; // has image_data been sent to the network? public: @@ -53,7 +52,6 @@ class Bitmap { void outline(unsigned char outlinecolor); unsigned char *info(int *width = NULL, int *height = NULL); - void refresh_sdlsurface(); void clear_data_sent(); void send_bitmapdata(); void resend_bitmapdata(); @@ -64,7 +62,6 @@ class Bitmap { int mask_color=-1); }; -void all_bitmaps_refresh(void); void all_bitmaps_resend_if_sent(void); void all_bitmaps_send_now(void); Bitmap *rotate_bitmap(Bitmap * picture, int degrees); diff --git a/src/gfx/fades.cpp b/src/gfx/fades.cpp index dcf72fd..b47eb5f 100644 --- a/src/gfx/fades.cpp +++ b/src/gfx/fades.cpp @@ -92,8 +92,6 @@ static void partial_fade(void) { int hit_value, temp_hit_value; int c, c2, c3, temp; - assert(update_vircr_mode); - next_color[0] = 0; for (c = 1; c < 256; c++) { diff --git a/src/io/video.cpp b/src/io/video.cpp index 6b72cdc..9e428cb 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -34,8 +34,6 @@ struct naytto ruutu; int current_mode = VGA_MODE; unsigned char *vircr; -int update_vircr_mode = 1; -int draw_with_vircr_mode = 1; int pixel_multiplier_vga = 1, pixel_multiplier_svga = 1; int wantfullscreen = 1; @@ -73,9 +71,6 @@ void setpal_range(const char pal[][3], int firstcolor, int n, int reverse) { memcpy(&curpal[firstcolor], cc, n * sizeof(SDL_Color)); wfree(cc); - - if (n != 8) // FIXME hack to ignore rotate_water_palet - all_bitmaps_refresh(); } void netsend_mode_and_curpal(void) { @@ -94,34 +89,16 @@ void netsend_mode_and_curpal(void) { } void fillrect(int x, int y, int w, int h, int c) { - SDL_Rect r; - netsend_fillrect(x, y, w, h, c); - if (update_vircr_mode) { - int screenw = (current_mode == VGA_MODE) ? 320 : 800; - if (w == 1 && h == 1) { - vircr[x + y * screenw] = c; - } else { - int i; - for (i = 0; i < h; i++) - memset(&vircr[x + (y + i) * screenw], c, w); - } + int screenw = (current_mode == VGA_MODE) ? 320 : 800; + if (w == 1 && h == 1) { + vircr[x + y * screenw] = c; + } else { + int i; + for (i = 0; i < h; i++) + memset(&vircr[x + (y + i) * screenw], c, w); } - - if (draw_with_vircr_mode) - return; - - r.x = x; - r.y = y; - r.w = w; - r.h = h; - SDL_SetRenderDrawColor(video_state.renderer, - curpal[c].r, - curpal[c].g, - curpal[c].b, - curpal[c].a); - SDL_RenderFillRect(video_state.renderer, &r); } void do_all(int do_retrace) { @@ -129,33 +106,27 @@ void do_all(int do_retrace) { network_print_serverinfo(); chat_draw_overlay(); - if (draw_with_vircr_mode) { - int w = (current_mode == VGA_MODE) ? 320 : 800; - int wh = (current_mode == VGA_MODE) ? 320 * 200 : 800 * 600; - int i; - uint8_t *in = vircr; - uint32_t *out = video_state.texture_buffer; - - for (i = 0; i < wh; i++, in++, out++) - *out = ((255 << 24) | - (curpal[*in].r << 16) | - (curpal[*in].g << 8) | - curpal[*in].b); - - SDL_UpdateTexture(video_state.texture, - NULL, - video_state.texture_buffer, - w * sizeof(uint32_t)); - SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); - SDL_RenderClear(video_state.renderer); - SDL_RenderCopy(video_state.renderer, video_state.texture, NULL, NULL); - } + int w = (current_mode == VGA_MODE) ? 320 : 800; + int wh = (current_mode == VGA_MODE) ? 320 * 200 : 800 * 600; + int i; + uint8_t *in = vircr; + uint32_t *out = video_state.texture_buffer; + + for (i = 0; i < wh; i++, in++, out++) + *out = ((255 << 24) | + (curpal[*in].r << 16) | + (curpal[*in].g << 8) | + curpal[*in].b); + + SDL_UpdateTexture(video_state.texture, + NULL, + video_state.texture_buffer, + w * sizeof(uint32_t)); + SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); + SDL_RenderClear(video_state.renderer); + SDL_RenderCopy(video_state.renderer, video_state.texture, NULL, NULL); + SDL_RenderPresent(video_state.renderer); - if (!draw_with_vircr_mode) { - // Prepare for drawing the next frame - SDL_SetRenderDrawColor(video_state.renderer, 0, 0, 0, 255); - SDL_RenderClear(video_state.renderer); - } netsend_endofframe(); network_update(); @@ -214,15 +185,13 @@ static int init_mode(int new_mode, const char *paletname) { SDL_RenderSetLogicalSize(video_state.renderer, w, h); - if (draw_with_vircr_mode) { - video_state.texture = SDL_CreateTexture(video_state.renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - w, - h); - assert(video_state.texture); - video_state.texture_buffer = (uint32_t *) walloc(w * h * sizeof(uint32_t)); - } + video_state.texture = SDL_CreateTexture(video_state.renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, + w, + h); + assert(video_state.texture); + video_state.texture_buffer = (uint32_t *) walloc(w * h * sizeof(uint32_t)); dksopen(paletname); diff --git a/src/io/video.h b/src/io/video.h index e7095de..d0f090c 100644 --- a/src/io/video.h +++ b/src/io/video.h @@ -57,8 +57,6 @@ void init_video(void); extern unsigned char *vircr; extern Bitmap *standard_background; extern int current_mode; -extern int update_vircr_mode; -extern int draw_with_vircr_mode; extern int pixel_multiplier_vga, pixel_multiplier_svga; extern int wantfullscreen; diff --git a/src/triplane.cpp b/src/triplane.cpp index 5068518..fa2181c 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -1765,9 +1765,6 @@ void main_engine(void) { //// Record setwrandom(7); - if (!draw_with_vircr_mode) - update_vircr_mode = 0; - all_bitmaps_send_now(); network_ping(15); @@ -1990,9 +1987,6 @@ void main_engine(void) { network_change_game_mode(0); - if (!draw_with_vircr_mode) - update_vircr_mode = 1; - wait_relase(); mission_re_fly = -1; @@ -3694,10 +3688,6 @@ void handle_parameters(void) { if (findparameter("-nofullscreen")) { wantfullscreen = 0; } - - if (findparameter("-sdldraw")) { - draw_with_vircr_mode = 0; - } } int main(int argc, char *argv[]) { From d51e3383d9f97e6e6f22752cf6f4c6d12138adab Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 15:24:32 +0200 Subject: [PATCH 45/54] Remove broken -2svga mode --- doc/triplane.6 | 4 ---- src/io/video.cpp | 2 +- src/io/video.h | 2 +- src/triplane.cpp | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/doc/triplane.6 b/doc/triplane.6 index 3b47734..e318a37 100644 --- a/doc/triplane.6 +++ b/doc/triplane.6 @@ -27,10 +27,6 @@ Examination->Hearing->Sounds on in the game menus. Scale the 320x200-pixel game window 2x, 3x or 4x using software scaling. This helps you play Triplane on displays that cannot switch to such low resolutions. -.IP "\fB\-2svga\fP" -Scale the 800x600-pixel window 2x using software scaling, to -produce a 1600x1200-pixel window. This resolution is used only in -multiplayer mode. .PP Note that triplane does not warn about unknown options. .SH "GETTING STARTED" diff --git a/src/io/video.cpp b/src/io/video.cpp index 9e428cb..fa44be8 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -34,7 +34,7 @@ struct naytto ruutu; int current_mode = VGA_MODE; unsigned char *vircr; -int pixel_multiplier_vga = 1, pixel_multiplier_svga = 1; +int pixel_multiplier_vga = 1; int wantfullscreen = 1; SDL_Color curpal[256]; diff --git a/src/io/video.h b/src/io/video.h index d0f090c..6f1fa79 100644 --- a/src/io/video.h +++ b/src/io/video.h @@ -57,7 +57,7 @@ void init_video(void); extern unsigned char *vircr; extern Bitmap *standard_background; extern int current_mode; -extern int pixel_multiplier_vga, pixel_multiplier_svga; +extern int pixel_multiplier_vga; extern int wantfullscreen; #endif diff --git a/src/triplane.cpp b/src/triplane.cpp index fa2181c..ac03edc 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -3677,10 +3677,6 @@ void handle_parameters(void) { pixel_multiplier_vga = 4; } - if (findparameter("-2svga")) { - pixel_multiplier_svga = 2; - } - if (findparameter("-fullscreen")) { wantfullscreen = 1; } From 631530cdba81e9c741561cc5f46f7750274ecd4f Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 15:30:34 +0200 Subject: [PATCH 46/54] Make -3 the default, implement -1. --- doc/triplane.6 | 7 +++---- src/io/video.cpp | 2 +- src/triplane.cpp | 4 ++++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/triplane.6 b/doc/triplane.6 index e318a37..8cdb37d 100644 --- a/doc/triplane.6 +++ b/doc/triplane.6 @@ -23,10 +23,9 @@ Start the game without sounds. Note: This option also disables sounds in the configuration file so sounds will be disabled also the next time you start triplane. To enable sounds set Physical Examination->Hearing->Sounds on in the game menus. -.IP "\fB\-2, -3, -4\fP" -Scale the 320x200-pixel game window 2x, 3x or 4x using software -scaling. This helps you play Triplane on displays that cannot switch to -such low resolutions. +.IP "\fB\-1, -2, -3, -4\fP" +Scale the initial 320x200-pixel game window 1x, 2x, 3x or 4x. +The default is -3. You can resize the window freely at runtime. .PP Note that triplane does not warn about unknown options. .SH "GETTING STARTED" diff --git a/src/io/video.cpp b/src/io/video.cpp index fa44be8..6ec2909 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -34,7 +34,7 @@ struct naytto ruutu; int current_mode = VGA_MODE; unsigned char *vircr; -int pixel_multiplier_vga = 1; +int pixel_multiplier_vga = 3; int wantfullscreen = 1; SDL_Color curpal[256]; diff --git a/src/triplane.cpp b/src/triplane.cpp index ac03edc..0ed4410 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -3665,6 +3665,10 @@ void handle_parameters(void) { loading_text("Sounds disabled."); } + if (findparameter("-1")) { + pixel_multiplier_vga = 1; + } + if (findparameter("-2")) { pixel_multiplier_vga = 2; } From 7477434ef31dd875ef0f968b27faf557beeb6700 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 15:35:16 +0200 Subject: [PATCH 47/54] Document Pause/F4 --- doc/triplane.6 | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/triplane.6 b/doc/triplane.6 index 8cdb37d..5bef8ec 100644 --- a/doc/triplane.6 +++ b/doc/triplane.6 @@ -48,6 +48,7 @@ controls: .IP "\(bu use X and W to steer the plane" 4 .IP "\(bu use 1 to drop a bomb" 4 .IP "\(bu use S to roll the plane upside down" 4 +.IP "\(bu use Pause or F4 to pause the game" 4 .PP When you have destroyed all the targets try to land by releasing power and gliding to the runway. Holding up and down simultaneously From 34c46df4392ab51e76d6764011b7ea9a811bc9d5 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 17:12:29 +0200 Subject: [PATCH 48/54] Refactor network UI. Remove support for specifying preferred plane --- src/io/network.cpp | 4 +- src/menus/tripmenu.cpp | 126 ++++++++++------------------------------- src/settings.cpp | 2 - src/settings.h | 1 - 4 files changed, 32 insertions(+), 101 deletions(-) diff --git a/src/io/network.cpp b/src/io/network.cpp index 316feab..247072a 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -148,7 +148,7 @@ void network_print_serverinfo(void) { if (clients[i].state >= CS_ACCEPTED) nclients++; - netinfo_font->printf(x, y, "Network host active (%d client%s)", + netinfo_font->printf(x, y, "Server active (%d client%s)", nclients, nclients==1 ? "" : "s"); y += linesep; @@ -639,7 +639,7 @@ void network_activate_host(const char *listenaddr, exit(1); } - netinfo_printf(0, "Network host listening on TCP port %d", port); + netinfo_printf(0, "Server listening on TCP port %d", port); chat_set_sender(network_chat_send); } diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 16981cb..c397302 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3184,19 +3184,13 @@ void netgame_menu(void) { int exit_flag = 0; int i, x, y, n1, n2, menuselect; Bitmap *netmenu, *help, *right; - Bitmap *napp[4]; - Bitmap *controlme; - Bitmap *upnapp[4]; Bitmap *assignme, *playerselect; char str[100]; menu_position positions[] = { { 40, 43, 1 }, { 168, 43, 1 }, { 82, 53, 1 }, { 210, 53, 1 }, { 40, 73, 1 }, { 168, 73, 1 }, - { 37, 108, 1 }, { 37+1*18, 108, 1 }, - { 37+2*18, 108, 1 }, { 37+3*18, 108, 1 }, - { 114, 130, 0 /* (config.netc_controlplanes != 0) */ }, - { 122, 130, 0 /* (config.netc_controlplanes != 0) */ }, + { 106, 130, 1 }, { 114, 130, 1 }, { 40, 153, 1 }, { 67, 174, 1 }, { 195, 174, 1 }, { 284, 180, 1 }, { 0, 0, -1 } }; @@ -3205,65 +3199,39 @@ void netgame_menu(void) { !roster[config.netc_solo_controls].pilotname[0]) config.netc_solo_controls = 0; + if (strcmp(config.netc_playername, "netplayer") == 0 && + roster[config.netc_solo_controls].pilotname[0] && + check_strict_string(roster[config.netc_solo_controls].pilotname, 21)) + strcpy(config.netc_playername, + roster[config.netc_solo_controls].pilotname); + netmenu = new Bitmap("NETMEN"); - help = new Bitmap("HELP4"); // FIXME no help bitmap yet right = new Bitmap("RIGHT"); - napp[0] = new Bitmap("NAPPRE"); - napp[1] = new Bitmap("NAPPBL"); - napp[2] = new Bitmap("NAPPGR"); - napp[3] = new Bitmap("NAPPYL"); - controlme = new Bitmap("NAPPIS"); - upnapp[0] = new Bitmap(11, 23, 12, 11, controlme); - upnapp[1] = new Bitmap(38, 23, 12, 11, controlme); - upnapp[2] = new Bitmap(11, 46, 12, 11, controlme); - upnapp[3] = new Bitmap(38, 46, 12, 11, controlme); assignme = new Bitmap("ASSIGN"); - playerselect = new Bitmap(42, 79, 111, 11, assignme); + playerselect = new Bitmap(134, 79, 19, 10, assignme); while (!exit_flag) { menu_keys(&exit_flag, &help_on); - - // can control only 0 or 1 planes - for (i = 0; i < 4; i++) { - if (config.netc_controlplanes & (1<blit(0, 0); - frost->printf(20+4, 11, "NETWORK GAME CLIENT"); + frost->printf(20+4, 11, "JOIN A NETWORK GAME"); - frost->printf(20, 30, "Address of host:"); + frost->printf(20, 30, "Server address:"); frost->printf(20+10, 40, "%s", config.netc_host); - frost->printf(20, 50, "Port number:"); + frost->printf(20, 50, "Server port:"); frost->printf(20+55, 50, "%d", config.netc_port); // FIXME hide password? - frost->printf(20, 60, "Game password:"); + frost->printf(20, 60, "Server password:"); frost->printf(20+10, 70, "%s", config.netc_password); - frost->printf(20, 80, "Which plane would you\n" - "like to control, if any?\n" - "(the host can change it)"); - for (i = 0; i < 4; i++) - if (config.netc_controlplanes & (1<blit(20+12 + i*18 - 1, 103 - 1); - else - upnapp[i]->blit(20+12 + i*18, 103); + frost->printf(20, 118, "Use solo controls of:"); + playerselect->blit(101, 125); + if (roster[config.netc_solo_controls].pilotname[0]) + frost->printf(20+10, 128, "%s", + roster[config.netc_solo_controls].pilotname); - if (config.netc_controlplanes != 0) { - frost->printf(20, 118, "Use solo controls of:"); - playerselect->blit(16, 125); - if (roster[config.netc_solo_controls].pilotname[0]) - frost->printf(20, 128, "%s", - roster[config.netc_solo_controls].pilotname); - } - - frost->printf(20, 140, "Your player name:"); + frost->printf(20, 140, "Player name:"); frost->printf(20+10, 150, "%s", config.netc_playername); // FIXME draw the button in the bitmap? @@ -3273,23 +3241,19 @@ void netgame_menu(void) { frost->printf(148, 30, "Listen address:"); frost->printf(148+10, 40, "%s", config.neth_listenaddr); - frost->printf(148, 50, "Port number:"); + frost->printf(148, 50, "Server port:"); frost->printf(148+55, 50, "%d", config.neth_listenport); // FIXME hide password? - frost->printf(148, 60, "Game password:"); + frost->printf(148, 60, "Server password:"); frost->printf(148+10, 70, "%s", config.neth_password); // FIXME draw the button in the bitmap? if (network_is_active()) { - frost->printf(148, 160, "The host is already active"); - frost->printf(148+5, 170, "[ DEACTIVATE HOST ]"); + frost->printf(148+5, 170, "[ STOP THE SERVER ]"); } else { - frost->printf(148+3, 170, "[ ACTIVATE THE HOST ]"); + frost->printf(148+3, 170, "[ START THE SERVER ]"); } - if (help_on) - help->blit(0, 0); - menuselect = 0; if (x >= 267 && x <= 301 && y >= 155 && y <= 190) { @@ -3308,8 +3272,8 @@ void netgame_menu(void) { menuselect = 10 + (x - 20 - 12) / 18; } - if (x >= 110 && x <= 125 && y >= 126 && y <= 126+7) { - menuselect = 15 + (x >= 118); + if (x >= 102 && x <= 117 && y >= 126 && y <= 126+7) { + menuselect = 15 + (x >= 110); } if (x >= 20+10 && x <= 115 && y >= 35 && y <= 49) { @@ -3361,14 +3325,13 @@ void netgame_menu(void) { frost->printf(20+5, 170, "Connecting... (Esc to abort)"); do_all(); if (network_is_active()) { - small_warning("You cannot be both host and client\n" + small_warning("You cannot be both server and client\n" "at the same time.\n" "\n" - "Please deactivate the host first."); + "Please stop the server first."); break; } - if (config.netc_controlplanes != 0 && - !roster[config.netc_solo_controls].pilotname[0]) { + if (!roster[config.netc_solo_controls].pilotname[0]) { small_warning("Your roster is empty, but you need a\n" "player in the roster to set your keys.\n" "\n" @@ -3379,16 +3342,9 @@ void netgame_menu(void) { set_keys_none(); - if (config.netc_controlplanes == 0) { - netclient_activate_controls(-1, 0); - } else { - for (i = 0; i < 4; i++) { - if (config.netc_controlplanes & (1<= MAX_PLAYERS_IN_ROSTER || @@ -3533,15 +3473,9 @@ void netgame_menu(void) { } } - for (i = 0; i < 4; i++) { - delete upnapp[i]; - delete napp[i]; - } delete playerselect; delete assignme; - delete controlme; - delete help; delete right; delete netmenu; diff --git a/src/settings.cpp b/src/settings.cpp index 4412af0..1f32170 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -435,7 +435,6 @@ void swap_config_endianes(void) { clean_string(config.netc_password, 40); clean_string(config.netc_playername, 40); config.netc_solo_controls = SDL_SwapLE32(config.netc_solo_controls); - config.netc_controlplanes = SDL_SwapLE32(config.netc_controlplanes); clean_string(config.neth_listenaddr, 80); config.neth_listenport = SDL_SwapLE32(config.neth_listenport); @@ -503,7 +502,6 @@ void load_config(void) { memset(config.netc_playername, 0, 40); strcpy(config.netc_playername, "netplayer"); config.netc_solo_controls = 1; - config.netc_controlplanes = 0; memset(config.neth_listenaddr, 0, 80); strcpy(config.neth_listenaddr, "0.0.0.0"); diff --git a/src/settings.h b/src/settings.h index f6eee81..4331fa9 100644 --- a/src/settings.h +++ b/src/settings.h @@ -161,7 +161,6 @@ struct configuration { char netc_password[40]; char netc_playername[40]; int32_t netc_solo_controls; - int32_t netc_controlplanes; char neth_listenaddr[80]; int32_t neth_listenport; From 898682e8e7ed22e8dea5bc78e7b6b142e42e8e41 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 17:54:09 +0200 Subject: [PATCH 49/54] Use black on cyan for OSD texts --- src/triplane.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/triplane.cpp b/src/triplane.cpp index 0ed4410..6ab55d9 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -2478,7 +2478,7 @@ void load_up(void) { fontti = new Font("FONTT"); grid2 = new Font("G2FONT"); grid2->scale(); - foverlay = new Font("GRFONT", 123, 116); + foverlay = new Font("GRFONT", 0, 116); foverlay->scale(); } From fea25034bb171e3989b2e2ff97a6c296bb8e0d93 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 17:55:01 +0200 Subject: [PATCH 50/54] Remove unused variable --- src/menus/tripmenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index c397302..5395541 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -3183,7 +3183,7 @@ void netgame_menu(void) { int help_on = 0; int exit_flag = 0; int i, x, y, n1, n2, menuselect; - Bitmap *netmenu, *help, *right; + Bitmap *netmenu, *right; Bitmap *assignme, *playerselect; char str[100]; menu_position positions[] = { From d420161053ce3feb061e00b0ddffa5f250823b00 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 18:12:43 +0200 Subject: [PATCH 51/54] Rework network game texts --- src/io/network.cpp | 21 +++------------------ src/menus/tripmenu.cpp | 20 ++------------------ 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/src/io/network.cpp b/src/io/network.cpp index 247072a..f06dceb 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -966,39 +966,24 @@ const char *network_controlling_player_string(int playernum) { return "no network game"; if (controls_clientname[playernum][0] == '\0') { - return "played by host"; + return "local player"; } else if (controls_active_for_client[playernum] == -1) { - sprintf(buf, "%s should play", + sprintf(buf, "remote player %s", controls_clientname[playernum]); } else { - sprintf(buf, "%s plays", + sprintf(buf, "remote player %s", clients[controls_active_for_client[playernum]].name); } return buf; } void network_change_game_mode(int newmode) { - char buf[200]; - game_mode = newmode; if (!network_host_active) return; netsend_gamemode(newmode); - - if (newmode == 1) { - strcpy(buf, "Controls: R: "); - strcat(buf, network_controlling_player_string(0)); - strcat(buf, " B: "); - strcat(buf, network_controlling_player_string(1)); - netinfo_printf(1, "%s", buf); - strcpy(buf, "Controls: G: "); - strcat(buf, network_controlling_player_string(2)); - strcat(buf, " Y: "); - strcat(buf, network_controlling_player_string(3)); - netinfo_printf(1, "%s", buf); - } } static void network_handle_timeouts() { diff --git a/src/menus/tripmenu.cpp b/src/menus/tripmenu.cpp index 5395541..dc384fc 100644 --- a/src/menus/tripmenu.cpp +++ b/src/menus/tripmenu.cpp @@ -2334,9 +2334,7 @@ void assign_menu(void) { int lym[4] = { 0, 11, 24, 36 }; int response; int help_on = 0; - int plane_colors[4] = { 32, 144, 96, 71 }; char clientname[4][21] = { "", "", "", "" }; - int clientcolor[4] = { -1, -1, -1, -1 }; char tmpname[21] = ""; char unallocated[200] = ""; bool autoallocate[4] = { true, true, true, true }; @@ -2377,7 +2375,6 @@ void assign_menu(void) { if (network_is_active()) { for (l = 0; l < 4; l++) { network_get_allowed_controls(l, clientname[l]); - clientcolor[l] = network_find_preferred_color(clientname[l]); } } @@ -2442,7 +2439,6 @@ void assign_menu(void) { config.player_type[l] = 3; allocate: strcpy(clientname[l], tmpname); - clientcolor[l] = l; set_roster_from_clientname(l, clientname[l]); continue; notallocated: @@ -2513,13 +2509,9 @@ void assign_menu(void) { continue; if (clientname[l][0] == '\0') { - frost->printf(46 + lx, 70 + ly, "Controlled by host only"); + frost->printf(46 + lx, 70 + ly, "Local player"); } else { - frost->printf(46 + lx, 70 + ly, "Net: %s", clientname[l]); - if (clientcolor[l] >= 0 && clientcolor[l] <= 3) - fill_vircr(46 + lx + 16, 70 + ly + 1, - 46 + lx + 18, 70 + ly + 3, - plane_colors[clientcolor[l]]); + frost->printf(46 + lx, 70 + ly, "Remote: %s", clientname[l]); } if (x >= (134 + lx) && x < (143 + lx) && y >= (67 + ly) && y <= (77 + ly)) { @@ -2534,11 +2526,6 @@ void assign_menu(void) { //frost->printf(82,177,"Select previous network player"); } } - - if (unallocated[0] != '\0') { - frost->printf(30, 175, "Network players wanting controls:"); - frost->printf(45, 185, "%s", unallocated); - } } cursor->blit(x - 10, y - 10); @@ -2618,7 +2605,6 @@ void assign_menu(void) { for (l = 0; l < 4; l++) { if (config.player_type[l] == 0 || config.player_type[l] == 2) { clientname[l][0] = '\0'; - clientcolor[l] = -1; } } } @@ -2686,8 +2672,6 @@ void assign_menu(void) { case 5: case 6: network_find_next_controls((menuselect == 6), clientname[menusubselect1]); - clientcolor[menusubselect1] = - network_find_preferred_color(clientname[menusubselect1]); set_roster_from_clientname(menusubselect1, clientname[menusubselect1]); break; From 420445934adc643c4a6c4e004a4380160f03f77a Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 18:30:32 +0200 Subject: [PATCH 52/54] Fix client controls when client disconnects --- src/io/network.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/io/network.cpp b/src/io/network.cpp index f06dceb..82ebe92 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -611,6 +611,7 @@ void network_activate_host(const char *listenaddr, */ controls_clientname[i][0] = '\0'; controls_active_for_client[i] = -1; + net_controls[i] = 0; } listen_socket = socket(PF_INET, SOCK_STREAM, 0); @@ -704,7 +705,7 @@ void network_controls_for_player(int playernum, if (!network_host_active) return; - if (controls_active_for_client[playernum] == -1) + if (controls_clientname[playernum][0] == '\0') return; /* no network controls for this player */ uint8_t controls = net_controls[playernum]; @@ -953,6 +954,7 @@ void network_reallocate_controls(void) { shouldbe = -1; if (controls_active_for_client[i] != shouldbe) { controls_active_for_client[i] = shouldbe; + net_controls[i] = 0; // FIXME notify the players of the change? } } From d09c536e6cf1ab20ec5cbf0e6028e022ac2cfe15 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 18:42:06 +0200 Subject: [PATCH 53/54] Remove chat support for now --- Makefile | 2 +- src/io/chat.cpp | 163 ------------------------------------------ src/io/chat.h | 32 --------- src/io/netclient.cpp | 26 ------- src/io/network.cpp | 36 ---------- src/io/network.h | 14 +--- src/io/sdl_compat.cpp | 9 +-- src/io/video.cpp | 2 - src/triplane.cpp | 4 -- 9 files changed, 5 insertions(+), 283 deletions(-) delete mode 100644 src/io/chat.cpp delete mode 100644 src/io/chat.h diff --git a/Makefile b/Makefile index 4cd76e3..bd32a20 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ INSTALL_PROGRAM ?= install COMMON_OBJS = src/gfx/bitmap.o src/gfx/font.o \ src/gfx/gfx.o src/util/wutil.o src/util/random.o \ src/io/sdl_compat.o src/io/video.o src/io/network.o \ - src/io/chat.o src/io/mouse.o src/io/dksfile.o src/io/timing.o + src/io/mouse.o src/io/dksfile.o src/io/timing.o TRIPLANE_OBJS = src/triplane.o src/world/tripai.o \ src/world/tripmis.o src/gfx/fades.o \ src/menus/menusupport.o src/menus/tripmenu.o \ diff --git a/src/io/chat.cpp b/src/io/chat.cpp deleted file mode 100644 index 6f5519d..0000000 --- a/src/io/chat.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Triplane Classic - a side-scrolling dogfighting game. - * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy - * - * 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 3 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, see . - * - * tjt@users.sourceforge.net - */ - -/******************************************************************************* - - Purpose: - Writing and sending chat messages in networked game - -*******************************************************************************/ - -#include "triplane.h" -#include "gfx/gfx.h" -#include "gfx/font.h" -#include "io/network.h" -#include "util/wutil.h" - -// Maximum length of a chat message in the line editor, not including -// the terminating NUL. Must be smaller than length of -// NET_PKTTYPE_C_CHATMSG msg. -#define EDITLINELEN 70 - -static const char chat_f6_message[] = "Help!"; -static const char chat_f7_message[] = "Yes, ok"; -static const char chat_f8_message[] = "No"; - -static Font *chat_font = NULL; -static void (*chat_sender)(const char *) = NULL; -static int chat_overlay_on = 0; -static char chat_text[EDITLINELEN + 1] = ""; -static int chat_textpos = 0; -static uint32_t chat_last_message_time = 0; - -static void chat_send(const char *msg) { - if (SDL_GetTicks() < chat_last_message_time + 1000) - return; // we are chatting too fast, ignore - - chat_last_message_time = SDL_GetTicks(); - - if (chat_sender != NULL) - chat_sender(msg); -} - -static void chat_erase_text(void) { - chat_text[0] = '\0'; - chat_textpos = 0; -} - -static void chat_overlay_start(void) { - chat_overlay_on = 1; -} - -static void chat_overlay_stop(void) { - chat_overlay_on = 0; -} - -void chat_overlay_init(Font *font) { - chat_font = font; -} - -void chat_set_sender(void (*new_sender)(const char *)) { - chat_sender = new_sender; -} - -void chat_draw_overlay(void) { - int display_was_enabled; - - if (!chat_overlay_on) - return; - - if (chat_sender == NULL) { - chat_overlay_stop(); - return; - } - - // don't display this to the clients, if we are the server - display_was_enabled = network_display_enable(0); - - // FIXME draw a bitmap for the background (load in chat_overlay_init()) - fill_vircr(20, 50, 300, 80, 14); - chat_font->printf(30, 55, "Message for chat (Enter=done, Esc=exit):"); - chat_font->printf(30, 65, "%s_", chat_text); - - network_display_enable(display_was_enabled); -} - -static int chat_edit_key(int keysym) { - switch (keysym) { - case SDLK_RETURN: - chat_overlay_stop(); - if (chat_text[0] != '\0') - chat_send(chat_text); - chat_erase_text(); - return 1; - case SDLK_ESCAPE: - chat_overlay_stop(); - chat_erase_text(); - return 1; - case SDLK_F5: - chat_overlay_stop(); - return 1; - case SDLK_BACKSPACE: - if (chat_textpos > 0) { - chat_text[--chat_textpos] = '\0'; - } - return 1; - default: - if (printable_char(keysym)) { - if (chat_textpos < EDITLINELEN) { - chat_text[chat_textpos++] = keysym; - chat_text[chat_textpos] = '\0'; - } - return 1; - } - } - - return 0; -} - -// returns 1 if keysym was consumed by the chat code -int chat_input_key(int keysym) { - if (chat_sender == NULL) - return 0; - - if (chat_overlay_on) { - return chat_edit_key(keysym); - } else { - if (keysym == SDLK_F5) { - chat_overlay_start(); - return 1; - } else if (keysym == SDLK_F6) { - chat_send(chat_f6_message); - return 1; - } else if (keysym == SDLK_F7) { - chat_send(chat_f7_message); - return 1; - } else if (keysym == SDLK_F8) { - chat_send(chat_f8_message); - return 1; - } - } - return 0; -} - -int chat_overlay_is_on(void) { - return chat_overlay_on; -} diff --git a/src/io/chat.h b/src/io/chat.h deleted file mode 100644 index 1cdb83f..0000000 --- a/src/io/chat.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Triplane Classic - a side-scrolling dogfighting game. - * Copyright (C) 1996,1997,2009 Dodekaedron Software Creations Oy - * - * 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 3 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, see . - * - * tjt@users.sourceforge.net - */ - -#ifndef CHAT_H -#define CHAT_H - -#include "gfx/font.h" - -void chat_overlay_init(Font *font); -void chat_set_sender(void (*new_sender)(const char *)); -void chat_draw_overlay(void); -int chat_input_key(int keysym); -int chat_overlay_is_on(void); - -#endif diff --git a/src/io/netclient.cpp b/src/io/netclient.cpp index 6de3445..41a9f99 100644 --- a/src/io/netclient.cpp +++ b/src/io/netclient.cpp @@ -28,7 +28,6 @@ #include "triplane.h" #include "util/wutil.h" #include "io/network.h" -#include "io/chat.h" #include "io/netclient.h" #include "io/joystick.h" #include "io/sdl_compat.h" @@ -231,13 +230,6 @@ static void netc_send_setcontrols(uint8_t controls) { netc_send_packet(6, NET_PKTTYPE_C_SETCONTROLS, &controls); } -static void netc_send_chatmsg(const char *msg) { - char data[71]; - memset(data, 0, 71); - strcpy(data, msg); - netc_send_packet(76, NET_PKTTYPE_C_CHATMSG, data); -} - static void netc_recv_quit(void) { netc_printf("Server quitting"); netc_close(); @@ -291,10 +283,6 @@ static void netc_recv_infomsg(const char *msg) { netc_printf("%s", msg); } -static void netc_recv_chatmsg(const char *sender, const char *msg) { - netc_printf("<%s> %s", (sender[0] == '\0' ? "Host" : sender), msg); -} - static void netc_recv_fillrect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t color) { @@ -409,12 +397,6 @@ static int netc_receive_packet(uint32_t length, uint8_t type, void *data) { if (!check_printable_string(cdata, 71)) return 0; netc_recv_infomsg(cdata); - } else if (type == NET_PKTTYPE_CHATMSG && length == 97) { - if (!check_strict_string(cdata, 21)) - return 0; - if (!check_printable_string(cdata + 21, 71)) - return 0; - netc_recv_chatmsg(cdata, cdata + 21); } else if (type == NET_PKTTYPE_FILLRECT && length == 14) { uint16_t x = read_from_net_16(cdata); uint16_t y = read_from_net_16(cdata + 2); @@ -807,10 +789,6 @@ void netclient_activate_controls(int countrynum, int rosternum) { } } -static void netclient_chat_send(const char *msg) { - netc_send_chatmsg(msg); -} - void netclient_loop(const char *host, int port, const char *playername, const char *password) { int client_exit = 0; @@ -828,8 +806,6 @@ void netclient_loop(const char *host, int port, if (netc_controls_country >= 0) netc_send_wantcontrols(netc_controls_country); - chat_set_sender(netclient_chat_send); - netc_last_endofframe = SDL_GetTicks(); while (netc_socket != -1) { @@ -854,8 +830,6 @@ void netclient_loop(const char *host, int port, netc_controls(); } - chat_set_sender(NULL); - if (netc_socket != -1) { netc_printf("Client quitting"); netc_send_quit(); diff --git a/src/io/network.cpp b/src/io/network.cpp index 82ebe92..2ee1e95 100644 --- a/src/io/network.cpp +++ b/src/io/network.cpp @@ -27,7 +27,6 @@ #include "util/wutil.h" #include "io/network.h" -#include "io/chat.h" #include "io/video.h" #include "gfx/bitmap.h" #include @@ -427,15 +426,6 @@ void netsend_infomsg(const char *msg) { write_to_net_fixedstring(msg, 71); } -void netsend_chatmsg(const char *sender, const char *msg) { - if (!network_host_active) - return; - - write_to_net_hdr(97, NET_PKTTYPE_CHATMSG); - write_to_net_fixedstring(sender, 21); - write_to_net_fixedstring(msg, 71); -} - void netsend_fillrect(int x, int y, int w, int h, int c) { if (!network_host_active || !network_display_enabled) return; @@ -571,11 +561,6 @@ static void send_initial_data(int clientid) { netsend_change_target(oldtarget); } -/* send chat message originating from host */ -static void network_chat_send(const char *msg) { - netinfo_printf(0, " %s", msg); - netsend_chatmsg("", msg); -} void network_activate_host(const char *listenaddr, int port, @@ -641,8 +626,6 @@ void network_activate_host(const char *listenaddr, } netinfo_printf(0, "Server listening on TCP port %d", port); - - chat_set_sender(network_chat_send); } static void net_recv_quit(int client) { @@ -688,11 +671,6 @@ static void net_recv_set_controls(int client, /* else just ignore the packet */ } -static void net_recv_chatmsg(int client, const char *msg) { - netinfo_printf(0, "<%s> %s", clients[client].name, msg); - netsend_chatmsg(clients[client].name, msg); -} - /* * Possibly sets the controls for playernum from the network. This * function either leaves the arguments alone, or changes them to @@ -795,18 +773,6 @@ static void net_receive_packet(int client, uint8_t controls = *((uint8_t *)data); controls &= 0x3f; /* only 6 lower bits are used */ net_recv_set_controls(client, controls); - } else if (type == NET_PKTTYPE_C_CHATMSG) { - if (length != 76) - return; - char *msg = &((char *)data)[0]; - msg[50] = 0; - if (!check_printable_string(msg, 71)) { - netinfo_printf(0, "Error: invalid chat message from client #%d", - client); - client_close(client); - return; - } - net_recv_chatmsg(client, msg); } else return; /* ignores unknown packet types */ } @@ -1170,8 +1136,6 @@ void network_quit(void) { if (!network_host_active) return; - chat_set_sender(NULL); - netinfo_printf(1, "Preparing to quit, closing all connections"); write_to_net_hdr(5, NET_PKTTYPE_QUIT); diff --git a/src/io/network.h b/src/io/network.h index f0328f3..8e2d5ab 100644 --- a/src/io/network.h +++ b/src/io/network.h @@ -90,12 +90,6 @@ * length=76 * (should be displayed somewhere on screen for a while) * - * type=NET_PKTTYPE_CHATMSG: received chat message - * char sender[21] client player name, or empty for host - * char msg[71] only [a-zA-Z0-9] characters - * length=97 - * (should be displayed somewhere on screen for a while) - * * type=NET_PKTTYPE_FILLRECT: * uint16_t x, y, w, h coordinates are always inside the screen * uint8_t color @@ -191,10 +185,6 @@ * the client should send these packets only when the game mode set by * NET_PKTTYPE_GAMEMODE is 1) * - * type=NET_PKTTYPE_C_CHATMSG: public oneline chat message (shown to all) - * char msg[71] only [a-zA-Z0-9] characters are accepted - * length=76 - * * A very simple dummy client for debugging and seeing how much data * is sent (the echo command sends a C_GREETING packet using the default * password): @@ -214,7 +204,7 @@ #define NET_PKTTYPE_PING 6 #define NET_PKTTYPE_GAMEMODE 7 #define NET_PKTTYPE_INFOMSG 8 -#define NET_PKTTYPE_CHATMSG 9 +//#define NET_PKTTYPE_CHATMSG 9 #define NET_PKTTYPE_FILLRECT 10 #define NET_PKTTYPE_BITMAPDATA 11 @@ -239,7 +229,7 @@ #define NET_PKTTYPE_C_DISABLECONTROLS 212 #define NET_PKTTYPE_C_SETCONTROLS 213 -#define NET_PKTTYPE_C_CHATMSG 220 +//#define NET_PKTTYPE_C_CHATMSG 220 void netsend_videomode(char new_mode); void netsend_setpal_range(const char pal[][3], diff --git a/src/io/sdl_compat.cpp b/src/io/sdl_compat.cpp index 7075db8..1661e19 100644 --- a/src/io/sdl_compat.cpp +++ b/src/io/sdl_compat.cpp @@ -36,7 +36,6 @@ #include #include "sdl_compat.h" #include "io/dksfile.h" -#include "io/chat.h" #include "util/wutil.h" #include "io/timing.h" @@ -88,8 +87,7 @@ int getch(void) { s = toupper(s); } } - if (!chat_input_key(s)) - return s; + return s; } } @@ -100,10 +98,7 @@ void update_key_state(void) { while (getch() != 0) ; - if (chat_overlay_is_on()) - key = NULL; - else - key = SDL_GetKeyboardState(&key_size); + key = SDL_GetKeyboardState(&key_size); } void wait_relase(void) { diff --git a/src/io/video.cpp b/src/io/video.cpp index 6ec2909..d5e17d3 100644 --- a/src/io/video.cpp +++ b/src/io/video.cpp @@ -21,7 +21,6 @@ #include "io/video.h" #include "io/dksfile.h" #include "io/network.h" -#include "io/chat.h" #include "util/wutil.h" #include #include @@ -104,7 +103,6 @@ void fillrect(int x, int y, int w, int h, int c) { void do_all(int do_retrace) { // this code is called at the end of every displayed frame network_print_serverinfo(); - chat_draw_overlay(); int w = (current_mode == VGA_MODE) ? 320 : 800; int wh = (current_mode == VGA_MODE) ? 320 * 200 : 800 * 600; diff --git a/src/triplane.cpp b/src/triplane.cpp index 6ab55d9..c851f68 100644 --- a/src/triplane.cpp +++ b/src/triplane.cpp @@ -27,7 +27,6 @@ #include "triplane.h" #include "io/joystick.h" #include "io/netclient.h" -#include "io/chat.h" #include "gfx/gfx.h" #include "menus/menusupport.h" #include "menus/tripmenu.h" @@ -3002,9 +3001,6 @@ void load_up(void) { loading_text("Loading mouse cursor."); cursor = new Bitmap("CURSOR"); - - loading_text("Loading data for chat support."); - chat_overlay_init(frost); // not foverlay since a background is drawn } From 16ccac9ef70bf5c1cb98755559ac0b8bff47f748 Mon Sep 17 00:00:00 2001 From: Timo Juhani Lindfors Date: Sun, 19 Nov 2017 19:04:13 +0200 Subject: [PATCH 54/54] Update build instructions --- README.install | 1 + 1 file changed, 1 insertion(+) diff --git a/README.install b/README.install index 8449daa..b1a35a0 100644 --- a/README.install +++ b/README.install @@ -8,6 +8,7 @@ Dependencies * SDL 2.0 * SDL mixer 2.0 * GNU make +* zlib Build procedure ---------------