OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
gamemenu.cpp
Go to the documentation of this file.
1#include "gamemenu.h"
2
3#include <Tempest/Painter>
4#include <Tempest/Log>
5#include <Tempest/TextCodec>
6#include <Tempest/Dialog>
7
8#include <algorithm>
9
10#include "utils/string_frm.h"
11#include "world/objects/npc.h"
12#include "world/world.h"
13#include "ui/menuroot.h"
14#include "utils/gthfont.h"
15#include "utils/fileutil.h"
16#include "utils/keycodec.h"
18#include "game/serialize.h"
19#include "game/savegameheader.h"
20#include "gothic.h"
21#include "resources.h"
22#include "build.h"
23
24using namespace Tempest;
25
26static const float scriptDiv=8192.0f;
27
30 setFocusPolicy(ClickFocus);
31 setCursorShape(CursorShape::Hidden);
32 setFocus(true);
33 }
34
35 void mouseDownEvent(MouseEvent& e) override {
36 if(e.button==Event::ButtonRight) {
37 close();
38 }
39 }
40
41 void mouseWheelEvent(Tempest::MouseEvent& event) override {
42 onMove(-event.delta);
43 }
44
45 void keyDownEvent(KeyEvent &e) override { e.accept(); }
46 void keyUpEvent (KeyEvent &e) override {
47 if(e.key==Event::K_ESCAPE) {
48 close();
49 return;
50 }
51 if(e.key==Event::K_W || e.key==Event::K_Up) {
52 onMove(-1);
53 update();
54 }
55 if(e.key==Event::K_S || e.key==Event::K_Down) {
56 onMove(+1);
57 update();
58 }
59 }
60
61 void onMove(int dy) {
62 if(dy<0) {
63 if(textView.scroll>0)
64 textView.scroll--;
65 }
66 if(dy>0) {
67 // will be clamped at draw
68 textView.scroll++;
69 }
70 }
71
72 void paintEvent (PaintEvent&) override {}
73 void paintShadow(PaintEvent&) override {}
74
76 };
77
78struct GameMenu::ListViewDialog : Dialog {
80 setFocusPolicy(ClickFocus);
81 setCursorShape(CursorShape::Hidden);
82 setFocus(true);
83 status = toStatus(list.handle->user_string[0]);
84 }
85
86 void mouseDownEvent(MouseEvent& e) override {
87 if(e.button==Event::ButtonLeft) {
88 showQuest();
89 }
90 if(e.button==Event::ButtonRight) {
91 close();
92 }
93 }
94
95 void showQuest() {
96 auto prev = owner.curItem;
97 auto* next = owner.selectedContentItem(&list);
98 auto* ql = selectedQuest();
99 if(next==nullptr || ql==nullptr)
100 return;
101
102 auto vis = next->visible;
103 next->visible = true;
104
105 std::string text;
106 for(size_t i=0; i<ql->entry.size(); ++i) {
107 text += ql->entry[i];
108 if(i+1<ql->entry.size())
109 text+="\n---\n";
110 }
111 next->scroll = 0;
112 next->handle->text[0] = text;
113
114 for(uint32_t i=0; i<zenkit::IMenu::item_count; ++i)
115 if(&owner.hItems[i]==next) {
116 owner.curItem = i;
117 break;
118 }
119
120 ListContentDialog dlg(*next);
121 dlg.resize(owner.owner.size());
122 dlg.exec();
123 next->visible = vis;
124 owner.curItem = prev;
125 }
126
127 void keyDownEvent(KeyEvent &e) override { e.accept(); }
128
129 void keyRepeatEvent(KeyEvent &e) override {
130 keyUpEvent(e);
131 }
132
133 void keyUpEvent (KeyEvent &e) override {
134 if(e.key==Event::K_Return) {
135 showQuest();
136 return;
137 }
138 if(e.key==Event::K_ESCAPE) {
139 close();
140 return;
141 }
142 if(e.key==Event::K_W || e.key==Event::K_Up) {
143 onMove(-1);
144 update();
145 }
146 if(e.key==Event::K_S || e.key==Event::K_Down) {
147 onMove(+1);
148 update();
149 }
150 }
151
152 void mouseWheelEvent(Tempest::MouseEvent& event) override {
153 onMove(-event.delta);
154 }
155
156 void onMove(int dy) {
157 if(dy<0) {
158 if(list.value>0)
159 list.value--;
160 }
161 if(dy>0) {
162 const size_t num = numQuests();
163 if(list.value+1<int(num))
164 list.value++;
165 }
166 }
167
168 void paintEvent (PaintEvent&) override {}
169 void paintShadow(PaintEvent&) override {}
170
171 size_t numQuests() const {
172 return size_t(GameMenu::numQuests(Gothic::inst().questLog(),status));
173 }
174
176 int32_t num = 0;
177 if(auto ql=Gothic::inst().questLog()) {
178 for(size_t i=0; i<ql->questCount(); ++i) {
179 auto& quest = ql->quest(ql->questCount()-i-1);
180 if(!isCompatible(quest,status))
181 continue;
182 if(num==list.value)
183 return &quest;
184 ++num;
185 }
186 }
187 return nullptr;
188 }
189
192 QuestStat status = QuestStat::Log;
193 };
194
195struct GameMenu::KeyEditDialog : Dialog {
197 setFocusPolicy(ClickFocus);
198 setCursorShape(CursorShape::Hidden);
199 setFocus(true);
200 }
201 void keyDownEvent(KeyEvent &e) override { e.accept(); }
202 void keyUpEvent (KeyEvent &e) override {
203 close();
204 key = e.key;
205 }
206
207 void mouseDownEvent(MouseEvent& e) override { e.accept(); }
208 void mouseUpEvent (MouseEvent& e) override {
209 close();
210 mkey = e.button;
211 }
212
213 void paintEvent (PaintEvent&) override {}
214 void paintShadow(PaintEvent&) override {}
215
216 Event::KeyType key = Event::K_ESCAPE;
217 Event::MouseButton mkey = Event::ButtonNone;
218 };
219
220struct GameMenu::SavNameDialog : Dialog {
222 setFocusPolicy(ClickFocus);
223 setCursorShape(CursorShape::Hidden);
224 setFocus(true);
225 }
226
227 void mouseDownEvent(MouseEvent& e) override { e.accept(); }
228 void mouseUpEvent (MouseEvent&) override {
229 close();
230 accepted = true;
231 }
232
233 void keyDownEvent(KeyEvent &e) override { e.accept(); }
234 void keyUpEvent (KeyEvent &e) override {
235 update();
236
237 if(e.key==Event::K_ESCAPE) {
238 text = text0;
239 close();
240 return;
241 }
242 if(e.key==Event::K_Return) {
243 accepted = true;
244 close();
245 return;
246 }
247 if(e.key==Event::K_Back && text.size()>0) {
248 text.pop_back();
249 return;
250 }
251
252 char ch[2] = {'\0','\0'};
253 if(('a'<=e.code && e.code<='z') || ('A'<=e.code && e.code<='Z') ||
254 ('0'<=e.code && e.code<='9') || e.code==' ' || e.code=='.')
255 ch[0] = char(e.code);
256 if(ch[0]=='\0')
257 return;
258 text = text + ch;
259 }
260
261 void paintEvent (PaintEvent&) override {}
262 void paintShadow(PaintEvent&) override {}
263
264 std::string& text;
265 std::string text0;
266
267 bool accepted = false;
268 };
269
270GameMenu::GameMenu(MenuRoot &owner, KeyCodec& keyCodec, zenkit::DaedalusVm& vm, std::string_view menuSection, KeyCodec::Action kClose)
271 :owner(owner), keyCodec(keyCodec), vm(&vm), kClose(kClose) {
272 setCursorShape(CursorShape::Hidden);
273 timer.timeout.bind(this,&GameMenu::onTick);
274 timer.start(100);
275
276 textBuf.reserve(64);
277
278 auto* menuSectionSymbol = vm.find_symbol_by_name(menuSection);
279 if(menuSectionSymbol!=nullptr) {
280 menu = vm.init_instance<zenkit::IMenu>(menuSectionSymbol);
281 } else {
282 Tempest::Log::e("Cannot initialize menu ", menuSection, ": Symbol not found.");
283 menu = std::make_shared<zenkit::IMenu>();
284 }
285
286 back = Resources::loadTexture(menu->back_pic);
287
288 initItems();
289 float infoX = 1000.0f/scriptDiv;
290 float infoY = 7500.0f/scriptDiv;
291
292 // There could be script-defined values
293 if(vm.find_symbol_by_name("MENU_INFO_X") != nullptr && vm.find_symbol_by_name("MENU_INFO_X") != nullptr) {
294 auto* symX = vm.find_symbol_by_name("MENU_INFO_X");
295 auto* symY = vm.find_symbol_by_name("MENU_INFO_Y");
296
297 infoX = float(symX->get_int())/scriptDiv;
298 infoY = float(symY->get_int())/scriptDiv;
299 }
300 setPosition(int(infoX*float(w())),int(infoY*float(h())));
301
302 setSelection(Gothic::inst().isInGameAndAlive() ? menu->default_ingame : menu->default_outgame);
303 updateValues();
304 slider = Resources::loadTexture("MENU_SLIDER_POS.TGA");
305
306 up = Resources::loadTexture("O.TGA");
307 down = Resources::loadTexture("U.TGA");
308
310 }
311
315 Resources::device().waitIdle(); // safe-delete savethumb
316 }
317
318void GameMenu::resetVm(zenkit::DaedalusVm* inVm) {
319 vm = inVm;
320 for(int i=0; i<zenkit::IMenu::item_count; ++i){
321 hItems[i].handle = nullptr;
322 }
323 if(vm==nullptr)
324 return;
325 initItems();
326 updateValues();
327 }
328
329GameMenu::QuestStat GameMenu::toStatus(std::string_view str) {
330 if(str=="CURRENTMISSIONS")
331 return QuestStat::Current;
332 if(str=="OLDMISSIONS")
333 return QuestStat::Old;
334 if(str=="FAILEDMISSIONS")
335 return QuestStat::Failed;
336 if(str=="LOG")
337 return QuestStat::Log;
338 return QuestStat::Log;
339 }
340
341bool GameMenu::isCompatible(const QuestLog::Quest& q, QuestStat st) {
342 if(q.section==QuestLog::Section::Note && st==QuestStat::Log)
343 return true;
344 return (uint8_t(q.status)==uint8_t(st) && q.section!=QuestLog::Section::Note);
345 }
346
347int32_t GameMenu::numQuests(const QuestLog* ql, QuestStat st) {
348 if(ql==nullptr)
349 return 0;
350 int32_t num = 0;
351 for(size_t i=0; i<ql->questCount(); ++i)
352 if(isCompatible(ql->quest(i),st))
353 ++num;
354 return num;
355 }
356
357void GameMenu::initItems() {
358 for(int i=0; i<zenkit::IMenu::item_count; ++i){
359 if(menu->items[i].empty())
360 continue;
361
362 hItems[i].name = menu->items[i];
363
364 auto* menuItemSymbol = vm->find_symbol_by_name(hItems[i].name);
365 if (menuItemSymbol != nullptr) {
366 hItems[i].handle = vm->init_instance<zenkit::IMenuItem>(menuItemSymbol);
367 } else {
368 Tempest::Log::e("Cannot initialize menu item ", hItems[i].name, ": Symbol not found.");
369 hItems[i].handle = std::make_shared<zenkit::IMenuItem>();
370 }
371
372 hItems[i].img = Resources::loadTexture(hItems[i].handle->backpic);
373
374 if(hItems[i].handle->type==zenkit::MenuItemType::LISTBOX) {
375 hItems[i].visible = false;
376 }
377 updateItem(hItems[i]);
378 }
379 }
380
381void GameMenu::paintEvent(PaintEvent &e) {
382 const float scale = Gothic::interfaceScale(this);
383
384 Painter p(e);
385 p.setScissor(-x(),-y(),owner.w(),owner.h());
386
387 if(back) {
388 p.setBrush(*back);
389 p.drawRect(0,0,w(),h(),
390 0,0,back->w(),back->h());
391 }
392
393 for(auto& hItem:hItems)
394 drawItem(p,hItem);
395
396 if(menu->flags & zenkit::MenuFlag::SHOW_INFO) {
397 if(auto sel=selectedItem()) {
398 auto& item = sel->handle;
399 if(item->text->size()>0) {
400 auto fnt = Resources::font(scale);
401 std::string_view txt = item->text[1];
402 int tw = fnt.textSize(txt).w;
403
404 fnt.drawText(p,(w()-tw)/2,h()-int(7*scale),txt);
405 }
406 }
407 }
408
409 if(owner.hasVersionLine()) {
410 auto& fnt = Resources::font(scale);
411 fnt.drawText(p, w()-fnt.textSize(appBuild).w-int(25*scale), h()-int(25*scale), appBuild);
412 }
413 }
414
415void GameMenu::drawItem(Painter& p, Item& hItem) {
416 if(!hItem.visible || hItem.name.empty())
417 return;
418 if(isHidden(hItem.handle))
419 return;
420
421 auto& item = hItem.handle;
422 auto flags = item->flags;
423 getText(hItem,textBuf);
424
425 const int32_t dimx = (item->dim_x!=-1) ? item->dim_x : 8192;
426 const int32_t dimy = (item->dim_y!=-1) ? item->dim_y : 750;
427
428 const int x = int(float(w()*item->pos_x)/scriptDiv);
429 const int y = int(float(h()*item->pos_y)/scriptDiv);
430 int szX = int(float(w()*dimx )/scriptDiv);
431 int szY = int(float(h()*dimy )/scriptDiv);
432
433 if(hItem.img && !hItem.img->isEmpty()) {
434 p.setBrush(*hItem.img);
435 p.drawRect(x,y,szX,szY,
436 0,0,hItem.img->w(),hItem.img->h());
437 }
438
439 auto fnt = getTextFont(hItem);
440 int tw = szX;
441 int th = szY;
442
443 AlignFlag txtAlign=NoAlign;
444 if(flags & zenkit::MenuItemFlag::CENTERED) {
445 txtAlign = AlignHCenter | AlignVCenter;
446 }
447
448 //p.setBrush(Color(1,1,1,1));
449 //p.drawRect(x,y,szX,szY);
450 {
451 int padd = 0;
452 if((flags & zenkit::MenuItemFlag::MULTILINE) &&
453 std::min(tw,th)>100*2+fnt.pixelSize()) {
454 padd = 100; // TODO: find out exact padding formula
455 }
456 auto tRect = Rect(x+padd,y+fnt.pixelSize()+padd,
457 tw-2*padd, th-2*padd);
458
459 if(flags & zenkit::MenuItemFlag::MULTILINE) {
460 int lineCnt = fnt.lineCount(tRect.w,textBuf.data());
461 int linesInView = tRect.h/fnt.pixelSize();
462
463 hItem.scroll = std::min(hItem.scroll, std::max(lineCnt-linesInView,0));
464 hItem.scroll = std::max(hItem.scroll, 0);
465 if(lineCnt>linesInView+hItem.scroll) {
466 p.setBrush(*down);
467 p.drawRect(x+(tw-down->w())/2, y+th-padd,
468 down->w(), down->h());
469 }
470 if(hItem.scroll>0) {
471 p.setBrush(*up);
472 p.drawRect(x+(tw-up->w())/2, y+padd-up->h(),
473 up->w(), up->h());
474 }
475 } else {
476 tRect.h = std::max(tRect.h, fnt.pixelSize());
477 }
478
479 fnt.drawText(p, tRect.x,tRect.y, tRect.w, tRect.h,
480 textBuf.data(), txtAlign, hItem.scroll);
481 }
482
483 if(item->type==zenkit::MenuItemType::SLIDER && slider!=nullptr) {
484 drawSlider(p,hItem,x,y,szX,szY);
485 }
486 else if(item->type==zenkit::MenuItemType::LISTBOX) {
487 if(auto ql = Gothic::inst().questLog()) {
488 const int px = int(float(w()*item->frame_sizex)/scriptDiv);
489 const int py = int(float(h()*item->frame_sizey)/scriptDiv);
490
491 auto st = toStatus(item->user_string[0]);
492 drawQuestList(p, hItem, x+px,y+py, szX-2*px,szY-2*py, *ql,st);
493 }
494 }
495 else if(item->type==zenkit::MenuItemType::INPUT) {
496 string_frm textBuf;
497 if(item->on_chg_set_option_section=="KEYS") {
498 auto keys = Gothic::settingsGetS(item->on_chg_set_option_section, item->on_chg_set_option);
499 if(&hItem!=ctrlInput)
500 textBuf = KeyCodec::keysStr(keys); else
501 textBuf = "_";
502 }
503 else {
504 auto str = string_frm(item->text[0]);
505 if(str.empty() && &hItem!=ctrlInput && saveSlotId(hItem)!=size_t(-1))
506 str = "---";
507 if(&hItem!=ctrlInput)
508 textBuf = std::string_view(str); else
509 textBuf = string_frm(str,"_");
510 }
511
512 fnt.drawText(p,
513 x,y+fnt.pixelSize(),
514 szX, std::max(szY,fnt.pixelSize()),
515 textBuf,
516 txtAlign);
517 }
518 }
519
520void GameMenu::drawSlider(Painter& p, Item& it, int x, int y, int sw, int sh) {
521 float k = float(sh/2)/float(slider->h());
522 int w = int(float(slider->w())*k);
523 int h = int(float(slider->h())*k);
524 p.setBrush(*slider);
525
526 auto& sec = it.handle->on_chg_set_option_section;
527 auto& opt = it.handle->on_chg_set_option;
528 if(sec.empty() || opt.empty())
529 return;
530
531 float v = Gothic::settingsGetF(sec, opt);
532 int dx = int(float(sw-w)*std::max(0.f,std::min(v,1.f)));
533 p.drawRect(x+dx,y+(sh-h)/2,w,h,
534 0,0,p.brush().w(),p.brush().h());
535 }
536
537void GameMenu::drawQuestList(Painter& p, Item& it, int x, int y, int w, int h,
538 const QuestLog& log, QuestStat st) {
539 int itY = y;
540 int32_t listId = -1;
541 int32_t listLen = numQuests(&log,st);
542 int32_t listBegin = it.scroll;
543 int32_t listEnd = listLen;
544
545 const float scale = Gothic::interfaceScale(this);
546 for(size_t i=0; i<log.questCount(); i++) {
547 auto& quest = log.quest(log.questCount()-i-1);
548 if(!isCompatible(quest,st))
549 continue;
550 ++listId;
551 if(listId<listBegin)
552 continue;
553
555 if(listId==it.value && selectedItem()==&it)
557
558 auto& fnt = Resources::font(it.handle->fontname,ft,scale);
559 auto sz = fnt.textSize(w,quest.name);
560 if(itY+sz.h>h+y) {
561 listEnd = listId;
562 break;
563 }
564
565 fnt.drawText(p,x,itY+int(fnt.pixelSize()),w,sz.h,quest.name,Tempest::AlignLeft);
566 itY+=sz.h;
567 }
568
569 if(listBegin>it.value) {
570 it.scroll = std::max(it.scroll-1,0);
571 update();
572 }
573 if(listEnd<=it.value) {
574 it.scroll = std::min(it.scroll+1,listLen);
575 update();
576 }
577
578 if(listEnd<listLen && down!=nullptr) {
579 p.setBrush(*down);
580 p.drawRect(x+w-down->w(),y+h-down->h(),
581 down->w(),down->h());
582 }
583 if(listBegin>0 && up!=nullptr) {
584 p.setBrush(*up);
585 p.drawRect(x+w-up->w(),y,
586 up->w(),up->h());
587 }
588 }
589
590void GameMenu::resizeEvent(SizeEvent &) {
591 onTick();
592 }
593
595 auto sel = selectedItem();
596 if(sel!=nullptr && isHorSelectable(sel->handle)) {
597 if(key==KeyCodec::Left)
598 key = KeyCodec::Forward;
599 else if(key==KeyCodec::Right)
600 key = KeyCodec::Back;
601 }
602
603 if(key==KeyCodec::Forward || key==KeyCodec::Back) {
604 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx("MENU_BROWSE"));
605 const int dy = (key==KeyCodec::Forward) ? -1 : +1;
606 setSelection(int(curItem)+dy, dy);
607 if(auto s = selectedItem())
608 updateSavThumb(*s);
609 update();
610 }
611
612 if((key==KeyCodec::Left || key==KeyCodec::Right) && sel!=nullptr) {
613 const int dx = (key==KeyCodec::Left) ? -1 : +1;
614 exec(*sel,dx,key);
615 }
616
617 if(key==KeyCodec::ActionGeneric && sel!=nullptr) {
618 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx("MENU_SELECT"));
619 exec(*sel,0,key);
620 }
621 if(key==KeyCodec::K_Del && sel!=nullptr) {
622 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx("MENU_SELECT"));
623 exec(*sel,0,key);
624 }
625 }
626
628 update();
629
630 const float scale = Gothic::interfaceScale(this);
631 const float fx = 640.0f * scale;
632 const float fy = 480.0f * scale;
633
634 const float wx = float(owner.w());
635 const float wy = float(owner.h());
636
637 Size size = {0, 0};
638 if(menu->flags & zenkit::MenuFlag::DONT_SCALE_DIMENSION) {
639 size = {int(float(menu->dim_x)*fx/scriptDiv),int(float(menu->dim_y)*fy/scriptDiv)};
640 } else {
641 size = {int(float(menu->dim_x)*wx/scriptDiv),int(float(menu->dim_y)*wy/scriptDiv)};
642 }
643 resize(size);
644
645 if(menu->flags & zenkit::MenuFlag::ALIGN_CENTER) {
646 setPosition((owner.w()-w())/2, (owner.h()-h())/2);
647 } else {
648 setPosition(int(float(menu->pos_x)/scriptDiv*fx), int(float(menu->pos_y)/scriptDiv*fy));
649 }
650 }
651
653 if(auto theme = Gothic::musicDef()[menu->music_theme])
655 }
656
657GameMenu::Item *GameMenu::selectedItem() {
658 if(curItem<zenkit::IMenu::item_count)
659 return &hItems[curItem];
660 return nullptr;
661 }
662
663GameMenu::Item* GameMenu::selectedNextItem(Item *it) {
664 uint32_t cur=curItem+1;
665 for(uint32_t i=0;i<zenkit::IMenu::item_count;++i)
666 if(&hItems[i]==it) {
667 cur=i+1;
668 break;
669 }
670
671 for(int i=0;i<zenkit::IMenu::item_count;++i,cur++) {
672 cur%=zenkit::IMenu::item_count;
673
674 auto& it=hItems[cur].handle;
675 if(isEnabled(it))
676 return &hItems[cur];
677 }
678 return nullptr;
679 }
680
681GameMenu::Item* GameMenu::selectedContentItem(Item *it) {
682 uint32_t cur=curItem+1;
683 for(uint32_t i=0;i<zenkit::IMenu::item_count;++i)
684 if(&hItems[i]==it) {
685 cur=i+1;
686 break;
687 }
688
689 for(int i=0;i<zenkit::IMenu::item_count;++i,cur++) {
690 cur%=zenkit::IMenu::item_count;
691
692 auto& it=hItems[cur].handle;
693 if(isEnabled(it) && it->type==zenkit::MenuItemType::TEXT)
694 return &hItems[cur];
695 }
696 return nullptr;
697 }
698
699void GameMenu::setSelection(int desired, int seek) {
700 uint32_t cur=uint32_t(desired);
701 for(int i=0; i<zenkit::IMenu::item_count; ++i,cur+=uint32_t(seek)) {
702 cur%=zenkit::IMenu::item_count;
703
704 auto& it=hItems[cur].handle;
705 if(isSelectable(it) && isEnabled(it)){
706 curItem=cur;
707 for(size_t i=0;i<zenkit::IMenuItem::select_action_count;++i)
708 if(it->on_sel_action[i]==int(zenkit::MenuItemSelectAction::EXECUTE_COMMANDS))
709 execCommands(it->on_sel_action_s[i],false,KeyCodec::Idle);
710 return;
711 }
712 }
713 curItem=uint32_t(-1);
714 }
715
716void GameMenu::getText(const Item& it, std::vector<char> &out) {
717 if(out.size()==0)
718 out.resize(1);
719 out[0]='\0';
720
721 const auto& src = it.handle->text[0];
722 if(it.handle->type==zenkit::MenuItemType::TEXT) {
723 size_t size = src.size();
724 out.resize(size+1);
725 std::memcpy(out.data(),src.c_str(),size+1);
726 return;
727 }
728
729 if(it.handle->type==zenkit::MenuItemType::CHOICEBOX) {
730 strEnum(src,it.value,out);
731 return;
732 }
733 }
734
735const GthFont& GameMenu::getTextFont(const GameMenu::Item &it) {
736 const float scale = Gothic::interfaceScale(this);
737 GthFont ret;
738 if(!isEnabled(it.handle))
739 return Resources::font(it.handle->fontname, Resources::FontType::Disabled, scale);
740 if(&it==selectedItem())
741 return Resources::font(it.handle->fontname, Resources::FontType::Hi, scale);
742 return Resources::font(it.handle->fontname, Resources::FontType::Normal, scale);
743 }
744
745bool GameMenu::isSelectable(const std::shared_ptr<zenkit::IMenuItem>& item) {
746 return item != nullptr && (item->flags & zenkit::MenuItemFlag::SELECTABLE) && !isHidden(item);
747 }
748
749bool GameMenu::isHorSelectable(const std::shared_ptr<zenkit::IMenuItem>& item) {
750 return item != nullptr && (item->flags & zenkit::MenuItemFlag::HOR_SELECTABLE) && !isHidden(item);
751 }
752
753bool GameMenu::isEnabled(const std::shared_ptr<zenkit::IMenuItem>& item) {
754 if(item==nullptr)
755 return false;
756 if((item->flags & zenkit::MenuItemFlag::ONLY_INGAME) && !Gothic::inst().isInGameAndAlive())
757 return false;
758 if((item->flags & zenkit::MenuItemFlag::ONLY_OUTGAME) && Gothic::inst().isInGameAndAlive())
759 return false;
760 return true;
761 }
762
763bool GameMenu::isHidden(const std::shared_ptr<zenkit::IMenuItem>& item) {
764 if(item==nullptr)
765 return false;
766 if(item->hide_if_option_set.empty())
767 return false;
768
769 auto opt = Gothic::inst().settingsGetI(item->hide_if_option_section_set,
770 item->hide_if_option_set);
771 return opt==item->hide_on_value;
772 }
773
774void GameMenu::exec(Item &p, int slideDx, KeyCodec::Action hint) {
775 auto* it = &p;
776 while(it!=nullptr) {
777 if(it==&p)
778 execSingle(*it,slideDx,hint); else
779 execChgOption(*it,slideDx);
780 if(it->handle->flags & zenkit::MenuItemFlag::EFFECTS) {
781 auto next=selectedNextItem(it);
782 if(next!=&p)
783 it=next;
784 } else {
785 it=nullptr;
786 }
787 }
788
789 if(closeFlag)
790 owner.closeAll();
791 else if(exitFlag)
792 owner.popMenu();
793 }
794
795void GameMenu::execSingle(Item &it, int slideDx, KeyCodec::Action hint) {
796 auto& item = it.handle;
797 auto& onSelAction = item->on_sel_action;
798 auto& onSelAction_S = item->on_sel_action_s;
799 auto& onEventAction = item->on_event_action;
800
801 if(item->type==zenkit::MenuItemType::INPUT && slideDx==0) {
802 ctrlInput = &it;
803 if(item->on_chg_set_option.empty()) {
804 SavNameDialog dlg{item->text[0]};
805 if(it.savHdr.version==0)
806 dlg.text = "";
807 dlg.resize(owner.size());
808 dlg.exec();
809 ctrlInput = nullptr;
810 if(!dlg.accepted)
811 return;
812 }
813 else if(hint==KeyCodec::K_Del) {
814 keyCodec.clear(item->on_chg_set_option_section, item->on_chg_set_option);
815 updateItem(it);
816 ctrlInput = nullptr;
817 return; //HACK: there is a SEL_ACTION_BACK token in same item
818 }
819 else {
820 KeyEditDialog dlg;
821 dlg.resize(owner.size());
822 dlg.exec();
823 ctrlInput = nullptr;
824
825 if(dlg.key==Event::K_ESCAPE && dlg.mkey==Event::ButtonNone)
826 return;
827 int32_t next = 0;
828 if(dlg.key==Event::K_ESCAPE)
829 next = KeyCodec::keyToCode(dlg.mkey); else
830 next = KeyCodec::keyToCode(dlg.key);
831 keyCodec.set(item->on_chg_set_option_section, item->on_chg_set_option, next);
832 updateItem(it);
833 return; //HACK: there is a SEL_ACTION_BACK token in same item
834 }
835 }
836
837 for(size_t i=0; i<zenkit::IMenuItem::select_action_count; ++i) {
838 auto action = zenkit::MenuItemSelectAction(onSelAction[i]);
839 switch(action) {
840 case zenkit::MenuItemSelectAction::UNKNOWN:
841 break;
842 case zenkit::MenuItemSelectAction::BACK:
843 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx("MENU_ESC"));
844 exitFlag = true;
845 break;
846 case zenkit::MenuItemSelectAction::START_MENU:
847 if(vm->find_symbol_by_name(onSelAction_S[i]) != nullptr)
848 owner.pushMenu(new GameMenu(owner,keyCodec,*vm,onSelAction_S[i],keyClose()));
849 break;
850 case zenkit::MenuItemSelectAction::START_ITEM:
851 break;
852 case zenkit::MenuItemSelectAction::CLOSE:
853 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx("MENU_ESC"));
854
855 if(onSelAction_S[i]=="NEW_GAME") {
856 Gothic::inst().onStartGame(Gothic::inst().defaultWorld());
857 }
858 else if(onSelAction_S[i]=="LEAVE_GAME") {
859 Log::i("Exiting, by item action (`LEAVE_GAME`)");
860 Tempest::SystemApi::exit();
861 }
862 else if(onSelAction_S[i]=="SAVEGAME_SAVE") {
863 execSaveGame(it);
864 }
865 else if(onSelAction_S[i]=="SAVEGAME_LOAD"){
866 if (!execLoadGame(it)){
867 break;
868 }
869 }
870 closeFlag = true;
871 break;
872 case zenkit::MenuItemSelectAction::CON_COMMANDS:
873 // TODO: inline marvin commands
874 break;
875 case zenkit::MenuItemSelectAction::PLAY_SOUND:
876 Gothic::inst().emitGlobalSound(Gothic::inst().loadSoundFx(onSelAction_S[i]));
877 break;
878 case zenkit::MenuItemSelectAction::EXECUTE_COMMANDS:
879 execCommands(onSelAction_S[i],true,hint);
880 break;
881 }
882 }
883
884 if(onEventAction[int(zenkit::MenuItemSelectEvent::EXECUTE)]>0){
885 auto* sym = vm->find_symbol_by_index(uint32_t(onEventAction[int(zenkit::MenuItemSelectEvent::EXECUTE)]));
886 if (sym != nullptr)
887 vm->call_function(sym);
888 }
889
890 execChgOption(it,slideDx);
891 }
892
893void GameMenu::execChgOption(Item &item, int slideDx) {
894 auto& sec = item.handle->on_chg_set_option_section;
895 auto& opt = item.handle->on_chg_set_option;
896 if(sec.empty() || opt.empty())
897 return;
898
899 if(item.handle->type==zenkit::MenuItemType::SLIDER && slideDx!=0) {
900 updateItem(item);
901 float v = Gothic::settingsGetF(sec, opt);
902 v = std::max(0.f,std::min(v+float(slideDx)*0.03f,1.f));
903 Gothic::settingsSetF(sec, opt, v);
904 }
905 if(item.handle->type==zenkit::MenuItemType::CHOICEBOX) {
906 updateItem(item);
907 const int cnt = int(strEnumSize(item.handle->text[0]));
908 if(slideDx==0 && cnt==2)
909 slideDx = 1; // QoL: on/off toggle
910
911 item.value += slideDx; // next value
912 if(cnt>0)
913 item.value = std::clamp(item.value,0,cnt-1); else
914 item.value = 0;
915 Gothic::settingsSetI(sec, opt, item.value);
916 }
917 }
918
919void GameMenu::execSaveGame(const GameMenu::Item& item) {
920 const size_t id = saveSlotId(item);
921 if(id==size_t(-1))
922 return;
923
924 string_frm fname("save_slot_",int(id),".sav");
925 Gothic::inst().save(fname,item.handle->text[0]);
926 }
927
928bool GameMenu::execLoadGame(const GameMenu::Item &item) {
929 const size_t id = saveSlotId(item);
930 if(id==size_t(-1))
931 return false;
932
933 string_frm fname("save_slot_",int(id),".sav");
934 if(!FileUtil::exists(TextCodec::toUtf16(fname.c_str())))
935 return false;
936 Gothic::inst().load(fname);
937 return true;
938 }
939
940void GameMenu::execCommands(std::string str, bool isClick, KeyCodec::Action hint) {
941 if(str.find("EFFECTS ")==0) {
942 // menu log
943 const char* arg0 = str.data()+std::strlen("EFFECTS ");
944 for(uint32_t id=0; id<zenkit::IMenu::item_count; ++id) {
945 auto& i = hItems[id];
946 if(i.handle != nullptr && i.handle->type==zenkit::MenuItemType::LISTBOX) {
947 i.visible = (i.name==arg0);
948 if(i.visible && isClick) {
949 const uint32_t prev = curItem;
950 curItem = id;
951 ListViewDialog dlg(*this,i);
952 if(dlg.numQuests()==0) {
953 curItem = prev;
954 return;
955 }
956 dlg.resize(owner.size());
957 dlg.exec();
958 curItem = prev;
959 }
960 }
961 }
962 }
963
964 if(!isClick)
965 return;
966 if(str.find("RUN ")==0) {
967 const char* what = str.data()+4;
968 while(*what==' ')
969 ++what;
970 for(auto& i:hItems)
971 if(i.name==what)
972 execSingle(i,0,hint);
973 }
974 if(str=="SETDEFAULT") {
975 setDefaultKeys("KEYSDEFAULT0");
976 }
977 if(str=="SETALTERNATIVE") {
978 setDefaultKeys("KEYSDEFAULT1");
979 }
980 }
981
982void GameMenu::updateItem(GameMenu::Item &item) {
983 auto& it = item.handle;
984 item.value = Gothic::settingsGetI(it->on_chg_set_option_section, it->on_chg_set_option);
985 updateSavTitle(item);
986 }
987
988void GameMenu::updateSavTitle(GameMenu::Item& sel) {
989 if(sel.handle->on_sel_action_s[0]!="SAVEGAME_LOAD" &&
990 sel.handle->on_sel_action_s[1]!="SAVEGAME_SAVE")
991 return;
992 const size_t id = saveSlotId(sel);
993 if(id==size_t(-1))
994 return;
995
996 char fname[64]={};
997 std::snprintf(fname,sizeof(fname)-1,"save_slot_%d.sav",int(id));
998
999 if(!FileUtil::exists(TextCodec::toUtf16(fname))) {
1000 sel.handle->text[0] = "---";
1001 return;
1002 }
1003
1004 SaveGameHeader hdr;
1005 try {
1006 RFile fin(fname);
1007 Serialize reader(fin);
1008 reader.setEntry("header");
1009 reader.read(hdr);
1010 if(id!=0 || sel.handle->text[0].empty())
1011 sel.handle->text[0] = hdr.name;
1012 sel.savHdr = std::move(hdr);
1013
1014 if(reader.setEntry("priview.png"))
1015 reader.read(sel.savPriview); // legacy
1016 else if(reader.setEntry("preview.png"))
1017 reader.read(sel.savPriview);
1018 }
1019 catch(std::bad_alloc&) {
1020 return;
1021 }
1022 catch(std::system_error& e) {
1023 Log::d(e.what());
1024 return;
1025 }
1026 catch(std::runtime_error&) {
1027 return;
1028 }
1029 }
1030
1031void GameMenu::updateSavThumb(GameMenu::Item &sel) {
1032 if(sel.handle->on_sel_action_s[0]!="SAVEGAME_LOAD" &&
1033 sel.handle->on_sel_action_s[1]!="SAVEGAME_SAVE")
1034 return;
1035
1036 if(!implUpdateSavThumb(sel)) {
1037 set("MENUITEM_LOADSAVE_THUMBPIC",static_cast<Texture2d*>(nullptr));
1038 set("MENUITEM_LOADSAVE_LEVELNAME_VALUE","");
1039 set("MENUITEM_LOADSAVE_DATETIME_VALUE", "");
1040 set("MENUITEM_LOADSAVE_GAMETIME_VALUE", "");
1041 set("MENUITEM_LOADSAVE_PLAYTIME_VALUE", "");
1042 }
1043 }
1044
1045void GameMenu::updateVideo() {
1046 set("MENUITEM_VID_DEVICE_CHOICE", Resources::renderer());
1047 set("MENUITEM_VID_RESOLUTION_CHOICE", "full|upscale(75%)|upscale(half)");
1048 }
1049
1050void GameMenu::setDefaultKeys(std::string_view preset) {
1051 keyCodec.setDefaultKeys(preset);
1052 }
1053
1054bool GameMenu::implUpdateSavThumb(GameMenu::Item& sel) {
1055 const size_t id = saveSlotId(sel);
1056 if(id==size_t(-1))
1057 return false;
1058
1059 char fname[64]={};
1060 std::snprintf(fname,sizeof(fname)-1,"save_slot_%d.sav",int(id));
1061
1062 if(!FileUtil::exists(TextCodec::toUtf16(fname)))
1063 return false;
1064
1065 const SaveGameHeader& hdr = sel.savHdr;
1066 char form[64]={};
1067 Resources::device().waitIdle();
1068 savThumb = Resources::loadTexturePm(sel.savPriview);
1069
1070 set("MENUITEM_LOADSAVE_THUMBPIC", &savThumb);
1071 set("MENUITEM_LOADSAVE_LEVELNAME_VALUE",hdr.world);
1072
1073 std::snprintf(form,sizeof(form),"%d.%d.%d - %d:%02d",int(hdr.pcTime.tm_mday),int(hdr.pcTime.tm_mon+1),int(1900+hdr.pcTime.tm_year), int(hdr.pcTime.tm_hour),int(hdr.pcTime.tm_min));
1074 set("MENUITEM_LOADSAVE_DATETIME_VALUE", form);
1075
1076 std::snprintf(form,sizeof(form),"%d - %d:%02d",int(hdr.wrldTime.day()),int(hdr.wrldTime.hour()),int(hdr.wrldTime.minute()));
1077 set("MENUITEM_LOADSAVE_GAMETIME_VALUE", form);
1078
1079 auto mins = hdr.playTime/(60*1000);
1080 std::snprintf(form,sizeof(form),"%dh %dmin",int(mins/60),int(mins%60));
1081 set("MENUITEM_LOADSAVE_PLAYTIME_VALUE", form);
1082 return true;
1083 }
1084
1085size_t GameMenu::saveSlotId(const GameMenu::Item &sel) {
1086 const char* prefix[2] = {"MENUITEM_LOAD_SLOT", "MENUITEM_SAVE_SLOT"};
1087 for(auto p:prefix) {
1088 if(sel.name.find(p)==0){
1089 size_t off=std::strlen(p);
1090 size_t id=0;
1091 for(size_t i=off;i<sel.name.size();++i){
1092 if('0'<=sel.name[i] && sel.name[i]<='9') {
1093 id=id*10+size_t(sel.name[i]-'0');
1094 } else {
1095 return size_t(-1);
1096 }
1097 }
1098 return id;
1099 }
1100 }
1101 return size_t(-1);
1102 }
1103
1104std::string_view GameMenu::strEnum(std::string_view en, int id, std::vector<char> &out) {
1105 int num=0;
1106
1107 size_t i=0;
1108 for(size_t r=0; r<en.size(); ++r)
1109 if(en[r]=='#'){
1110 i=r+1;
1111 break;
1112 }
1113
1114 for(;i<en.size();++i){
1115 size_t b=i;
1116 for(size_t r=i; r<en.size(); ++r,++i)
1117 if(en[r]=='|')
1118 break;
1119
1120 if(id==num) {
1121 size_t sz=i-b;
1122 out.resize(sz+1);
1123 std::memcpy(&out[0],&en[b],sz);
1124 out[sz]='\0';
1125 return out.data();
1126 }
1127 ++num;
1128 }
1129
1130 for(size_t i=0; i<en.size(); ++i){
1131 if(en[i]=='#' || en[i]=='|'){
1132 out.resize(i+1);
1133 std::memcpy(&out[0],en.data(),i);
1134 out[i]='\0';
1135 return out.data();
1136 }
1137 }
1138
1139 return "";
1140 }
1141
1142size_t GameMenu::strEnumSize(std::string_view en) {
1143 if(en.empty())
1144 return 0;
1145 size_t cnt = 0;
1146 for(size_t i=0; i<en.size(); ++i) {
1147 if(en[i]=='#' || en[i]=='|') {
1148 cnt += en[i]=='|' ? 2 : 1;
1149 i++;
1150 for(;i<en.size();++i) {
1151 if(en[i]=='|')
1152 cnt++;
1153 }
1154 }
1155 }
1156 return cnt;
1157 }
1158
1159void GameMenu::set(std::string_view item, const Texture2d *value) {
1160 for(auto& i:hItems)
1161 if(i.name==item) {
1162 i.img = value;
1163 return;
1164 }
1165 }
1166
1167void GameMenu::set(std::string_view item, const uint32_t value) {
1168 set(item,string_frm(value));
1169 }
1170
1171void GameMenu::set(std::string_view item, const int32_t value) {
1172 set(item,string_frm(value));
1173 }
1174
1175void GameMenu::set(std::string_view item, const int32_t value, const int32_t max) {
1176 set(item,string_frm(value,"/",max));
1177 }
1178
1179void GameMenu::set(std::string_view item, std::string_view value) {
1180 for(auto& i:hItems)
1181 if(i.name==item) {
1182 i.handle->text[0] = value;
1183 return;
1184 }
1185 }
1186
1187void GameMenu::updateValues() {
1188 gtime time;
1189 if(auto w = Gothic::inst().world())
1190 time = w->time();
1191
1192 set("MENU_ITEM_PLAYERGUILD","Debugger");
1193 set("MENU_ITEM_LEVEL","0");
1194 set("MENUITEM_AUDIO_PROVIDER_CHOICE", "OpenGothic|GothicKit (Experimental)");
1195 for(auto& i:hItems) {
1196 if(i.name=="MENU_ITEM_CONTENT_VIEWER") {
1197 i.visible=false;
1198 }
1199 if(i.name=="MENU_ITEM_DAY") {
1200 i.handle->text[0] = std::to_string(time.day());
1201 }
1202 if(i.name=="MENU_ITEM_TIME") {
1203 char form[64]={};
1204 std::snprintf(form,sizeof(form),"%d:%02d",int(time.hour()),int(time.minute()));
1205 i.handle->text[0] = form;
1206 }
1207 }
1208
1209 if(auto s = selectedItem())
1210 updateSavThumb(*s);
1211
1212 updateVideo();
1213 }
1214
1215void GameMenu::setPlayer(const Npc &pl) {
1216 auto world = Gothic::inst().world();
1217 if(world==nullptr)
1218 return;
1219
1220 auto& sc = world->script();
1221 auto* gilds = sc.findSymbol("TXT_GUILDS");
1222 auto* tal = sc.findSymbol("TXT_TALENTS");
1223 auto* talV = sc.findSymbol("TXT_TALENTS_SKILLS");
1224
1225 if(gilds==nullptr || tal==nullptr || talV==nullptr) {
1226 return;
1227 }
1228
1229 set("MENU_ITEM_PLAYERGUILD", gilds->get_string(uint16_t(pl.guild())));
1230
1231 set("MENU_ITEM_LEVEL", pl.level());
1232 set("MENU_ITEM_EXP", pl.experience());
1233 set("MENU_ITEM_LEVEL_NEXT", pl.experienceNext());
1234 set("MENU_ITEM_LEARN", pl.learningPoints());
1235
1236 set("MENU_ITEM_ATTRIBUTE_1", pl.attribute(ATR_STRENGTH));
1237 set("MENU_ITEM_ATTRIBUTE_2", pl.attribute(ATR_DEXTERITY));
1238 set("MENU_ITEM_ATTRIBUTE_3", pl.attribute(ATR_MANA), pl.attribute(ATR_MANAMAX));
1239 set("MENU_ITEM_ATTRIBUTE_4", pl.attribute(ATR_HITPOINTS), pl.attribute(ATR_HITPOINTSMAX));
1240
1241 set("MENU_ITEM_ARMOR_11", pl.protection(PROT_EDGE));
1242 set("MENU_ITEM_ARMOR_1", pl.protection(PROT_BLUNT));
1243 set("MENU_ITEM_ARMOR_2", pl.protection(PROT_POINT)); // not sure about it
1244 set("MENU_ITEM_ARMOR_3", pl.protection(PROT_FIRE));
1245 set("MENU_ITEM_ARMOR_4", pl.protection(PROT_MAGIC));
1246
1247 const bool g2 = Gothic::inst().version().game==2;
1248 const int talentMax = g2 ? TALENT_MAX_G2 : TALENT_MAX_G1;
1249 for(uint16_t i=0; i<talentMax; ++i) {
1250 auto& str = tal->get_string(i);
1251 if(str.empty())
1252 continue;
1253
1254 const int sk = pl.talentSkill(Talent(i));
1255 const int val = g2 ? pl.hitChance(Talent(i)) : pl.talentValue(Talent(i));
1256
1257 set(string_frm("MENU_ITEM_TALENT_",i,"_TITLE"), str);
1258 set(string_frm("MENU_ITEM_TALENT_",i,"_SKILL"), strEnum(talV->get_string(i),sk,textBuf));
1259 set(string_frm("MENU_ITEM_TALENT_",i), string_frm(val,"%"));
1260 }
1261 }
static const char * appBuild
Definition build.h:3
void setPlayer(const Npc &pl)
void onKeyboard(KeyCodec::Action k)
Definition gamemenu.cpp:594
~GameMenu() override
Definition gamemenu.cpp:312
void resizeEvent(Tempest::SizeEvent &event) override
Definition gamemenu.cpp:590
void paintEvent(Tempest::PaintEvent &event) override
Definition gamemenu.cpp:381
void resetVm(zenkit::DaedalusVm *vm)
Definition gamemenu.cpp:318
void onTick()
Definition gamemenu.cpp:627
GameMenu(MenuRoot &owner, KeyCodec &keyCodec, zenkit::DaedalusVm &vm, std::string_view menuSection, KeyCodec::Action keyClose)
Definition gamemenu.cpp:270
KeyCodec::Action keyClose() const
Definition gamemenu.h:34
void processMusicTheme()
Definition gamemenu.cpp:652
static Tags mkTags(Tags daytime, Tags mode)
static GameMusic & inst()
void setMusic(Music m)
zenkit::DaedalusSymbol * findSymbol(std::string_view s)
void pushPause()
Definition gothic.cpp:451
static void settingsSetI(std::string_view sec, std::string_view name, int val)
Definition gothic.cpp:819
static float interfaceScale(const Tempest::Widget *w)
Definition gothic.cpp:471
const World * world() const
Definition gothic.cpp:278
void popPause()
Definition gothic.cpp:455
Tempest::Signal< void(std::string_view)> onStartGame
Definition gothic.h:166
void load(std::string_view slot)
Definition gothic.cpp:627
static Gothic & inst()
Definition gothic.cpp:249
auto version() const -> const VersionInfo &
Definition gothic.cpp:263
static int settingsGetI(std::string_view sec, std::string_view name)
Definition gothic.cpp:807
void save(std::string_view slot, std::string_view usrName)
Definition gothic.cpp:623
static const MusicDefinitions & musicDef()
Definition gothic.cpp:659
static void flushSettings()
Definition gothic.cpp:858
static std::string_view settingsGetS(std::string_view sec, std::string_view name)
Definition gothic.cpp:824
bool isInGameAndAlive() const
Definition gothic.cpp:271
static void settingsSetF(std::string_view sec, std::string_view name, float val)
Definition gothic.cpp:853
static float settingsGetF(std::string_view sec, std::string_view name)
Definition gothic.cpp:841
void emitGlobalSound(std::string_view sfx)
Definition gothic.cpp:395
Definition item.h:14
const zenkit::IItem & handle() const
Definition item.h:83
static auto keysStr(std::string_view keys) -> string_frm< 64 >
Definition keycodec.cpp:305
void set(std::string_view section, std::string_view key, int32_t code)
Definition keycodec.cpp:139
static int32_t keyToCode(Tempest::Event::KeyType t)
Definition keycodec.cpp:423
void setDefaultKeys(std::string_view preset)
Definition keycodec.cpp:166
void clear(std::string_view section, std::string_view key)
Definition keycodec.cpp:162
@ ActionGeneric
Definition keycodec.h:45
@ Forward
Definition keycodec.h:37
void popMenu()
Definition menuroot.cpp:79
void pushMenu(GameMenu *w)
Definition menuroot.cpp:67
void closeAll()
Definition menuroot.cpp:98
bool hasVersionLine() const
Definition menuroot.cpp:125
Definition npc.h:25
int32_t protection(Protection p) const
Definition npc.cpp:1208
uint32_t guild() const
Definition npc.cpp:1223
int32_t experience() const
Definition npc.cpp:1258
int32_t hitChance(Talent t) const
Definition npc.cpp:1149
int32_t talentSkill(Talent t) const
Definition npc.cpp:1132
int32_t talentValue(Talent t) const
Definition npc.cpp:1143
int32_t attribute(Attribute a) const
Definition npc.cpp:1171
int32_t experienceNext() const
Definition npc.cpp:1262
int32_t learningPoints() const
Definition npc.cpp:1266
int32_t level() const
Definition npc.cpp:1254
auto quest(size_t i) const -> const Quest &
Definition questlog.h:41
size_t questCount() const
Definition questlog.h:40
static const GthFont & font(const float scale)
static Tempest::Device & device()
Definition resources.h:83
static const Tempest::Texture2d * loadTexture(std::string_view name, bool forceMips=false)
static const char * renderer()
static Tempest::Texture2d loadTexturePm(const Tempest::Pixmap &pm)
std::string world
std::string name
GameScript & script() const
Definition world.cpp:1019
int64_t day() const
Definition gametime.h:17
int64_t hour() const
Definition gametime.h:20
int64_t minute() const
Definition gametime.h:21
Talent
Definition constants.h:435
@ TALENT_MAX_G1
Definition constants.h:458
@ TALENT_MAX_G2
Definition constants.h:459
@ ATR_STRENGTH
Definition constants.h:467
@ ATR_MANAMAX
Definition constants.h:466
@ ATR_HITPOINTSMAX
Definition constants.h:464
@ ATR_DEXTERITY
Definition constants.h:468
@ ATR_HITPOINTS
Definition constants.h:463
@ ATR_MANA
Definition constants.h:465
@ PROT_EDGE
Definition constants.h:487
@ PROT_POINT
Definition constants.h:491
@ PROT_MAGIC
Definition constants.h:490
@ PROT_BLUNT
Definition constants.h:486
@ PROT_FIRE
Definition constants.h:488
static const float scriptDiv
Definition gamemenu.cpp:26
bool exists(const std::u16string &path)
Definition fileutil.cpp:15
void keyUpEvent(KeyEvent &e) override
Definition gamemenu.cpp:202
void mouseUpEvent(MouseEvent &e) override
Definition gamemenu.cpp:208
void keyDownEvent(KeyEvent &e) override
Definition gamemenu.cpp:201
void mouseDownEvent(MouseEvent &e) override
Definition gamemenu.cpp:207
void paintEvent(PaintEvent &) override
Definition gamemenu.cpp:213
void paintShadow(PaintEvent &) override
Definition gamemenu.cpp:214
Event::MouseButton mkey
Definition gamemenu.cpp:217
void paintShadow(PaintEvent &) override
Definition gamemenu.cpp:73
ListContentDialog(Item &textView)
Definition gamemenu.cpp:29
void mouseDownEvent(MouseEvent &e) override
Definition gamemenu.cpp:35
void keyUpEvent(KeyEvent &e) override
Definition gamemenu.cpp:46
void keyDownEvent(KeyEvent &e) override
Definition gamemenu.cpp:45
void paintEvent(PaintEvent &) override
Definition gamemenu.cpp:72
void mouseWheelEvent(Tempest::MouseEvent &event) override
Definition gamemenu.cpp:41
size_t numQuests() const
Definition gamemenu.cpp:171
void keyUpEvent(KeyEvent &e) override
Definition gamemenu.cpp:133
ListViewDialog(GameMenu &owner, Item &list)
Definition gamemenu.cpp:79
void mouseWheelEvent(Tempest::MouseEvent &event) override
Definition gamemenu.cpp:152
void mouseDownEvent(MouseEvent &e) override
Definition gamemenu.cpp:86
void paintEvent(PaintEvent &) override
Definition gamemenu.cpp:168
void keyDownEvent(KeyEvent &e) override
Definition gamemenu.cpp:127
const QuestLog::Quest * selectedQuest() const
Definition gamemenu.cpp:175
void paintShadow(PaintEvent &) override
Definition gamemenu.cpp:169
void keyRepeatEvent(KeyEvent &e) override
Definition gamemenu.cpp:129
void paintShadow(PaintEvent &) override
Definition gamemenu.cpp:262
void paintEvent(PaintEvent &) override
Definition gamemenu.cpp:261
void mouseDownEvent(MouseEvent &e) override
Definition gamemenu.cpp:227
SavNameDialog(std::string &text)
Definition gamemenu.cpp:221
void mouseUpEvent(MouseEvent &) override
Definition gamemenu.cpp:228
void keyDownEvent(KeyEvent &e) override
Definition gamemenu.cpp:233
void keyUpEvent(KeyEvent &e) override
Definition gamemenu.cpp:234
Section section
Definition questlog.h:27
Status status
Definition questlog.h:28