/* game_loop() has been turned into a SDL based loop. The code for one iteration of the original game_loop is in game_loop_iter. */ static void game_loop() { /* FPS */ int fps = 0; float fps_ema = 0; int fps_target = 25; /* TODO: compute alpha dynamically based on frametime */ const float ema_alpha = 0.07; const int frametime_target = 1000 / fps_target; /* in milliseconds */ int last_frame = SDL_GetTicks(); int drag_button = 0; unsigned int last_down[3] = {0}; unsigned int last_click[3] = {0}; unsigned int current_ticks = SDL_GetTicks(); unsigned int accum = 0; SDL_Event event; gui_event_t ev; game_loop_run = 1; while (game_loop_run) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_MOUSEBUTTONUP: if (drag_button == event.button.button) { ev.type = GUI_EVENT_TYPE_DRAG_END; ev.x = event.button.x; ev.y = event.button.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); drag_button = 0; } ev.type = GUI_EVENT_TYPE_BUTTON_UP; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (event.button.button <= 3 && current_ticks - last_down[event.button.button-1] < MOUSE_SENSITIVITY) { ev.type = GUI_EVENT_TYPE_CLICK; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (current_ticks - last_click[event.button.button-1] < MOUSE_SENSITIVITY) { ev.type = GUI_EVENT_TYPE_DBL_CLICK; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); } last_click[event.button.button-1] = current_ticks; } break; case SDL_MOUSEBUTTONDOWN: ev.type = GUI_EVENT_TYPE_BUTTON_DOWN; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (event.button.button <= 3) last_down[event.button.button-1] = current_ticks; break; case SDL_MOUSEMOTION: if (drag_button == 0) { /* Move pointer normally. */ interface_set_cursor(&interface, event.motion.x, event.motion.y); } for (int button = 1; button <= 3; button++) { if (event.motion.state & SDL_BUTTON(button)) { if (drag_button == 0) { drag_button = button; ev.type = GUI_EVENT_TYPE_DRAG_START; ev.x = event.motion.x; ev.y = event.motion.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); } ev.type = GUI_EVENT_TYPE_DRAG_MOVE; ev.x = event.motion.x; ev.y = event.motion.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); break; } } break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_CTRL)) { game_loop_quit(); break; } switch (event.key.keysym.sym) { /* Map scroll */ case SDLK_UP: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 0, -1); } break; case SDLK_DOWN: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 0, 1); } break; case SDLK_LEFT: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, -1, 0); } break; case SDLK_RIGHT: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 1, 0); } break; /* Panel click shortcuts */ case SDLK_1: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 0); } break; case SDLK_2: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 1); } break; case SDLK_3: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 2); } break; case SDLK_4: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 3); } break; case SDLK_5: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 4); } break; case SDLK_TAB: if (event.key.keysym.mod & KMOD_SHIFT) { interface_return_from_message(&interface); } else { interface_open_message(&interface); } break; /* Game speed */ case SDLK_PLUS: case SDLK_KP_PLUS: if (game.game_speed < 40) game.game_speed += 1; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_MINUS: case SDLK_KP_MINUS: if (game.game_speed >= 1) game.game_speed -= 1; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_0: game.game_speed = DEFAULT_GAME_SPEED; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_p: if (game.game_speed == 0) game_pause(0); else game_pause(1); break; /* Audio */ case SDLK_s: sfx_enable(!sfx_is_enabled()); break; case SDLK_m: midi_enable(!midi_is_enabled()); break; /* Video */ case SDLK_f: if (event.key.keysym.mod & KMOD_CTRL) { sdl_set_fullscreen(!sdl_is_fullscreen()); } break; /* Misc */ case SDLK_ESCAPE: if (GUI_OBJECT(&interface.popup)->displayed) { interface_close_popup(&interface); } else if (interface.building_road) { interface_build_road_end(&interface); } break; /* Debug */ case SDLK_g: interface.viewport.layers ^= VIEWPORT_LAYER_GRID; break; case SDLK_b: interface.viewport.show_possible_build = !interface.viewport.show_possible_build; break; case SDLK_j: { int current = 0; for (int i = 0; i < 4; i++) { if (interface.player == game.player[i]) { current = i; break; } } for (int i = (current+1) % 4; i != current; i = (i+1) % 4) { if (PLAYER_IS_ACTIVE(game.player[i])) { interface.player = game.player[i]; LOGD("main", "Switched to player %i.", i); break; } } } break; case SDLK_z: if (event.key.keysym.mod & KMOD_CTRL) { save_game(0); } break; case SDLK_F10: interface_open_game_init(&interface); break; default: break; } break; case SDL_VIDEORESIZE: sdl_set_resolution(event.resize.w, event.resize.h, 0); frame_t *screen = sdl_get_screen_frame(); int width = sdl_frame_get_width(screen); int height = sdl_frame_get_height(screen); screen_frame.clip.w = width; screen_frame.clip.h = height; gui_object_set_size((gui_object_t *)&interface, width, height); break; case SDL_QUIT: game_loop_quit(); break; } } unsigned int new_ticks = SDL_GetTicks(); int delta_ticks = new_ticks - current_ticks; current_ticks = new_ticks; /* Update FPS EMA per frame */ fps = 1000*(1.0 / (float)delta_ticks); if (fps_ema > 0) fps_ema = ema_alpha*fps + (1-ema_alpha)*fps_ema; else if (fps > 0) fps_ema = fps; accum += delta_ticks; while (accum >= TICK_LENGTH) { game_update(); /* Autosave periodically */ if ((game.const_tick % AUTOSAVE_INTERVAL) == 0 && game.game_speed > 0) { int r = save_game(1); if (r < 0) LOGW("main", "Autosave failed."); } /* Print FPS */ if ((game.const_tick % (10*TICKS_PER_SEC)) == 0) { LOGV("main", "FPS: %i", (int)fps_ema); } accum -= TICK_LENGTH; } /* Update and draw interface */ interface_update(&interface); gui_object_redraw(GUI_OBJECT(&interface), game.frame); /* TODO very crude dirty marking algortihm: mark everything. */ sdl_mark_dirty(0, 0, sdl_frame_get_width(game.frame), sdl_frame_get_height(game.frame)); /* Swap video buffers */ sdl_swap_buffers(); /* Reduce framerate to target if we finished too fast */ int now = SDL_GetTicks(); int frametime_spent = now - last_frame; if (frametime_spent < frametime_target) { SDL_Delay(frametime_target - frametime_spent); } last_frame = SDL_GetTicks(); } }
/* game_loop() has been turned into a SDL based loop. The code for one iteration of the original game_loop is in game_loop_iter. */ static void game_loop() { /* FPS */ int fps = 0; int fps_ema = 0; int fps_target = 25; const float ema_alpha = 0.003; int drag_button = 0; unsigned int last_down[3] = {0}; unsigned int last_click[3] = {0}; unsigned int current_ticks = SDL_GetTicks(); unsigned int accum = 0; unsigned int accum_frames = 0; SDL_Event event; gui_event_t ev; game_loop_run = 1; while (game_loop_run) { if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_MOUSEBUTTONUP: if (drag_button == event.button.button) { ev.type = GUI_EVENT_TYPE_DRAG_END; ev.x = event.button.x; ev.y = event.button.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); drag_button = 0; } ev.type = GUI_EVENT_TYPE_BUTTON_UP; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (event.button.button <= 3 && current_ticks - last_down[event.button.button-1] < MOUSE_SENSITIVITY) { ev.type = GUI_EVENT_TYPE_CLICK; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (current_ticks - last_click[event.button.button-1] < MOUSE_SENSITIVITY) { ev.type = GUI_EVENT_TYPE_DBL_CLICK; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); } last_click[event.button.button-1] = current_ticks; } break; case SDL_MOUSEBUTTONDOWN: ev.type = GUI_EVENT_TYPE_BUTTON_DOWN; ev.x = event.button.x; ev.y = event.button.y; ev.button = event.button.button; gui_object_handle_event((gui_object_t *)&interface, &ev); if (event.button.button <= 3) last_down[event.button.button-1] = current_ticks; break; case SDL_MOUSEMOTION: if (drag_button == 0) { /* Move pointer normally. */ interface_set_cursor(&interface, event.motion.x, event.motion.y); } for (int button = 1; button <= 3; button++) { if (event.motion.state & SDL_BUTTON(button)) { if (drag_button == 0) { drag_button = button; ev.type = GUI_EVENT_TYPE_DRAG_START; ev.x = event.motion.x; ev.y = event.motion.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); } ev.type = GUI_EVENT_TYPE_DRAG_MOVE; ev.x = event.motion.x; ev.y = event.motion.y; ev.button = drag_button; gui_object_handle_event((gui_object_t *)&interface, &ev); break; } } break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_CTRL)) { game_loop_quit(); break; } switch (event.key.keysym.sym) { /* Map scroll */ case SDLK_UP: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 0, -1); } break; case SDLK_DOWN: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 0, 1); } break; case SDLK_LEFT: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, -1, 0); } break; case SDLK_RIGHT: { viewport_t *viewport = interface_get_top_viewport(&interface); viewport_move_by_pixels(viewport, 1, 0); } break; /* Panel click shortcuts */ case SDLK_1: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 0); } break; case SDLK_2: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 1); } break; case SDLK_3: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 2); } break; case SDLK_4: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 3); } break; case SDLK_5: { panel_bar_t *panel = interface_get_panel_bar(&interface); panel_bar_activate_button(panel, 4); } break; /* Game speed */ case SDLK_PLUS: case SDLK_KP_PLUS: if (game.game_speed < 40) game.game_speed += 1; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_MINUS: case SDLK_KP_MINUS: if (game.game_speed >= 1) game.game_speed -= 1; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_0: game.game_speed = DEFAULT_GAME_SPEED; LOGI("main", "Game speed: %u", game.game_speed); break; case SDLK_p: if (game.game_speed == 0) game_pause(0); else game_pause(1); break; /* Audio */ case SDLK_s: sfx_enable(!sfx_is_enabled()); break; case SDLK_m: midi_enable(!midi_is_enabled()); break; /* Misc */ case SDLK_ESCAPE: if (BIT_TEST(interface.click, 7)) { /* Building road */ interface_build_road_end(&interface); } else if (interface.clkmap != 0) { interface_close_popup(&interface); } break; /* Debug */ case SDLK_g: interface.viewport.layers ^= VIEWPORT_LAYER_GRID; break; case SDLK_j: { int current = 0; for (int i = 0; i < 4; i++) { if (interface.player == game.player[i]) { current = i; break; } } for (int i = (current+1) % 4; i != current; i = (i+1) % 4) { if (PLAYER_IS_ACTIVE(game.player[i])) { interface.player = game.player[i]; LOGD("main", "Switched to player %i.", i); break; } } } break; case SDLK_z: if (event.key.keysym.mod & KMOD_CTRL) { save_game(0); } break; default: break; } break; case SDL_QUIT: game_loop_quit(); break; } } unsigned int new_ticks = SDL_GetTicks(); int delta_ticks = new_ticks - current_ticks; current_ticks = new_ticks; accum += delta_ticks; while (accum >= TICK_LENGTH) { game_update(); /* Autosave periodically */ if ((game.const_tick % AUTOSAVE_INTERVAL) == 0 && game.game_speed > 0) { int r = save_game(1); if (r < 0) LOGW("main", "Autosave failed."); } /* FPS */ fps = 1000*((float)accum_frames / accum); if (fps_ema > 0) fps_ema = ema_alpha*fps + (1-ema_alpha)*fps_ema; else if (fps > 0) fps_ema = fps; if ((game.const_tick % (10*TICKS_PER_SEC)) == 0) { LOGV("main", "FPS: %i", fps_ema); } accum -= TICK_LENGTH; accum_frames = 0; } /* Update and draw interface */ interface_update(&interface); interface.flags &= ~BIT(4); interface.flags &= ~BIT(7); gui_object_redraw((gui_object_t *)&interface, game.frame); /* TODO very crude dirty marking algortihm: mark everything. */ sdl_mark_dirty(0, 0, sdl_frame_get_width(game.frame), sdl_frame_get_height(game.frame)); /* Swap video buffers */ sdl_swap_buffers(); accum_frames += 1; /* Reduce framerate to target */ if (fps_target > 0) { int delay = 0; if (fps_ema > 0) delay = (1000/fps_target) - (1000/fps_ema); if (delay > 0) SDL_Delay(delay); } } }
int main(int argc, char *argv[]) { int r; char *data_file = NULL; char *save_file = NULL; int map_generator = 0; log_set_file(stdout); init_config_data(); //- read config data int screen_width = get_config_int(CONFIG_SCREEN_WIDTH, DEFAULT_SCREEN_WIDTH); int screen_height = get_config_int(CONFIG_SCREEN_HEIGHT, DEFAULT_SCREEN_HEIGHT); int fullscreen = get_config_bool(CONFIG_FULLSCREEN, 0); int log_level = get_config_int(CONFIG_LOGGLEVEL, DEFAULT_LOG_LEVEL); enum_lng_t language = str_to_lagEnum((char *)get_config_string(CONFIG_LANGUAGE, "EN")); int play_midi = get_config_bool(CONFIG_MUSIC, 1); int play_SFX = get_config_bool(CONFIG_SFX, 1); int volume = get_config_int(CONFIG_VOLUME, 75); //- read command line parameters int opt; while (1) { opt = getopt(argc, argv, "d:fg:hl:r:t:s:"); if (opt < 0) break; switch (opt) { case 'd': { int d = atoi(optarg); if (d >= 0 && d < LOG_LEVEL_MAX) { log_level = d; } } break; case 'f': fullscreen = 1; break; case 'g': data_file = (char *)malloc(strlen(optarg)+1); if (data_file == NULL) exit(EXIT_FAILURE); strcpy(data_file, optarg); break; case 'h': fprintf(stdout, HELP, argv[0]); exit(EXIT_SUCCESS); break; case 'l': save_file = (char *)malloc(strlen(optarg)+1); if (save_file == NULL) exit(EXIT_FAILURE); strcpy(save_file, optarg); break; case 'r': { char *hstr = strchr(optarg, 'x'); if (hstr == NULL) { fprintf(stderr, USAGE, argv[0]); exit(EXIT_FAILURE); } screen_width = atoi(optarg); screen_height = atoi(hstr+1); } break; case 't': map_generator = atoi(optarg); break; case 's': { char * tmp_language_str = (char *) malloc(strlen(optarg) + 1); if (tmp_language_str != NULL) { strcpy(tmp_language_str, optarg); language = str_to_lagEnum(tmp_language_str); } } break; default: fprintf(stderr, USAGE, argv[0]); exit(EXIT_FAILURE); break; } } /* Set up logging */ log_set_level((log_level_t)log_level); LOGI("main", "freeserf %s", FREESERF_VERSION); /* load language */ init_language_data(language); r = load_data_file(data_file); if (r < 0) { LOGE("main", "Could not load game data."); exit(EXIT_FAILURE); } free(data_file); gfx_data_fixup(); LOGI("main", "SDL init..."); r = sdl_init(); if (r < 0) exit(EXIT_FAILURE); /* TODO move to right place */ midi_play_track(MIDI_TRACK_0); audio_set_volume(volume); midi_enable(play_midi); sfx_enable(play_SFX); /*gfx_set_palette(DATA_PALETTE_INTRO);*/ gfx_set_palette(DATA_PALETTE_GAME); LOGI("main", "SDL resolution %ix%i...", screen_width, screen_height); r = sdl_set_resolution(screen_width, screen_height, fullscreen); if (r < 0) exit(EXIT_FAILURE); game.map_generator = map_generator; /* Initialize global lookup tables */ init_spiral_pattern(); game_init(); /* Initialize Missions*/ init_mission("missions.xml"); /* Initialize interface */ interface_init(&interface); gui_object_set_size((gui_object_t *)&interface, screen_width, screen_height); gui_object_set_displayed((gui_object_t *)&interface, 1); /* Either load a save game if specified or start a new game. */ if (save_file != NULL) { int r = game_load_save_game(save_file); if (r < 0) exit(EXIT_FAILURE); free(save_file); interface_set_player(&interface, 0); } else { int r = game_load_random_map(3, &interface.random); if (r < 0) exit(EXIT_FAILURE); /* Add default player */ r = game_add_player(12, 64, 40, 40, 40); if (r < 0) exit(EXIT_FAILURE); interface_set_player(&interface, r); } viewport_map_reinit(); if (save_file != NULL) { interface_close_game_init(&interface); } /* Start game loop */ game_loop(); LOGI("main", "Cleaning up..."); /* Clean up */ map_deinit(); viewport_map_deinit(); audio_cleanup(); sdl_deinit(); gfx_unload(); language_cleanup(); mission_cleanup(); config_cleanup(); return EXIT_SUCCESS; }