void Pi::MainLoop() { StartGame(); Uint32 last_stats = SDL_GetTicks(); int frame_stat = 0; int phys_stat = 0; char fps_readout[128]; double time_player_died = 0; #ifdef MAKING_VIDEO Uint32 last_screendump = SDL_GetTicks(); int dumpnum = 0; #endif /* MAKING_VIDEO */ double currentTime = 0.001 * (double)SDL_GetTicks(); double accumulator = Pi::GetTimeStep(); Pi::gameTickAlpha = 0; memset(fps_readout, 0, sizeof(fps_readout)); while (isGameStarted) { double newTime = 0.001 * (double)SDL_GetTicks(); Pi::frameTime = newTime - currentTime; if (Pi::frameTime > 0.25) Pi::frameTime = 0.25; currentTime = newTime; accumulator += Pi::frameTime * GetTimeAccel(); const float step = Pi::GetTimeStep(); if (step) { while (accumulator >= step) { Space::TimeStep(step); gameTime += step; phys_stat++; accumulator -= step; } Pi::gameTickAlpha = accumulator / step; } else { // paused } if (frame_stat == 0) { // called not more than once per second PiLuaModules::UpdateOncePerRealtimeSecond(); } frame_stat++; Render::PrepareFrame(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Calculate position for this rendered frame (interpolated between two physics ticks */ for (std::list<Body*>::iterator i = Space::bodies.begin(); i != Space::bodies.end(); ++i) { (*i)->UpdateInterpolatedTransform(Pi::GetGameTickAlpha()); } Space::rootFrame->UpdateInterpolatedTransform(Pi::GetGameTickAlpha()); currentView->Draw3D(); // XXX HandleEvents at the moment must be after view->Draw3D and before // Gui::Draw so that labels drawn to screen can have mouse events correctly // detected. Gui::Draw wipes memory of label positions. Pi::HandleEvents(); // hide cursor for ship control. if (Pi::MouseButtonState(3)) { SDL_ShowCursor(0); SDL_WM_GrabInput(SDL_GRAB_ON); // SDL_SetRelativeMouseMode(true); } else { SDL_ShowCursor(1); SDL_WM_GrabInput(SDL_GRAB_OFF); // SDL_SetRelativeMouseMode(false); } Render::PostProcess(); Gui::Draw(); //#ifdef DEBUG if (Pi::showDebugInfo) { Gui::Screen::EnterOrtho(); glColor3f(1,1,1); Gui::Screen::RenderString(fps_readout, 0, 0); Gui::Screen::LeaveOrtho(); } //#endif /* DEBUG */ glError(); Render::SwapBuffers(); //if (glGetError()) printf ("GL: %s\n", gluErrorString (glGetError ())); int timeAccel = Pi::requestedTimeAccelIdx; if (Pi::player->GetFlightState() == Ship::FLYING) { // check we aren't too near to objects for timeaccel // /* for (std::list<Body*>::iterator i = Space::bodies.begin(); i != Space::bodies.end(); ++i) { if ((*i) == Pi::player) continue; if ((*i)->IsType(Object::HYPERSPACECLOUD)) continue; vector3d toBody = Pi::player->GetPosition() - (*i)->GetPositionRelTo(Pi::player->GetFrame()); double dist = toBody.Length(); double rad = (*i)->GetBoundingRadius(); if (dist < 1000.0) { timeAccel = std::min(timeAccel, 1); } else if (dist < std::min(rad+0.0001*AU, rad*1.1)) { timeAccel = std::min(timeAccel, 2); } else if (dist < std::min(rad+0.001*AU, rad*5.0)) { timeAccel = std::min(timeAccel, 3); } else if (dist < std::min(rad+0.01*AU,rad*10.0)) { timeAccel = std::min(timeAccel, 4); } else if (dist < std::min(rad+0.1*AU, rad*1000.0)) { timeAccel = std::min(timeAccel, 5); } } */ } if (timeAccel != Pi::GetTimeAccelIdx()) { Pi::SetTimeAccel(timeAccel); accumulator = 0; // fix for huge pauses 10000x -> 1x } // fuckadoodledoo, did the player die? if (Pi::player->IsDead()) { if (time_player_died) { if (Pi::GetGameTime() - time_player_died > 8.0) { Sound::DestroyAllEvents(); Pi::TombStoneLoop(); break; } } else { Pi::SetTimeAccel(1); Pi::cpan->HideAll(); Pi::SetView(static_cast<View*>(Pi::worldView)); Pi::player->Disable(); time_player_died = Pi::GetGameTime(); } } else { // this is something we need not do every turn... AmbientSounds::Update(); StarSystem::ShrinkCache(); } cpan->Update(); currentView->Update(); if (SDL_GetTicks() - last_stats > 1000) { Pi::statSceneTris += LmrModelGetStatsTris(); snprintf(fps_readout, sizeof(fps_readout), "%d fps, %d phys updates, %d triangles, %.3f M tris/sec", frame_stat, phys_stat, Pi::statSceneTris, Pi::statSceneTris*frame_stat*1e-6); frame_stat = 0; phys_stat = 0; last_stats += 1000; } Pi::statSceneTris = 0; LmrModelClearStatsTris(); #ifdef MAKING_VIDEO if (SDL_GetTicks() - last_screendump > 50) { last_screendump = SDL_GetTicks(); char buf[256]; snprintf(buf, sizeof(buf), "screenshot%08d.tga", dumpnum++); Screendump(buf); } #endif /* MAKING_VIDEO */ } }
void Pi::MainLoop() { double time_player_died = 0; #ifdef MAKING_VIDEO Uint32 last_screendump = SDL_GetTicks(); int dumpnum = 0; #endif /* MAKING_VIDEO */ #if WITH_DEVKEYS Uint32 last_stats = SDL_GetTicks(); int frame_stat = 0; int phys_stat = 0; char fps_readout[256]; memset(fps_readout, 0, sizeof(fps_readout)); #endif int MAX_PHYSICS_TICKS = Pi::config->Int("MaxPhysicsCyclesPerRender"); if (MAX_PHYSICS_TICKS <= 0) MAX_PHYSICS_TICKS = 4; double currentTime = 0.001 * double(SDL_GetTicks()); double accumulator = Pi::game->GetTimeStep(); Pi::gameTickAlpha = 0; while (Pi::game) { double newTime = 0.001 * double(SDL_GetTicks()); Pi::frameTime = newTime - currentTime; if (Pi::frameTime > 0.25) Pi::frameTime = 0.25; currentTime = newTime; accumulator += Pi::frameTime * Pi::game->GetTimeAccelRate(); const float step = Pi::game->GetTimeStep(); if (step > 0.0f) { int phys_ticks = 0; while (accumulator >= step) { if (++phys_ticks >= MAX_PHYSICS_TICKS) { accumulator = 0.0; break; } game->TimeStep(step); accumulator -= step; } // rendering interpolation between frames: don't use when docked int pstate = Pi::game->GetPlayer()->GetFlightState(); if (pstate == Ship::DOCKED || pstate == Ship::DOCKING) Pi::gameTickAlpha = 1.0; else Pi::gameTickAlpha = accumulator / step; #if WITH_DEVKEYS phys_stat += phys_ticks; #endif } else { // paused } frame_stat++; // fuckadoodledoo, did the player die? if (Pi::player->IsDead()) { if (time_player_died > 0.0) { if (Pi::game->GetTime() - time_player_died > 8.0) { Pi::SetView(0); Pi::TombStoneLoop(); Pi::EndGame(); break; } } else { Pi::game->SetTimeAccel(Game::TIMEACCEL_1X); Pi::deathView->Init(); Pi::SetView(Pi::deathView); time_player_died = Pi::game->GetTime(); } } Pi::renderer->BeginFrame(); Pi::renderer->SetTransform(matrix4x4f::Identity()); /* Calculate position for this rendered frame (interpolated between two physics ticks */ // XXX should this be here? what is this anyway? for (Space::BodyIterator i = game->GetSpace()->BodiesBegin(); i != game->GetSpace()->BodiesEnd(); ++i) { (*i)->UpdateInterpTransform(Pi::GetGameTickAlpha()); } game->GetSpace()->GetRootFrame()->UpdateInterpTransform(Pi::GetGameTickAlpha()); currentView->Update(); currentView->Draw3D(); // XXX HandleEvents at the moment must be after view->Draw3D and before // Gui::Draw so that labels drawn to screen can have mouse events correctly // detected. Gui::Draw wipes memory of label positions. Pi::HandleEvents(); // hide cursor for ship control. SetMouseGrab(Pi::MouseButtonState(SDL_BUTTON_RIGHT)); Pi::renderer->EndFrame(); Gui::Draw(); #if WITH_DEVKEYS if (Pi::showDebugInfo) { Gui::Screen::EnterOrtho(); Gui::Screen::PushFont("ConsoleFont"); Gui::Screen::RenderString(fps_readout, 0, 0); Gui::Screen::PopFont(); Gui::Screen::LeaveOrtho(); } #endif Pi::renderer->SwapBuffers(); // game exit or failed load from GameMenuView will have cleared // Pi::game. we can't continue. if (!Pi::game) return; if (Pi::game->UpdateTimeAccel()) accumulator = 0; // fix for huge pauses 10000x -> 1x if (!Pi::player->IsDead()) { // XXX should this really be limited to while the player is alive? // this is something we need not do every turn... if (!config->Int("DisableSound")) AmbientSounds::Update(); StarSystem::ShrinkCache(); } cpan->Update(); musicPlayer.Update(); #if WITH_DEVKEYS if (Pi::showDebugInfo && SDL_GetTicks() - last_stats > 1000) { size_t lua_mem = Lua::manager->GetMemoryUsage(); int lua_memB = int(lua_mem & ((1u << 10) - 1)); int lua_memKB = int(lua_mem >> 10) % 1024; int lua_memMB = int(lua_mem >> 20); Pi::statSceneTris += LmrModelGetStatsTris(); snprintf( fps_readout, sizeof(fps_readout), "%d fps (%.1f ms/f), %d phys updates, %d triangles, %.3f M tris/sec, %d terrain vtx/sec, %d glyphs/sec\n" "Lua mem usage: %d MB + %d KB + %d bytes", frame_stat, (1000.0/frame_stat), phys_stat, Pi::statSceneTris, Pi::statSceneTris*frame_stat*1e-6, GeoSphere::GetVtxGenCount(), Text::TextureFont::GetGlyphCount(), lua_memMB, lua_memKB, lua_memB ); frame_stat = 0; phys_stat = 0; Text::TextureFont::ClearGlyphCount(); GeoSphere::ClearVtxGenCount(); if (SDL_GetTicks() - last_stats > 1200) last_stats = SDL_GetTicks(); else last_stats += 1000; } Pi::statSceneTris = 0; LmrModelClearStatsTris(); #endif #ifdef MAKING_VIDEO if (SDL_GetTicks() - last_screendump > 50) { last_screendump = SDL_GetTicks(); std::string fname = stringf(Lang::SCREENSHOT_FILENAME_TEMPLATE, formatarg("index", dumpnum++)); Screendump(fname.c_str(), GetScrWidth(), GetScrHeight()); } #endif /* MAKING_VIDEO */ }
void Pi::HandleEvents() { SDL_Event event; Pi::mouseMotion[0] = Pi::mouseMotion[1] = 0; while (SDL_PollEvent(&event)) { Gui::HandleSDLEvent(&event); switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { // only accessible once game started if (currentView != 0) { if (currentView != gameMenuView) { RequestTimeAccel(0); SetTimeAccel(0); SetView(gameMenuView); } else RequestTimeAccel(1); } break; } // special keys. LCTRL+turd if ((KeyState(SDLK_LCTRL) || (KeyState(SDLK_RCTRL)))) { if (event.key.keysym.sym == SDLK_q) Pi::Quit(); if (event.key.keysym.sym == SDLK_s) { Render::ToggleShaders(); } if (event.key.keysym.sym == SDLK_h) { Render::ToggleHDR(); } if (event.key.keysym.sym == SDLK_i) Pi::showDebugInfo = !Pi::showDebugInfo; if (event.key.keysym.sym == SDLK_p) { Sint64 crime, fine; Polit::GetCrime(&crime, &fine); printf("Criminal record: %llx, $%lld\n", crime, fine); Polit::AddCrime(0x1, 100); Polit::GetCrime(&crime, &fine); printf("Criminal record now: %llx, $%lld\n", crime, fine); } if (event.key.keysym.sym == SDLK_PRINT) { char buf[256]; const time_t t = time(0); struct tm *_tm = localtime(&t); strftime(buf, sizeof(buf), "screenshot-%Y%m%d-%H%M%S.tga", _tm); Screendump(buf); fprintf(stderr, "Screendump to %s\n", buf); } #ifdef DEBUG if (event.key.keysym.sym == SDLK_m) { Pi::player->SetMoney(Pi::player->GetMoney() + 10000000); } if (event.key.keysym.sym == SDLK_F12) { matrix4x4d m; Pi::player->GetRotMatrix(m); vector3d dir = m*vector3d(0,0,-1); /* add test object */ if (KeyState(SDLK_RSHIFT)) { Missile *missile = new Missile(ShipType::MISSILE_GUIDED, Pi::player, Pi::player->GetCombatTarget()); missile->SetRotMatrix(m); missile->SetFrame(Pi::player->GetFrame()); missile->SetPosition(Pi::player->GetPosition()+50.0*dir); missile->SetVelocity(Pi::player->GetVelocity()); Space::AddBody(missile); } else if (KeyState(SDLK_LSHIFT)) { SpaceStation *s = static_cast<SpaceStation*>(Pi::player->GetNavTarget()); if (s) { int port = s->GetFreeDockingPort(); if (port != -1) { printf("Putting ship into station\n"); // Make police ship intent on killing the player Ship *ship = new Ship(ShipType::LADYBIRD); ship->AIKill(Pi::player); ship->SetFrame(Pi::player->GetFrame()); ship->SetDockedWith(s, port); Space::AddBody(ship); } else { printf("No docking ports free dude\n"); } } else { printf("Select a space station...\n"); } } else { Ship *ship = new Ship(ShipType::LADYBIRD); ship->m_equipment.Set(Equip::SLOT_LASER, 0, Equip::PULSECANNON_1MW); ship->AIKill(Pi::player); ship->SetFrame(Pi::player->GetFrame()); ship->SetPosition(Pi::player->GetPosition()+100.0*dir); ship->SetVelocity(Pi::player->GetVelocity()); ship->m_equipment.Add(Equip::DRIVE_CLASS2); ship->m_equipment.Add(Equip::RADAR_MAPPER); ship->m_equipment.Add(Equip::SCANNER); ship->m_equipment.Add(Equip::SHIELD_GENERATOR); ship->m_equipment.Add(Equip::HYDROGEN, 10); Space::AddBody(ship); } } #endif /* DEBUG */ // XXX only works on X11 //if (event.key.keysym.sym == SDLK_F11) SDL_WM_ToggleFullScreen(Pi::scrSurface); if (event.key.keysym.sym == SDLK_F10) Pi::SetView(Pi::objectViewerView); if (event.key.keysym.sym == SDLK_F9) { std::string name = join_path(GetFullSavefileDirPath().c_str(), "_quicksave", 0); Serializer::SaveGame(name.c_str()); Pi::cpan->MsgLog()->Message("", "Game saved to "+name); } } Pi::keyState[event.key.keysym.sym] = 1; Pi::keyModState = event.key.keysym.mod; Pi::onKeyPress.emit(&event.key.keysym); break; case SDL_KEYUP: Pi::keyState[event.key.keysym.sym] = 0; Pi::keyModState = event.key.keysym.mod; Pi::onKeyRelease.emit(&event.key.keysym); break; case SDL_MOUSEBUTTONDOWN: Pi::mouseButton[event.button.button] = 1; Pi::onMouseButtonDown.emit(event.button.button, event.button.x, event.button.y); break; case SDL_MOUSEBUTTONUP: Pi::mouseButton[event.button.button] = 0; Pi::onMouseButtonUp.emit(event.button.button, event.button.x, event.button.y); break; case SDL_MOUSEMOTION: Pi::mouseMotion[0] += event.motion.xrel; Pi::mouseMotion[1] += event.motion.yrel; // SDL_GetRelativeMouseState(&Pi::mouseMotion[0], &Pi::mouseMotion[1]); break; case SDL_JOYAXISMOTION: if (joysticks[event.jaxis.which].joystick == NULL) break; if (event.jaxis.value == -32768) joysticks[event.jaxis.which].axes[event.jaxis.axis] = 1.f; else joysticks[event.jaxis.which].axes[event.jaxis.axis] = -event.jaxis.value / 32767.f; break; case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: if (joysticks[event.jaxis.which].joystick == NULL) break; joysticks[event.jbutton.which].buttons[event.jbutton.button] = event.jbutton.state != 0; break; case SDL_JOYHATMOTION: if (joysticks[event.jaxis.which].joystick == NULL) break; joysticks[event.jhat.which].hats[event.jhat.hat] = event.jhat.value; break; case SDL_QUIT: Pi::Quit(); break; } } }
void Pi::HandleEvents() { SDL_Event event; Pi::mouseMotion[0] = Pi::mouseMotion[1] = 0; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { if (Pi::game) Pi::EndGame(); Pi::Quit(); } else if (ui->DispatchSDLEvent(event)) continue; Gui::HandleSDLEvent(&event); if (!Pi::IsConsoleActive()) KeyBindings::DispatchSDLEvent(&event); else KeyBindings::toggleLuaConsole.CheckSDLEventAndDispatch(&event); switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { if (Pi::game) { // only accessible once game started if (currentView != 0) { if (currentView != gameMenuView) { Pi::game->SetTimeAccel(Game::TIMEACCEL_PAUSED); SetView(gameMenuView); } else { Pi::game->RequestTimeAccel(Game::TIMEACCEL_1X); SetView(Pi::player->IsDead() ? static_cast<View*>(deathView) : static_cast<View*>(worldView)); } } } break; } // special keys. LCTRL+turd if ((KeyState(SDLK_LCTRL) || (KeyState(SDLK_RCTRL)))) { switch (event.key.keysym.sym) { case SDLK_q: // Quit if (Pi::game) Pi::EndGame(); Pi::Quit(); break; case SDLK_PRINT: // print case SDLK_KP_MULTIPLY: // screen { char buf[256]; const time_t t = time(0); struct tm *_tm = localtime(&t); strftime(buf, sizeof(buf), "screenshot-%Y%m%d-%H%M%S.png", _tm); Screendump(buf, Graphics::GetScreenWidth(), Graphics::GetScreenHeight()); break; } #if WITH_DEVKEYS case SDLK_i: // Toggle Debug info Pi::showDebugInfo = !Pi::showDebugInfo; break; case SDLK_m: // Gimme money! if(Pi::game) { Pi::player->SetMoney(Pi::player->GetMoney() + 10000000); } break; case SDLK_F12: { if(Pi::game) { vector3d dir = -Pi::player->GetOrient().VectorZ(); /* add test object */ if (KeyState(SDLK_RSHIFT)) { Missile *missile = new Missile(ShipType::MISSILE_GUIDED, Pi::player, Pi::player->GetCombatTarget()); missile->SetOrient(Pi::player->GetOrient()); missile->SetFrame(Pi::player->GetFrame()); missile->SetPosition(Pi::player->GetPosition()+50.0*dir); missile->SetVelocity(Pi::player->GetVelocity()); game->GetSpace()->AddBody(missile); } else if (KeyState(SDLK_LSHIFT)) { SpaceStation *s = static_cast<SpaceStation*>(Pi::player->GetNavTarget()); if (s) { int port = s->GetFreeDockingPort(); if (port != -1) { printf("Putting ship into station\n"); // Make police ship intent on killing the player Ship *ship = new Ship(ShipType::LADYBIRD); ship->AIKill(Pi::player); ship->SetFrame(Pi::player->GetFrame()); ship->SetDockedWith(s, port); game->GetSpace()->AddBody(ship); } else { printf("No docking ports free dude\n"); } } else { printf("Select a space station...\n"); } } else { Ship *ship = new Ship(ShipType::LADYBIRD); ship->m_equipment.Set(Equip::SLOT_LASER, 0, Equip::PULSECANNON_1MW); ship->AIKill(Pi::player); ship->SetFrame(Pi::player->GetFrame()); ship->SetPosition(Pi::player->GetPosition()+100.0*dir); ship->SetVelocity(Pi::player->GetVelocity()); ship->m_equipment.Add(Equip::DRIVE_CLASS2); ship->m_equipment.Add(Equip::RADAR_MAPPER); ship->m_equipment.Add(Equip::SCANNER); ship->m_equipment.Add(Equip::SHIELD_GENERATOR); ship->m_equipment.Add(Equip::HYDROGEN, 10); ship->UpdateStats(); game->GetSpace()->AddBody(ship); } } break; } #endif /* DEVKEYS */ #if WITH_OBJECTVIEWER case SDLK_F10: Pi::SetView(Pi::objectViewerView); break; #endif case SDLK_F11: // XXX only works on X11 //SDL_WM_ToggleFullScreen(Pi::scrSurface); #if WITH_DEVKEYS renderer->ReloadShaders(); #endif break; case SDLK_F9: // Quicksave { if(Pi::game) { if (Pi::game->IsHyperspace()) Pi::cpan->MsgLog()->Message("", Lang::CANT_SAVE_IN_HYPERSPACE); else { const std::string name = "_quicksave"; const std::string path = FileSystem::JoinPath(GetSaveDir(), name); try { Game::SaveGame(name, Pi::game); Pi::cpan->MsgLog()->Message("", Lang::GAME_SAVED_TO + path); } catch (CouldNotOpenFileException) { Pi::cpan->MsgLog()->Message("", stringf(Lang::COULD_NOT_OPEN_FILENAME, formatarg("path", path))); } catch (CouldNotWriteToFileException) { Pi::cpan->MsgLog()->Message("", Lang::GAME_SAVE_CANNOT_WRITE); } } } break; } default: break; // This does nothing but it stops the compiler warnings } } Pi::keyState[event.key.keysym.sym] = 1; Pi::keyModState = event.key.keysym.mod; Pi::onKeyPress.emit(&event.key.keysym); break; case SDL_KEYUP: Pi::keyState[event.key.keysym.sym] = 0; Pi::keyModState = event.key.keysym.mod; Pi::onKeyRelease.emit(&event.key.keysym); break; case SDL_MOUSEBUTTONDOWN: if (event.button.button < COUNTOF(Pi::mouseButton)) { Pi::mouseButton[event.button.button] = 1; Pi::onMouseButtonDown.emit(event.button.button, event.button.x, event.button.y); } break; case SDL_MOUSEBUTTONUP: if (event.button.button < COUNTOF(Pi::mouseButton)) { Pi::mouseButton[event.button.button] = 0; Pi::onMouseButtonUp.emit(event.button.button, event.button.x, event.button.y); } break; case SDL_MOUSEMOTION: Pi::mouseMotion[0] += event.motion.xrel; Pi::mouseMotion[1] += event.motion.yrel; // SDL_GetRelativeMouseState(&Pi::mouseMotion[0], &Pi::mouseMotion[1]); break; case SDL_JOYAXISMOTION: if (joysticks[event.jaxis.which].joystick == NULL) break; if (event.jaxis.value == -32768) joysticks[event.jaxis.which].axes[event.jaxis.axis] = 1.f; else joysticks[event.jaxis.which].axes[event.jaxis.axis] = -event.jaxis.value / 32767.f; break; case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: if (joysticks[event.jaxis.which].joystick == NULL) break; joysticks[event.jbutton.which].buttons[event.jbutton.button] = event.jbutton.state != 0; break; case SDL_JOYHATMOTION: if (joysticks[event.jaxis.which].joystick == NULL) break; joysticks[event.jhat.which].hats[event.jhat.hat] = event.jhat.value; break; } } }