--- a/src/control.cpp +++ b/src/control.cpp @@ -19,7 +19,7 @@ $Id: control.cpp,v 1.3 2003/04/20 19:30:29 anoncvs_pygsl Exp $ #include "SDL/SDL.h" #include "SDL/SDL_image.h" #include "SDL/SDL_thread.h" -#include "SDL/SDL_gfxPrimitives.h" //rdm 9/11 +#include "fredy_gfx.h" #include "dfont.h" #include "submarine.h" #include "files.h" --- a/src/draw.cpp +++ b/src/draw.cpp @@ -17,9 +17,7 @@ void DrawPixel(SDL_Surface *screen, int x, int y, Uint32 color){ } //place the pixel on the screen - Uint16 *pixel_location; - pixel_location = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; - *pixel_location = color; + fredy_put_pixel(screen, x, y, color); } void DrawLine(SDL_Surface *screen, int X1, int Y1, int X2, int Y2, Uint32 Color){ --- a/src/esm.cpp +++ b/src/esm.cpp @@ -484,7 +484,5 @@ void Esm::DPixel(SDL_Surface *screen, int x, int y, Uint32 color) } //place the pixel on the screen - Uint16 *pixel_location; - pixel_location = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; - *pixel_location = color; + fredy_put_pixel(screen, x, y, color); } --- /dev/null +++ b/src/fredy_gfx.cpp @@ -0,0 +1,115 @@ +// fredygames.com: o unico pedaco do SDL_gfx que o OpenSSN usa (filledTrigonRGBA), escrito aqui porque o +// Emscripten nao traz o SDL_gfx. Preenche o triangulo linha a linha com SDL_FillRect (cor opaca; o jogo so +// chama com alfa 255). GPL-2, como o resto do jogo. +#include +#include +#include "SDL/SDL.h" +#include "fredy_gfx.h" + +int filledTrigonRGBA(SDL_Surface *dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, + Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + if (! dst) + return -1; + Uint32 color = SDL_MapRGB(dst->format, r, g, b); + int xs[3] = { x1, x2, x3 }; + int ys[3] = { y1, y2, y3 }; + int top = std::min(y1, std::min(y2, y3)); + int bottom = std::max(y1, std::max(y2, y3)); + for (int y = top; y <= bottom; y++) + { + int left = 1 << 30, right = -(1 << 30); + for (int i = 0; i < 3; i++) + { + int j = (i + 1) % 3; + int ya = ys[i], yb = ys[j]; + if (ya == yb) + { + if (y == ya) + { + left = std::min(left, std::min(xs[i], xs[j])); + right = std::max(right, std::max(xs[i], xs[j])); + } + continue; + } + if ( (y < std::min(ya, yb)) || (y > std::max(ya, yb)) ) + continue; + int x = xs[i] + (int) floor( (double) (xs[j] - xs[i]) * (y - ya) / (yb - ya) + 0.5 ); + left = std::min(left, x); + right = std::max(right, x); + } + if (left > right) + continue; + SDL_Rect span; + span.x = left; + span.y = y; + span.w = right - left + 1; + span.h = 1; + SDL_FillRect(dst, &span, color); + } + return 0; +} + + +#undef SDL_AddTimer +#undef SDL_RemoveTimer + +#define FREDY_MAX_TIMERS 8 +struct FredyTimer +{ + int active; + Uint32 interval; + Uint32 next; + SDL_TimerCallback callback; + void *param; +}; +static FredyTimer fredy_timers[FREDY_MAX_TIMERS]; + +SDL_TimerID fredy_add_timer(Uint32 interval, SDL_TimerCallback callback, void *param) +{ + for (int i = 0; i < FREDY_MAX_TIMERS; i++) + { + if (! fredy_timers[i].active) + { + fredy_timers[i].active = 1; + fredy_timers[i].interval = interval ? interval : 1; + fredy_timers[i].next = SDL_GetTicks() + fredy_timers[i].interval; + fredy_timers[i].callback = callback; + fredy_timers[i].param = param; + return (SDL_TimerID) (i + 1); + } + } + return 0; +} + +SDL_bool fredy_remove_timer(SDL_TimerID id) +{ + int i = (int) id - 1; + if ( (i < 0) || (i >= FREDY_MAX_TIMERS) || (! fredy_timers[i].active) ) + return SDL_FALSE; + fredy_timers[i].active = 0; + return SDL_TRUE; +} + +void fredy_poll_timers(void) +{ + Uint32 now = SDL_GetTicks(); + for (int i = 0; i < FREDY_MAX_TIMERS; i++) + { + if ( (fredy_timers[i].active) && ((Sint32) (now - fredy_timers[i].next) >= 0) ) + { + Uint32 again = fredy_timers[i].callback(fredy_timers[i].interval, fredy_timers[i].param); + // the callback may have removed this timer (or the slot may now belong to another one) + if (fredy_timers[i].active) + { + if (again == 0) + fredy_timers[i].active = 0; + else + { + fredy_timers[i].interval = again; + fredy_timers[i].next = now + again; + } + } + } + } +} --- /dev/null +++ b/src/fredy_gfx.h @@ -0,0 +1,6 @@ +#ifndef FREDY_GFX_H +#define FREDY_GFX_H +#include "SDL/SDL.h" +int filledTrigonRGBA(SDL_Surface *dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, + Uint8 r, Uint8 g, Uint8 b, Uint8 a); +#endif --- /dev/null +++ b/src/fredy_web.h @@ -0,0 +1,32 @@ +// fredygames.com: incluido com -include em todos os arquivos. O SDL_Delay do SDL 1.2 do Emscripten aborta no +// laco principal do navegador; a espera vira um emscripten_sleep (Asyncify), que devolve a vez ao navegador. +#ifndef FREDY_WEB_H +#define FREDY_WEB_H +#include +#include +#undef SDL_Delay +#define SDL_Delay(ms) emscripten_sleep(ms) + +// The game writes pixels straight into surfaces and assumes 16 bits per pixel. The SDL 1.2 of Emscripten keeps +// the screen in a canvas (32 bits per pixel), where writes to ->pixels only show up between SDL_LockSurface and +// SDL_UnlockSurface, so every pixel goes through SDL_FillRect (a 1x1 rectangle) instead, which works on any +// surface. +static inline void fredy_put_pixel(SDL_Surface *surface, int x, int y, Uint32 color) +{ + SDL_Rect dot; + dot.x = x; + dot.y = y; + dot.w = 1; + dot.h = 1; + SDL_FillRect(surface, &dot, color); +} + +// SDL_AddTimer of the Emscripten SDL 1.2 fires once and ignores the interval the callback returns; the game +// needs a periodic timer (the game clock). These replace it: the callbacks run from fredy_poll_timers(), which +// the main loop calls every frame (fredy_gfx.cpp). +SDL_TimerID fredy_add_timer(Uint32 interval, SDL_TimerCallback callback, void *param); +SDL_bool fredy_remove_timer(SDL_TimerID id); +void fredy_poll_timers(void); +#define SDL_AddTimer fredy_add_timer +#define SDL_RemoveTimer fredy_remove_timer +#endif --- a/src/main.cpp +++ b/src/main.cpp @@ -2710,14 +2710,17 @@ int HandleInput(SDL_Event &event, int &mousex, int &mousey){ break; // case SDLK_n: // Navigation case SDLK_F2: + case SDLK_m: return NAVMAP; break; // case SDLK_s: // Sonar case SDLK_F1: + case SDLK_o: return SONAR; break; // case SDLK_t: // TMA case SDLK_F3: + case SDLK_b: return WEAPONS; break; case SDLK_ESCAPE: // Quit... @@ -2730,23 +2733,30 @@ int HandleInput(SDL_Event &event, int &mousex, int &mousey){ return DUMPSCREEN; break; case SDLK_F4: + case SDLK_h: return SHIPCONTROL; break; case SDLK_F5: + case SDLK_d: return ESM; break; case SDLK_F6: + case SDLK_v: return RADAR; break; + case SDLK_j: case SDLK_KP4: // Turn Left return TURNPORT; break; + case SDLK_l: case SDLK_KP6: // Turn Right return TURNSTARBOARD; break; + case SDLK_k: case SDLK_KP2: // Dive return INCREASEDEPTH; break; + case SDLK_i: case SDLK_KP8: //Surface return DECREASEDEPTH; break; @@ -3651,7 +3661,7 @@ int main(int argc, char **argv){ free(ship_file); if (new_torpedo) { - char *sound_file, *file_name = "sounds/torpedo-launch.ogg"; + char *sound_file, *file_name = "sounds/torpedo-launch.wav"; sound_file = Find_Data_File(file_name); Play_Sound(sound_file); if ( (sound_file) && (sound_file != file_name) ) @@ -3702,6 +3712,7 @@ int main(int argc, char **argv){ timer2 = SDL_GetTicks(); } */ + fredy_poll_timers(); if (should_update_everything) { should_update_everything = FALSE; --- a/src/radar.cpp +++ b/src/radar.cpp @@ -1200,9 +1200,7 @@ void Radar::DrawPixel(SDL_Surface *screen, int x, int y, Uint32 color) } //place the pixel on the screen - Uint16 *pixel_location; - pixel_location = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; - *pixel_location = color; + fredy_put_pixel(screen, x, y, color); } void Radar::DrawArc(SDL_Surface *screen, int X1, int Y1, int Radius, int Theta1, int Theta2, Uint32 Color) --- a/src/sonar.cpp +++ b/src/sonar.cpp @@ -86,9 +86,7 @@ inline void AnBqq5::DPixel(SDL_Surface *screen, int x, int y, Uint32 color) { //this only works for 16bpp screens //place the pixel on the screen - Uint16 *pixel_location; - pixel_location = (Uint16 *)screen->pixels + y*screen->pitch/2 + x; - *pixel_location = color; + fredy_put_pixel(screen, x, y, color); } --- a/src/sound.cpp +++ b/src/sound.cpp @@ -25,12 +25,20 @@ int Init_Audio(int allow_sounds) void Sound_Done() { + // The SDL_mixer of Emscripten calls this hook again from inside Mix_HaltMusic(): without the guard + // the two call each other until the stack runs out. + static int in_done = FALSE; + if (in_done) + return; + in_done = TRUE; if (my_music) { - Mix_HaltMusic(); - Mix_FreeMusic(my_music); + Mix_Music *finished = my_music; my_music = NULL; + Mix_HaltMusic(); + Mix_FreeMusic(finished); } + in_done = FALSE; } --- a/src/submarine.cpp +++ b/src/submarine.cpp @@ -1300,7 +1300,7 @@ int Submarine::Check_Status() delta_depth = fabs(target->Depth - Depth); if ( (range < HITTING_RANGE) && (delta_depth < HITTING_DEPTH) ) { - char *full_path, *explosion = "sounds/explosion.ogg"; + char *full_path, *explosion = "sounds/explosion.wav"; full_path = Find_Data_File(explosion); Play_Sound(full_path); if ( (full_path) && (explosion) ) @@ -1562,7 +1562,7 @@ int Submarine::Send_Ping(Submarine *all_ships) { Submarine *current; int status; - char *full_path, *ping_file = "sounds/sonar-ping.ogg"; + char *full_path, *ping_file = "sounds/sonar-ping.wav"; // some craft do not have sonar if (! has_sonar) return FALSE; --- a/src/targetmotionanalysis.cpp +++ b/src/targetmotionanalysis.cpp @@ -183,9 +183,7 @@ void TargetMotionAnalysis::PlotPixel(int x, int y, Uint32 color){ } //place the pixel on the screen - Uint16 *pixel_location; - pixel_location = (Uint16 *)GeoPlotScreen->pixels + y*GeoPlotScreen->pitch/2 + x; - *pixel_location = color; + fredy_put_pixel(GeoPlotScreen, x, y, color); } /** erases the work screen */ void TargetMotionAnalysis::ClearGeoPlot(){