3#include <Tempest/Except>
4#include <Tempest/Painter>
6#include <Tempest/Brush>
8#include <Tempest/Layout>
9#include <Tempest/Application>
29using namespace Tempest;
32 : Window(Maximized),device(device),swapchain(device,hwnd()),
33 atlas(device),renderer(swapchain),
34 rootMenu(keycodec),inventory(keycodec),
35 dialogs(inventory),document(keycodec),
37#if defined(__MOBILE_PLATFORM__)
40 player(dialogs,inventory) {
45 setWindowTitle(
"Gothic II");
else
46 setWindowTitle(
"Gothic");
88 funcKey[2] = Shortcut(*
this,Event::M_NoModifier,Event::K_F2);
89 funcKey[2].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F2>);
91 funcKey[3] = Shortcut(*
this,Event::M_NoModifier,Event::K_F3);
92 funcKey[3].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F3>);
94 funcKey[4] = Shortcut(*
this,Event::M_NoModifier,Event::K_F4);
95 funcKey[4].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F4>);
97 funcKey[5] = Shortcut(*
this,Event::M_NoModifier,Event::K_F5);
98 funcKey[5].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F5>);
100 funcKey[6] = Shortcut(*
this,Event::M_NoModifier,Event::K_F6);
101 funcKey[6].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F6>);
103 funcKey[7] = Shortcut(*
this,Event::M_NoModifier,Event::K_F7);
104 funcKey[7].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F7>);
106 funcKey[9] = Shortcut(*
this,Event::M_NoModifier,Event::K_F9);
107 funcKey[9].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F9>);
109 funcKey[10] = Shortcut(*
this,Event::M_NoModifier,Event::K_F10);
110 funcKey[10].onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_F10>);
112 displayPos = Shortcut(*
this,Event::M_Alt,Event::K_P);
113 displayPos.onActivated.bind(
this, &MainWindow::onMarvinKey<Event::K_P>);
120 takeWidget(&dialogs);
121 takeWidget(&inventory);
122 takeWidget(&chapter);
123 takeWidget(&document);
125 takeWidget(&rootMenu);
126#if defined(__MOBILE_PLATFORM__)
127 takeWidget(&mobileUi);
135 return SystemApi::uiScale(hwnd());
138void MainWindow::setupUi() {
140 addWidget(&document);
142 addWidget(&inventory);
145 addWidget(&rootMenu);
146#if defined(__MOBILE_PLATFORM__)
147 addWidget(&mobileUi);
162void MainWindow::paintEvent(PaintEvent& event) {
171 if(world==
nullptr && !background.isEmpty()) {
172 p.setBrush(Color(0.0));
173 p.drawRect(0,0,w(),h());
176 p.setBrush(Brush(background,Painter::NoBlend));
177 p.drawRect(0,0,w(),h(),
178 0,0,background.w(),background.h());
183 world->globalFx()->scrBlend(p,Rect(0,0,w(),h()));
191 p.setBrush(Brush(*back,Painter::NoBlend));
192 p.drawRect(0,0,this->w(),this->h(),
193 0,0,back->w(),back->h());
195 if(loadBox!=
nullptr && !loadBox->isEmpty()) {
197 int lw = int(w()*0.5);
198 int lh = int(h()*0.05);
199 drawLoading(p,(w()-lw)/2,
int(h()*0.75), lw, lh);
201 drawLoading(p,
int(w()*0.92)-loadBox->w(),
int(h()*0.12), loadBox->w(),loadBox->h());
206 if(world!=
nullptr && world->view()){
210 p.setBrush(Color(1.0));
214 auto focus = world->validateFocus(player.
focus());
215 paintFocus(p,focus,vp);
223 bool showHealthBar = opt.showHealthBar;
224 bool showManaBar = (opt.showManaBar==2) || (opt.showManaBar==1 && (pl->weaponState()==
WeaponState::Mage || inventory.
isActive()));
225 bool showSwimBar = (opt.showSwimBar==2) || (opt.showSwimBar==1 && pl->isDive());
228 drawBar(p,barHp, 10, h()-10, hp, AlignLeft | AlignBottom);
230 drawBar(p,barMana, w()-10, h()-10, mp, AlignRight | AlignBottom);
232 uint32_t gl = pl->guild();
233 auto v = float(pl->world().script().guildVal().dive_time[gl]);
235 auto t = float(pl->diveTime())/1000.f;
236 drawBar(p,barMisc,w()/2,h()-10, (v-t)/(v), AlignHCenter | AlignBottom);
248 world->marchPoints(dbg);
249 world->marchInteractives(dbg);
250 world->view()->dbgLights(dbg);
251 world->marchCsCameras(dbg);
260 std::snprintf(fpsT,
sizeof(fpsT),
"fps = %.2f",fps.get());
263 fnt.drawText(p,5,fnt.pixelSize()+5,fpsT);
268 auto hour = world->time().hour();
269 auto min = world->time().minute();
272 fnt.drawText(p,w()-fnt.textSize(clockT).w-5,fnt.pixelSize()+5,clockT);
277 wx->dbgClusters(p, Vec2(
float(w()),
float(h())));
281void MainWindow::resizeEvent(SizeEvent&) {
287 camera->setViewport(swapchain.w(),swapchain.h());
289 const bool fs = SystemApi::isFullscreen(hwnd());
290 auto rect = SystemApi::windowClientRect(hwnd());
291 setCursorPosition(rect.w/2,rect.h/2);
292 setCursorShape(fs ? CursorShape::Hidden : CursorShape::Arrow);
296void MainWindow::mouseDownEvent(MouseEvent &event) {
297 if(event.button<
sizeof(mouseP))
298 mouseP[
event.button]=
true;
302void MainWindow::mouseUpEvent(MouseEvent &event) {
304 if(event.button<
sizeof(mouseP))
305 mouseP[
event.button]=
false;
308void MainWindow::mouseDragEvent(MouseEvent &event) {
309 const bool fs = SystemApi::isFullscreen(hwnd());
310 if(!mouseP[Event::ButtonLeft] && !fs)
314 processMouse(event,
true);
317void MainWindow::mouseMoveEvent(MouseEvent &event) {
318 processMouse(event,SystemApi::isFullscreen(hwnd()));
321void MainWindow::processMouse(MouseEvent& event,
bool enable) {
322 auto center = Point(w()/2,h()/2);
323 if(enable && event.pos()!=center && hasFocus()) {
324 dMouse += (
event.pos()-center);
325 setCursorPosition(center);
329void MainWindow::tickMouse() {
344 PointF dpScaled = PointF(
float(dMouse.x)*mouseSensitivity,
float(dMouse.y)*mouseSensitivity);
345 dpScaled.x/=float(w());
346 dpScaled.y/=float(h());
350 if(camLookaroundInverse)
353 camera->onRotateMouse(PointF(dpScaled.y,-dpScaled.x));
362void MainWindow::onSettings() {
367 maxFpsInv = 1000u/uint64_t(zMaxFps);
else
371void MainWindow::mouseWheelEvent(MouseEvent &event) {
373 camera->changeZoom(event.delta);
376void MainWindow::keyDownEvent(KeyEvent &event) {
380 if(event.isAccepted()){
389 if(event.isAccepted()){
398 if(event.isAccepted()){
407 if(event.isAccepted()){
416 if(event.isAccepted()){
425 if(event.isAccepted()){
432 auto act = keycodec.
tr(event);
433 auto mapping = keycodec.
mapping(event);
436 if(event.key==Event::K_F11) {
438 auto pm = device.readPixels(textureCast<const Texture2d&>(tex));
444void MainWindow::keyRepeatEvent(KeyEvent& event) {
446 if(event.isAccepted())
449 if(uiKeyUp==&rootMenu){
451 if(event.isAccepted())
454 if(uiKeyUp==&chapter){
455 if(event.isAccepted())
458 if(uiKeyUp==&document){
459 if(event.isAccepted())
462 if(uiKeyUp==&dialogs){
463 if(event.isAccepted())
466 if(uiKeyUp==&inventory){
468 if(event.isAccepted())
473void MainWindow::keyUpEvent(KeyEvent &event) {
476 if(event.isAccepted())
479 if(uiKeyUp==&rootMenu){
480 if(event.isAccepted())
483 if(uiKeyUp==&chapter){
485 if(event.isAccepted())
488 if(uiKeyUp==&document){
490 if(event.isAccepted())
493 if(uiKeyUp==&dialogs){
495 if(event.isAccepted())
498 if(uiKeyUp==&inventory){
500 if(event.isAccepted())
505 auto act = keycodec.
tr(event);
506 auto mapping = keycodec.
mapping(event);
508 std::string_view menuEv;
514 menuEv =
"MENU_STATUS";
516 if(!menuEv.empty()) {
536void MainWindow::focusEvent(FocusEvent &event) {
540 auto center = Point(w()/2,h()/2);
541 setCursorPosition(center);
544void MainWindow::paintFocus(Painter& p,
const Focus& focus,
const Matrix4x4& vp) {
550 auto pl = world==
nullptr ? nullptr : world->
player();
555 vp.project(pos.x,pos.y,pos.z);
557 int ix = int((0.5f*pos.x+0.5f)*
float(w()));
558 int iy = int((0.5f*pos.y+0.5f)*
float(h()));
571 drawBar(p,barHp, w()/2,10, hp, AlignHCenter|AlignTop);
575 if(focus.
npc!=
nullptr &&
576 (foc==1 || foc==3) &&
585 {b.
bbox[0].x,b.bbox[0].y,b.bbox[0].z},
586 {b.bbox[1].x,b.bbox[0].y,b.bbox[0].z},
587 {b.bbox[1].x,b.bbox[1].y,b.bbox[0].z},
588 {b.bbox[0].x,b.bbox[1].y,b.bbox[0].z},
589 {b.bbox[0].x,b.bbox[0].y,b.bbox[1].z},
590 {b.bbox[1].x,b.bbox[0].y,b.bbox[1].z},
591 {b.bbox[1].x,b.bbox[1].y,b.bbox[1].z},
592 {b.bbox[0].x,b.bbox[1].y,b.bbox[1].z},
595 int min[2]={ix,iy-20}, max[2]={ix,iy-20};
596 for(
int i=0; i<8; ++i) {
598 int x = int((bx[i].x*0.5f+0.5f)*
float(w()));
599 int y = int((bx[i].y*0.5f+0.5f)*
float(h()));
600 min[0] = std::min(x,min[0]);
601 min[1] = std::min(y,min[1]);
602 max[0] = std::max(x,max[0]);
603 max[1] = std::max(y,max[1]);
606 paintFocus(p,Rect(min[0],min[1],max[0]-min[0],max[1]-min[1]));
616void MainWindow::paintFocus(Painter& p, Rect rect) {
617 if(focusImg==
nullptr)
619 const int w2 = focusImg->w();
620 const int h2 = focusImg->h();
635 p.setBrush(Brush(*focusImg,Painter::Add));
636 p.drawRect(rect.x, rect.y, w,h, 0,0, w, h);
637 p.drawRect(rect.x+rect.w-w,rect.y, w,h, w,0, w2,h);
638 p.drawRect(rect.x, rect.y+rect.h-h,w,h, 0,h, w, h2);
639 p.drawRect(rect.x+rect.w-w,rect.y+rect.h-h,w,h, w,h, w2,h2);
642void MainWindow::drawBar(Painter &p,
const Tempest::Texture2d* bar,
int x,
int y,
float v, AlignFlag flg) {
643 if(barBack==
nullptr || bar==
nullptr)
646 const float destW = 200.f*scale*float(std::min(w(),800))/800.f;
647 const float k = float(destW)/float(std::max(barBack->w(),1));
648 const float destH = float(barBack->h())*k;
649 const float destHin = float(destH)*24.f/32.f;
652 v = std::max(0.f,std::min(v,1.f));
655 else if(flg & AlignHCenter)
657 if(flg & AlignBottom)
660 p.setBrush(*barBack);
661 p.drawRect(x,y,
int(destW),
int(destH), 0,0,barBack->w(),barBack->h());
663 int dy = int(0.5f*(destH-destHin));
666 p.drawRect(x+
int(pd),y+dy,
int(
float(destW-pd*2)*v),
int(destHin),
667 0,0,bar->w(),bar->h());
670void MainWindow::drawMsg(Tempest::Painter& p) {
672 const float destW = 200.f*scale*float(std::min(w(),800))/800.f;
673 const float k = float(destW)/float(std::max(barBack->w(),1));
674 const float destH = float(barBack->h())*k;
676 const int y = 10 + int(destH) + 10;
680void MainWindow::drawProgress(Painter &p,
int x,
int y,
int w,
int h,
float v) {
683 p.setBrush(*loadBox);
684 p.drawRect(x,y,w,h, 0,0,loadBox->w(),loadBox->h());
686 int paddL = int((
float(w)*75.f)/
float(loadBox->w()));
687 int paddT = int((
float(h)*10.f)/
float(loadBox->h()));
690 paddL = int((
float(w)*30.f)/
float(loadBox->w()));
691 paddT = int((
float(h)* 5.f)/
float(loadBox->h()));
694 p.setBrush(*loadVal);
695 p.drawRect(x+paddL,y+paddT,
int(
float(w-2*paddL)*v),h-2*paddT,
696 0,0,loadVal->w(),loadVal->h());
699void MainWindow::drawLoading(Painter &p,
int x,
int y,
int w,
int h) {
700 float v = float(
Gothic::inst().loadingProgress())/100.f;
701 drawProgress(p,x,y,w,h,v);
704void MainWindow::drawSaving(Painter &p) {
706 p.setBrush(Brush(*back,Painter::NoBlend));
707 p.drawRect(0,0,this->w(),this->h(),
708 0,0,back->w(),back->h());
711 if(saveback==
nullptr)
713 if(saveback==
nullptr)
720 if(szX<=460 || szY<=300) {
725 szX = int(
float(szX)*scale);
726 szY = int(
float(szY)*scale);
727 drawSaving(p,*saveback,szX,szY,scale);
730void MainWindow::drawSaving(Painter& p,
const Tempest::Texture2d& back,
int sw,
int sh,
float scale) {
731 const int x = (w()-sw)/2, y = (h()-sh)/2;
736 for(
int i=0;i<10;++i) {
737 p.drawRect(x,y,sw,sh, 0,0,back.w(),back.h());
740 float v = float(
Gothic::inst().loadingProgress())/100.f;
741 drawProgress(p, x+
int(100.f*scale), y+sh-
int(75.f*scale), sw-2*
int(100.f*scale),
int(40.f*scale), v);
744void MainWindow::isDialogClosed(
bool& ret) {
748template<Tempest::KeyEvent::KeyType k>
749void MainWindow::onMarvinKey() {
753 console.resize(w(),h());
754 console.setFocus(
true);
759 setFullscreen(!SystemApi::isFullscreen(hwnd()));
765 if(camera!=
nullptr && pl!=
nullptr) {
774 const bool debug =
false;
776 const bool debug =
true;
793 auto inter = pl!=
nullptr ? pl->
interactive() :
nullptr;
794 if(camera!=
nullptr && inter==
nullptr)
812 if(runtimeMode==R_Normal)
813 runtimeMode = R_Suspended;
else
814 runtimeMode = R_Normal;
816 else if(!
Gothic::inst().isPause() && useQuickSaveKeys) {
822 if(runtimeMode==R_Suspended)
823 runtimeMode = R_Step;
828 auto pos = p->position();
829 string_frm buf(
"Position: ", pos.x,
'/',pos.y,
'/',pos.z);
837uint64_t MainWindow::tick() {
838 auto time = Application::tickCount();
839 auto dt = time-lastTick;
872 if(runtimeMode==R_Step) {
873 runtimeMode = R_Suspended;
876 else if(runtimeMode==R_Suspended) {
878 if(camera!=
nullptr && camera->isFree()) {
901void MainWindow::updateAnimation(uint64_t dt) {
905void MainWindow::tickCamera(uint64_t dt) {
911 auto& camera = *pcamera;
916 auto pos = pl!=
nullptr ? pl->cameraBone(camera.isFirstPerson()) : Vec3();
918 if(!camera.isCutscene()) {
919 const bool fs = SystemApi::isFullscreen(hwnd());
920 if(!fs && mouseP[Event::ButtonLeft]) {
921 camera.setSpin(camera.destSpin());
922 camera.setDestPosition(pos);
928 camera.setDestPosition(pos);
930 else if(player.
focus().
npc!=
nullptr && meleeFocus && pl!=
nullptr) {
931 auto spin = camera.destSpin();
932 spin.y = pl->rotation();
933 camera.setDestSpin(spin);
934 camera.setDestPosition(pos);
936 else if(pl!=
nullptr) {
937 auto spin = camera.destSpin();
938 if(pl->interactive()==
nullptr && !pl->isDown())
939 spin.y = pl->rotation();
940 if(pl->isDive() && !camera.isMarvin())
941 spin.x = -pl->rotationY();
942 camera.setDestSpin(spin);
943 camera.setDestPosition(pos);
949 if(camera.isToggleEnabled() && !camera.isCutscene())
950 camera.setMode(solveCameraMode());
965 if(pl->interactive()!=
nullptr)
979 if(pl->isFallingDeep())
982 switch(pl->weaponState()){
1000void MainWindow::startGame(std::string_view slot) {
1004 setGameImpl(
nullptr);
1007 Gothic::inst().
startLoad(
"LOADING.TGA",[slot=std::string(slot)](std::unique_ptr<GameSession>&& game){
1009 std::unique_ptr<GameSession> w(
new GameSession(slot));
1013 background = Texture2d();
1017void MainWindow::loadGame(std::string_view slot) {
1019 setGameImpl(
nullptr);
1023 Gothic::inst().
startLoad(
"LOADING.TGA",[slot=std::string(slot)](std::unique_ptr<GameSession>&& game){
1025 Tempest::RFile file(slot);
1027 std::unique_ptr<GameSession> w(
new GameSession(s));
1031 background = Texture2d();
1035void MainWindow::saveGame(std::string_view slot, std::string_view name) {
1037 auto pm = device.readPixels(textureCast<const Texture2d&>(tex));
1041 if(
auto w =
Gothic::inst().world(); w!=
nullptr && w->currentCs()!=
nullptr)
1044 Gothic::inst().
startSave(std::move(textureCast<Texture2d&>(tex)),[slot=std::string(slot),name=std::string(name),pm](std::unique_ptr<GameSession>&& game){
1046 return std::move(game);
1048 Tempest::WFile f(slot);
1050 game->save(s,name,pm);
1054 return std::move(game);
1060void MainWindow::onVideo(std::string_view fname) {
1066void MainWindow::onStartLoading() {
1072void MainWindow::onWorldLoaded() {
1078 world->execTriggerEvent(evt);
1088 for(
auto& c:commands)
1089 c = device.commandBuffer();
1092 c->setViewport(uint32_t(w()),uint32_t(h()));
1098 lastTick = Application::tickCount();
1102void MainWindow::onSessionExit() {
1106void MainWindow::onBenchmarkFinished() {
1107 if(benchmark.numFrames==0)
1110 double fps = benchmark.fpsSum/double(benchmark.numFrames);
1113 for(
size_t i=0; i<benchmark.low1procent.size(); ++i) {
1114 auto v = benchmark.low1procent[i];
1117 low1 += 1000.0/double(v);
1120 low1 = num1>0 ? low1/double(num1) : 0.0;
1123 string_frm str(
"Benchmark: low 1% = ", low1,
" fps = ", fps);
1124 Log::i(str.c_str());
1128 Log::i(
"Exiting benchmark");
1129 Tempest::SystemApi::exit();
1133 console.setFocus(
true);
1137void MainWindow::setGameImpl(std::unique_ptr<GameSession> &&w) {
1141void MainWindow::clearInput() {
1143 std::memset(mouseP,0,
sizeof(mouseP));
1146void MainWindow::setFullscreen(
bool fs) {
1147 SystemApi::setAsFullscreen(hwnd(),fs);
1150void MainWindow::render(){
1152 static uint64_t time=Application::tickCount();
1154 static bool once=
true;
1173 const uint64_t dt = tick();
1174 updateAnimation(dt);
1177 auto& sync = fence[cmdId];
1180 std::this_thread::yield();
1186 video.
paint(device,cmdId);
1188 PaintEvent p(uiLayer,atlas,this->w(),this->h());
1192 dispatchPaintEvent(uiLayer,atlas);
1195 PaintEvent p(numOverlay,atlas,this->w(),this->h());
1198 uiMesh [cmdId].update(device,uiLayer);
1199 numMesh[cmdId].update(device,numOverlay);
1201 CommandBuffer& cmd = commands[cmdId];
1203 auto enc = cmd.startEncoding(device);
1204 renderer.
draw(enc,cmdId,swapchain.currentImage(),uiMesh[cmdId],numMesh[cmdId],inventory,video);
1206 sync = device.submit(cmd);
1207 device.present(swapchain);
1210 auto t = Application::tickCount();
1212 uint32_t delay = uint32_t(16-(t-time));
1213 Application::sleep(delay);
1216 else if(maxFpsInv>0 && t-time<maxFpsInv) {
1217 uint32_t delay = uint32_t(maxFpsInv-(t-time));
1218 Application::sleep(delay);
1223 benchmark.push(t-time);
1226 catch(
const Tempest::SwapchainSuboptimal&) {
1227 Log::e(
"swapchain is outdated - reset renderer");
1234double MainWindow::Fps::get()
const {
1235 uint64_t sum=0,num=0;
1241 if(num==0 || sum==0)
1243 uint64_t fps = (1000*100*num)/sum;
1244 return double(fps)/100.0;
1247void MainWindow::Fps::push(uint64_t t) {
1248 for(
size_t i=9;i>0;--i)
1253void MainWindow::BenchmarkData::push(uint64_t t) {
1254 fpsSum += t>0 ? (1000.0/double((t))) : 60.0;
1256 auto at = std::lower_bound(low1procent.begin(), low1procent.end(), t, std::greater<uint64_t>());
1257 low1procent.insert(at, t);
1258 low1procent.resize(std::min(low1procent.size(), (numFrames+99)/100));
1261void MainWindow::BenchmarkData::clear() {
1262 low1procent.reserve(128);
1263 low1procent.clear();
Tempest::Matrix4x4 viewProj() const
void keyUpEvent(Tempest::KeyEvent &e)
void keyDownEvent(Tempest::KeyEvent &e)
void show(const Show &image)
static const CommandLine & inst()
Tempest::Vec3 displayPosition() const
std::string_view displayName() const
static GameMusic & inst()
Tempest::Signal< void()> onSettingsChanged
static auto options() -> const Options &
Tempest::Signal< void(std::string_view, int, int, int, const GthFont &)> onPrintScreen
Tempest::Signal< void(std::string_view)> onLoadGame
void updateAnimation(uint64_t dt)
static float interfaceScale(const Tempest::Widget *w)
const World * world() const
LoadState checkLoading() const
Tempest::Signal< void(std::string_view)> onStartGame
void load(std::string_view slot)
Tempest::Signal< void(Npc &, Npc &, AiOuputPipe *&)> onDialogPipe
std::string_view menuMain() const
auto version() const -> const VersionInfo &
void startLoad(std::string_view banner, const std::function< std::unique_ptr< GameSession >(std::unique_ptr< GameSession > &&)> f)
Tempest::Signal< void()> onStartLoading
static int settingsGetI(std::string_view sec, std::string_view name)
Tempest::Signal< void(std::string_view)> onVideo
Tempest::Signal< void(const DocumentMenu::Show &)> onShowDocument
Tempest::Signal< void()> onBenchmarkFinished
static float settingsGetF(std::string_view sec, std::string_view name)
void setBenchmarkMode(Benchmark b)
std::function< bool(const Npc *)> isNpcInDialogFn
void setGame(std::unique_ptr< GameSession > &&w)
Tempest::Signal< void(const ChapterScreen::Show &)> onIntroChapter
void emitGlobalSoundWav(std::string_view wav)
Tempest::Signal< void(std::string_view)> onPrint
Tempest::Signal< void(std::string_view, std::string_view)> onSaveGame
void startSave(Tempest::Texture2d &&tex, const std::function< std::unique_ptr< GameSession >(std::unique_ptr< GameSession > &&)> f)
Tempest::Signal< void()> onSessionExit
Tempest::Signal< void()> onWorldLoaded
Mapping mapping(Tempest::KeyEvent const &e) const
Gets a mapping out of a key event.
Action tr(Tempest::KeyEvent const &e) const
MainWindow(Tempest::Device &device)
Constructs the main window.
float uiScale() const
Returns the current UI scaling factor.
auto weaponState() const -> WeaponState
auto transform() const -> Tempest::Matrix4x4
int32_t attribute(Attribute a) const
auto interactive() const -> Interactive *
bool isPressed(KeyCodec::Action a) const
void onRotateMouse(float dAngle)
void onKeyReleased(KeyCodec::Action a, KeyCodec::Mapping mapping=KeyCodec::Mapping::Primary)
bool tickMove(uint64_t dt)
void onRotateMouseDy(float dAngle)
bool tickCameraMove(uint64_t dt)
void onKeyPressed(KeyCodec::Action a, Tempest::Event::KeyType key, KeyCodec::Mapping mapping=KeyCodec::Mapping::Primary)
Tempest::Attachment screenshoot(uint8_t frameId)
void dbgDraw(Tempest::Painter &painter)
void draw(Tempest::Encoder< Tempest::CommandBuffer > &cmd, uint8_t cmdId, size_t imgId, Tempest::VectorImage::Mesh &uiLayer, Tempest::VectorImage::Mesh &numOverlay, InventoryMenu &inventory, VideoWidget &video)
static void resetRecycled(uint8_t fId)
static Tempest::Texture2d loadTextureUncached(std::string_view name, bool forceMips=false)
static const GthFont & font(const float scale)
static const Tempest::Texture2d * loadTexture(std::string_view name, bool forceMips=false)
Main application window for OpenGothic.