4#include <Tempest/TextCodec>
10#include <zenkit/addon/daedalus.hh>
28using namespace Tempest;
31Gothic* Gothic::instance =
nullptr;
35 if(p.meshlets.meshShader && p.meshlets.taskShader)
42 if(p.descriptors.nonUniformIndexing && p.descriptors.maxTexture>=65000 && p.descriptors.maxStorage>=65000)
50 systemPackIniFile.reset(
new IniFile(
nestedPath({u
"system",u
"SystemPack.ini"},Dir::FT_File)));
51 showFpsCounter = systemPackIniFile->getI(
"DEBUG",
"Show_FPS_Counter");
52 opts.
hideFocus = systemPackIniFile->getI(
"PARAMETERS",
"HideFocus");
53 opts.
cameraFov = systemPackIniFile->getF(
"PARAMETERS",
"VerticalFOV");
54 opts.
fpsLimit = std::max(0, systemPackIniFile->getI(
"PARAMETERS",
"FPS_Limit", 0));
58 opts.
interfaceScale = systemPackIniFile->getF(
"INTERFACE",
"Scale",1);
71 opts.
showHealthBar = !(systemPackIniFile->getI(
"INTERFACE",
"HideHealthBar",0)!=0);
72 opts.
showManaBar = uint8_t(std::clamp(systemPackIniFile->getI(
"INTERFACE",
"ShowManaBar",1), 0, 2));
73 opts.
showSwimBar = uint8_t(std::clamp(systemPackIniFile->getI(
"INTERFACE",
"ShowSwimBar",1), 0, 2));
85 if(gpu.raytracing.rayQuery) {
111 baseIniFile.reset(
new IniFile(
nestedPath({u
"system",u
"Gothic.ini"},Dir::FT_File)));
112 iniFile .reset(
new IniFile(u
"Gothic.ini"));
113 if(!iniFile->has(
"INTERNAL",
"vidResIndex")) {
114 iniFile->set(
"INTERNAL",
"vidResIndex", 0);
118 defaults->set(
"GAME",
"enableMouse", 1);
119 defaults->set(
"GAME",
"mouseSensitivity", 0.5f);
120 defaults->set(
"GAME",
"invCatOrder",
"COMBAT,POTION,FOOD,ARMOR,MAGIC,RUNE,DOCS,OTHER,NONE");
121 defaults->set(
"GAME",
"invMaxColumns", 5);
122 defaults->set(
"GAME",
"animatedWindows", 1);
123 defaults->set(
"GAME",
"useGothic1Controls", 1);
124 defaults->set(
"GAME",
"highlightMeleeFocus", 0);
125 defaults->set(
"GAME",
"useQuickSaveKeys", 1);
127 defaults->set(
"GAME",
"animatedWindows", 1);
128 defaults->set(
"GAME",
"subTitles", 1);
129 defaults->set(
"GAME",
"subTitlesPlayer", 1);
132 defaults->set(
"GAME",
"language", -1);
133 defaults->set(
"GAME",
"voice", -1);
134 defaults->set(
"GAME",
"scaleVideos", 1);
136 defaults->set(
"SKY_OUTDOOR",
"zSunName",
"unsun5.tga");
137 defaults->set(
"SKY_OUTDOOR",
"zSunSize", 200);
138 defaults->set(
"SKY_OUTDOOR",
"zSunAlpha", 230);
139 defaults->set(
"SKY_OUTDOOR",
"zMoonName",
"moon.tga");
140 defaults->set(
"SKY_OUTDOOR",
"zMoonSize", 400);
141 defaults->set(
"SKY_OUTDOOR",
"zMoonAlpha", 255);
143 defaults->set(
"RENDERER_D3D",
"zFogRadial", 1);
144 defaults->set(
"ENGINE",
"zEnvMappingEnabled", 1);
145 defaults->set(
"ENGINE",
"zCloudShadowScale", gpu.type==Tempest::DeviceType::Discrete);
146 defaults->set(
"INTERNAL",
"vidResIndex", 0);
148 defaults->set(
"VIDEO",
"zVidBrightness", 0.5f);
149 defaults->set(
"VIDEO",
"zVidContrast", 0.5f);
150 defaults->set(
"VIDEO",
"zVidGamma", 0.5f);
152 defaults->set(
"SOUND",
"musicEnabled", 1);
153 defaults->set(
"SOUND",
"musicVolume", 0.5f);
154 defaults->set(
"SOUND",
"soundVolume", 0.5f);
158 defaults->set(
"ENGINE",
"zWindEnabled", 1);
159 defaults->set(
"ENGINE",
"zWindCycleTime", 4);
160 defaults->set(
"ENGINE",
"zWindCycleTimeVar", 6);
162 defaults->set(
"KEYS",
"keyEnd",
"0100");
163 defaults->set(
"KEYS",
"keyHeal",
"2300");
164 defaults->set(
"KEYS",
"keyPotion",
"1900");
165 defaults->set(
"KEYS",
"keyLockTarget",
"4f00");
166 defaults->set(
"KEYS",
"keyParade",
"cf000d02");
167 defaults->set(
"KEYS",
"keyActionRight",
"d100");
168 defaults->set(
"KEYS",
"keyActionLeft",
"d300");
169 defaults->set(
"KEYS",
"keyUp",
"c8001100");
170 defaults->set(
"KEYS",
"keyDown",
"d0001f00");
171 defaults->set(
"KEYS",
"keyLeft",
"cb001000");
172 defaults->set(
"KEYS",
"keyRight",
"cd001200");
173 defaults->set(
"KEYS",
"keyStrafeLeft",
"d3001e00");
174 defaults->set(
"KEYS",
"keyStrafeRight",
"d1002000");
175 defaults->set(
"KEYS",
"keyAction",
"1d000c02");
176 defaults->set(
"KEYS",
"keySlow",
"2a003600");
177 defaults->set(
"KEYS",
"keySMove",
"38009d00");
178 defaults->set(
"KEYS",
"keyWeapon",
"39000e02");
179 defaults->set(
"KEYS",
"keySneak",
"2d00");
180 defaults->set(
"KEYS",
"keyLook",
"13005200");
181 defaults->set(
"KEYS",
"keyLookFP",
"21005300");
182 defaults->set(
"KEYS",
"keyInventory",
"0f000e00");
183 defaults->set(
"KEYS",
"keyShowStatus",
"30002e00");
184 defaults->set(
"KEYS",
"keyShowLog",
"31002600");
185 defaults->set(
"KEYS",
"keyShowMap",
"3200");
188 detectGothicVersion();
192 modFile.reset(
new IniFile(mod));
195 std::vector<std::u16string> modvdfs;
196 bool modFilter =
true;
197 if(modFile!=
nullptr) {
198 wrldDef = modFile->getS(
"SETTINGS",
"WORLD");
199 size_t split = wrldDef.rfind(
'\\');
200 if(split!=std::string::npos)
201 wrldDef = wrldDef.substr(split+1);
202 plDef = modFile->getS(
"SETTINGS",
"PLAYER");
203 gameDatDef = modFile->getS(
"FILES",
"GAME");
204 ouDef = modFile->getS(
"FILES",
"OUTPUTUNITS");
206 std::u16string vdf = TextCodec::toUtf16(modFile->getS(
"FILES",
"VDF"));
207 modFilter = modFile->has(
"FILES",
"VDF");
208 for(
size_t start = 0, split = 0; split != std::string::npos; start = split+1) {
209 split = vdf.find(
' ', start);
210 std::u16string mod = vdf.substr(start, split-start);
212 modvdfs.emplace_back(std::move(mod));
223 if(wrldDef.empty()) {
225 wrldDef =
"newworld.zen";
else
226 wrldDef =
"world.zen";
232 if(gameDatDef.empty())
233 gameDatDef =
"GOTHIC.DAT";
else
234 gameDatDef +=
".DAT";
250 assert(instance!=
nullptr);
268 return game!=
nullptr;
273 if(pl==
nullptr || pl->isDead())
280 return game->world();
286 return game->world();
296 return std::move(game);
315 return game->player();
321 return &game->camera();
327 return &game->script()->questLog();
332 return loadProgress.load();
336 loadProgress.store(v);
340 return loadTex.isEmpty() ? &saveTex : &loadTex;
347 auto cname = std::string(name);
349 std::lock_guard<std::mutex> guard(syncSnd);
350 auto it=sndFxCache.find(cname);
351 if(it!=sndFxCache.end())
355 auto ret = sndFxCache.emplace(name,
SoundFx(name));
356 return &ret.first->second;
359 Tempest::Log::e(
"unable to load soundfx \"",cname,
"\"");
366 auto cname = std::string(name);
368 std::lock_guard<std::mutex> guard(syncSnd);
369 auto it=sndWavCache.find(cname);
370 if(it!=sndWavCache.end())
374 auto ret = sndWavCache.emplace(name,
SoundFx(std::move(snd)));
375 return &ret.first->second;
378 Tempest::Log::e(
"unable to load soundfx \"",cname,
"\"");
384 return vfxDef->get(name);
388 return particleDef->get(name,relaxed);
392 return particleDef->get(base,key);
402 auto s =
sfx->load(sndDev,loop);
405 for(
size_t i=0;i<sndStorage.size();){
406 if(sndStorage[i].isFinished()){
407 sndStorage[i]=std::move(sndStorage.back());
408 sndStorage.pop_back();
413 sndStorage.push_back(std::move(s));
418 auto s = sndDev.load(
sfx);
421 for(
size_t i=0;i<sndStorage.size();){
422 if(sndStorage[i].isFinished()){
423 sndStorage[i]=std::move(sndStorage.back());
424 sndStorage.pop_back();
429 sndStorage.push_back(std::move(s));
436 for(
size_t i=0;i<sndStorage.size();){
437 if(sndStorage[i].isFinished()){
438 sndStorage[i]=std::move(sndStorage.back());
439 sndStorage.pop_back();
444 sndStorage.push_back(std::move(s));
448 return instance->inventoryOrder;
476 while(w->owner()!=
nullptr) {
479 if(
auto window =
dynamic_cast<const MainWindow*
>(w))
480 return window->uiScale() * mul;
497 return instance->opts;
501 return loadingFlag.load();
510 if(pendingGame!=
nullptr)
511 game = std::move(pendingGame);
512 saveTex = Texture2d();
513 loadTex = Texture2d();
521 const std::function<std::unique_ptr<GameSession>(std::unique_ptr<GameSession>&&)> f) {
522 saveTex = std::move(tex);
523 implStartLoadSave(
"",
false,f);
527 const std::function<std::unique_ptr<GameSession>(std::unique_ptr<GameSession>&&)> f) {
528 implStartLoadSave(banner,
true,f);
531void Gothic::implStartLoadSave(std::string_view banner,
533 const std::function<std::unique_ptr<GameSession>(std::unique_ptr<GameSession>&&)> f) {
534 loadTex = banner.empty() ? Texture2d() :
Resources::loadTextureUncached(banner);
535 loadProgress.store(0);
539 if(!loadingFlag.compare_exchange_strong(zero,one)){
546 auto l = std::thread([
this,f,g,one]()
noexcept {
548 std::unique_ptr<GameSession> game(g);
549 std::unique_ptr<GameSession> next;
553 next = f(std::move(game));
554 pendingGame = std::move(next);
557 catch(std::bad_alloc&){
558 Tempest::Log::e(
"loading error: out of memory");
559 loadingFlag.compare_exchange_strong(curState,err);
561 catch(std::system_error& e){
562 Tempest::Log::e(
"loading error: ", e.what());
563 loadingFlag.compare_exchange_strong(curState,err);
565 catch(std::runtime_error& e){
566 Tempest::Log::e(
"loading error: ",e.what());
567 loadingFlag.compare_exchange_strong(curState,err);
569 catch(std::bad_function_call&){
570 Tempest::Log::e(
"loading error: bad_function_call");
571 loadingFlag.compare_exchange_strong(curState,err);
573 catch(
const std::exception&e) {
574 Tempest::Log::e(
"loading error: ", e.what());
575 loadingFlag.compare_exchange_strong(curState,err);
579 pendingGame = std::move(game);
582 loaderTh=std::move(l);
602 pendingChapter=
false;
612 game->updateAnimation(dt);
616 save(
"save_slot_0.sav",
"Quick save");
620 load(
"save_slot_0.sav");
632 return game->updateDialog(dlg,
player,npc);
636 game->dialogExec(dlg,
player,npc);
652 return *instance->fight;
656 return *instance->soundDef;
660 return *instance->music;
664 return *instance->camDef;
670 return game->messageFromSvm(
id,voice);
676 return game->messageByName(
id);
682 return game->messageTime(
id);
706 if(game && game->script())
707 return game->script()->menuMain();
715 auto vm = std::make_unique<zenkit::DaedalusVm>(std::move(sc), zenkit::DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS);
723 zenkit::DaedalusScript script;
724 script.load(buf.get());
728 const size_t segment = datFile.find_last_of(
"\\/");
731 zenkit::DaedalusScript script;
732 script.load(buf.get());
736 char16_t str16[256] = {};
737 for(
size_t i=0; i<datFile.size() && i<255; ++i)
738 str16[i] =
char16_t(datFile[i]);
747 datFile = datFile.substr(segment+1);
748 for(
size_t i=0; i<datFile.size() && i<255; ++i)
749 str16[i] =
char16_t(datFile[i]);
750 str16[datFile.size()] = u
'\0';
755 auto buf = zenkit::Read::from(path);
756 zenkit::DaedalusScript script;
757 script.load(buf.get());
763 auto registerIfExists = [&](std::string_view sym,
auto d) {
764 if(sc.find_symbol_by_name(sym) !=
nullptr) {
769 auto registerOpaque = [&](std::string_view sym){
770 if(
auto ptr = sc.find_symbol_by_name(sym)) {
771 sc.register_as_opaque(ptr);
775 registerIfExists(
"C_GILVALUES", zenkit::IGuildValues::register_);
776 registerIfExists(
"C_NPC", zenkit::INpc::register_);
777 registerIfExists(
"C_MISSION", zenkit::IMission::register_);
778 registerIfExists(
"C_ITEM", zenkit::IItem::register_);
779 registerIfExists(
"C_FOCUS", zenkit::IFocus::register_);
780 registerIfExists(
"C_INFO", zenkit::IInfo::register_);
781 registerIfExists(
"C_ITEMREACT", zenkit::IItemReact::register_);
782 registerIfExists(
"C_SPELL", zenkit::ISpell::register_);
783 registerOpaque (
"C_SVM");
784 registerIfExists(
"C_MENU", zenkit::IMenu::register_);
785 registerIfExists(
"C_MENU_ITEM", zenkit::IMenuItem::register_);
786 registerIfExists(
"CCAMSYS", zenkit::ICamera::register_);
787 registerIfExists(
"C_MUSICSYS_CFG", zenkit::IMusicSystem::register_);
788 registerIfExists(
"C_MUSICTHEME", zenkit::IMusicTheme::register_);
789 registerIfExists(
"C_MUSICJINGLE", zenkit::IMusicJingle::register_);
790 registerIfExists(
"C_PARTICLEFX", zenkit::IParticleEffect::register_);
791 registerIfExists(
"CFX_BASE", zenkit::IEffectBase::register_);
792 registerIfExists(
"C_PARTICLEFXEMITKEY", zenkit::IParticleEffectEmitKey::register_);
793 registerOpaque (
"C_FIGHTAI");
794 registerIfExists(
"C_SFX", zenkit::ISoundEffect::register_);
795 registerIfExists(
"C_SNDSYS_CFG", zenkit::ISoundSystem::register_);
799 if(instance->iniFile->has(sec))
801 if(instance->baseIniFile->has(sec))
810 if(instance->iniFile->has(sec,name))
811 return instance->iniFile->getI(sec,name);
812 if(instance->baseIniFile->has(sec,name))
813 return instance->baseIniFile->getI(sec,name);
814 if(instance->defaults->has(sec,name))
815 return instance->defaults->getI(sec,name);
820 instance->iniFile->set(sec,name,val);
827 if(instance->iniFile->has(sec,name))
828 return instance->iniFile->getS(sec,name);
829 if(instance->baseIniFile->has(sec,name))
830 return instance->baseIniFile->getS(sec,name);
831 if(instance->defaults->has(sec,name))
832 return instance->defaults->getS(sec,name);
837 instance->iniFile->set(sec,name,val);
844 if(instance->iniFile->has(sec,name))
845 return instance->iniFile->getF(sec,name);
846 if(instance->baseIniFile->has(sec,name))
847 return instance->baseIniFile->getF(sec,name);
848 if(instance->defaults->has(sec,name))
849 return instance->defaults->getF(sec,name);
854 instance->iniFile->set(sec,name,val);
859 instance->iniFile->flush();
862void Gothic::detectGothicVersion() {
866 if(gpath.find(u
"Gothic/")!=std::string::npos || gpath.find(u
"gothic/")!=std::string::npos)
873 if(baseIniFile->has(
"KEYSDEFAULT1"))
875 if(baseIniFile->has(
"GAME",
"PATCHVERSION"))
877 if(baseIniFile->has(
"GAME",
"useGothic1Controls"))
880 if(score[1]>score[2])
885 vinfo.
patch = baseIniFile->getI(
"GAME",
"PATCHVERSION");
901void Gothic::setupSettings() {
903 game->setupSettings();
905 const float soundVolume =
settingsGetF(
"SOUND",
"soundVolume");
906 sndDev.setGlobalVolume(soundVolume);
909 while(!ord.empty()) {
910 auto l = ord.find_first_of(
" \t,");
911 auto name = ord.substr(0,l);
913 if(l!=std::string::npos)
914 ord = ord.substr(l+1);
else
915 ord = std::string_view();
920 else if(name==
"POTION")
922 else if(name==
"FOOD")
924 else if(name==
"ARMOR")
926 else if(name==
"MAGIC")
928 else if(name==
"RUNE")
930 else if(name==
"DOCS")
932 else if(name==
"OTHER")
934 else if(name==
"NONE")
938 inventoryOrder.push_back(v);
942std::unique_ptr<DocumentMenu::Show>& Gothic::getDocument(
int id) {
943 if(
id>=0 &&
size_t(
id)<documents.size())
944 return documents[size_t(
id)];
945 static std::unique_ptr<DocumentMenu::Show> empty;
949std::u16string
Gothic::nestedPath(
const std::initializer_list<const char16_t*> &name, Tempest::Dir::FileType type) {
954 vm.register_default_external([](std::string_view name) { notImplementedRoutine(std::string {name}); });
956 if(
auto sym = vm.find_symbol_by_name(
"C_SVM"))
957 vm.register_as_opaque(sym);
958 if(
auto sym = vm.find_symbol_by_name(
"C_FIGHTAI"))
959 vm.register_as_opaque(sym);
961 vm.register_external(
"concatstrings", [](std::string_view a, std::string_view b) {
return Gothic::concatstrings(a, b);});
962 vm.register_external(
"inttostring", [](
int i) {
return Gothic::inttostring(i); });
963 vm.register_external(
"floattostring", [](
float f) {
return Gothic::floattostring(f); });
964 vm.register_external(
"inttofloat", [](
int i) {
return Gothic::inttofloat(i); });
965 vm.register_external(
"floattoint", [](
float f) {
return Gothic::floattoint(f); });
967 vm.register_external(
"hlp_strcmp", [](std::string_view a, std::string_view b) {
return Gothic::hlp_strcmp(a, b); });
968 vm.register_external(
"hlp_random", [
this](
int max) {
return hlp_random(max); });
970 vm.register_external(
"introducechapter", [
this](std::string_view title, std::string_view subtitle, std::string_view img, std::string_view sound,
int time){ introducechapter(title, subtitle, img, sound, time); });
971 vm.register_external(
"playvideo", [
this](std::string_view name){
return playvideo(name); });
972 vm.register_external(
"playvideoex", [
this](std::string_view name,
bool a,
bool b){
return playvideoex(name, a, b); });
973 vm.register_external(
"printscreen", [
this](std::string_view msg,
int posx,
int posy, std::string_view font,
int timesec){
return printscreen(msg, posx, posy, font, timesec); });
974 vm.register_external(
"printdialog", [
this](
int dialognr, std::string_view msg,
int posx,
int posy, std::string_view font,
int timesec){
return printdialog(dialognr, msg, posx, posy, font, timesec); });
975 vm.register_external(
"print", [
this](std::string_view msg){ print(msg); });
977 vm.register_external(
"doc_create", [
this](){
return doc_create(); });
978 vm.register_external(
"doc_createmap", [
this](){
return doc_createmap(); });
979 vm.register_external(
"doc_setpage", [
this](
int handle,
int page, std::string_view img,
int scale){ doc_setpage(handle, page, img, scale); });
980 vm.register_external(
"doc_setpages", [
this](
int handle,
int count){ doc_setpages(handle, count); });
981 vm.register_external(
"doc_printline", [
this](
int handle,
int page, std::string_view text){ doc_printline(handle, page, text); });
982 vm.register_external(
"doc_printlines", [
this](
int handle,
int page, std::string_view text){ doc_printlines(handle, page, text); });
983 vm.register_external(
"doc_setmargins", [
this](
int handle,
int page,
int left,
int top,
int right,
int bottom,
int mul){ doc_setmargins(handle, page, left, top, right, bottom, mul); });
984 vm.register_external(
"doc_setfont", [
this](
int handle,
int page, std::string_view font){ doc_setfont(handle, page, font); });
985 vm.register_external(
"doc_setlevel", [
this](
int handle, std::string_view level){ doc_setlevel(handle, level); });
986 vm.register_external(
"doc_setlevelcoords", [
this](
int handle,
int left,
int top,
int right,
int bottom){ doc_setlevelcoords(handle, left, top, right, bottom); });
987 vm.register_external(
"doc_show", [
this](
int handle){ doc_show(handle); });
989 vm.register_external(
"exitgame", [
this](){ exitgame(); });
991 vm.register_external(
"printdebug", [
this](std::string_view msg){ printdebug(msg); });
992 vm.register_external(
"printdebugch", [
this](
int ch, std::string_view msg){ printdebugch(ch, msg); });
993 vm.register_external(
"printdebuginst", [
this](std::string_view msg){ printdebuginst(msg); });
994 vm.register_external(
"printdebuginstch", [
this](
int ch, std::string_view msg){ printdebuginstch(ch, msg); });
997void Gothic::notImplementedRoutine(std::string_view fn) {
998 static std::set<std::string, std::less<>> s;
1000 if(s.find(fn)==s.end()){
1001 auto [v, _] = s.insert(std::string {fn});
1002 Log::e(
"not implemented call [",v->c_str(),
"]");
1006std::string Gothic::concatstrings(std::string_view a, std::string_view b) {
1007 return std::string {a} + std::string {b};
1010std::string Gothic::inttostring(
int i){
1011 return std::to_string(i);
1014std::string Gothic::floattostring(
float f) {
1015 return std::to_string(f);
1018int Gothic::floattoint(
float f) {
1019 return static_cast<int>(f);
1022float Gothic::inttofloat(
int i) {
1023 return static_cast<float>(i);
1026int Gothic::hlp_random(
int max) {
1027 auto mod = uint32_t(std::max(1, max));
1028 return static_cast<int32_t
>(randGen() % mod);
1031bool Gothic::hlp_strcmp(std::string_view a, std::string_view b) {
1035void Gothic::introducechapter(std::string_view title, std::string_view subtitle, std::string_view img, std::string_view sound,
int time) {
1036 pendingChapter =
true;
1045bool Gothic::playvideo(std::string_view name) {
1050bool Gothic::playvideoex(std::string_view name,
bool,
bool) {
1055bool Gothic::printscreen(std::string_view msg,
int posx,
int posy, std::string_view font,
int timesec) {
1060bool Gothic::printdialog(
int, std::string_view msg,
int posx,
int posy, std::string_view font,
int timesec) {
1065void Gothic::print(std::string_view msg) {
1069int Gothic::doc_create() {
1070 for(
size_t i=0;i<documents.size();++i){
1071 if(documents[i]==
nullptr){
1073 return static_cast<int>(i);
1077 return static_cast<int>(documents.size()) - 1;
1080int Gothic::doc_createmap() {
1081 for(
size_t i=0;i<documents.size();++i){
1082 if(documents[i]==
nullptr){
1084 return static_cast<int>(i);
1088 return static_cast<int>(documents.size())-1;
1091void Gothic::doc_setpage(
int handle,
int page, std::string_view img,
int scale) {
1095 auto& doc = getDocument(handle);
1098 if(page>=0 &&
size_t(page)<doc->pages.size()){
1099 auto& pg = doc->pages[size_t(page)];
1107void Gothic::doc_setpages(
int handle,
int count) {
1108 auto& doc = getDocument(handle);
1109 if(doc!=
nullptr && count>=0 && count<1024){
1110 doc->pages.resize(
size_t(count));
1114void Gothic::doc_printline(
int handle,
int page, std::string_view text) {
1115 auto& doc = getDocument(handle);
1116 if(doc!=
nullptr && page>=0 &&
size_t(page)<doc->pages.size()){
1117 doc->pages[size_t(page)].text += text;
1118 doc->pages[size_t(page)].text +=
"\n";
1122void Gothic::doc_printlines(
int handle,
int page, std::string_view text) {
1123 auto& doc = getDocument(handle);
1124 if(doc!=
nullptr && page>=0 &&
size_t(page)<doc->pages.size()){
1125 doc->pages[size_t(page)].text += text;
1126 doc->pages[size_t(page)].text +=
"\n";
1130void Gothic::doc_setmargins(
int handle,
int page,
int left,
int top,
int right,
int bottom,
int mul) {
1136 auto& doc = getDocument(handle);
1139 if(page>=0 &&
size_t(page)<doc->pages.size()){
1140 auto& pg = doc->pages[size_t(page)];
1141 pg.margins = Tempest::Margin(left,right,top,bottom);
1144 doc->margins = Tempest::Margin(left,right,top,bottom);
1148void Gothic::doc_setfont(
int handle,
int page, std::string_view font) {
1149 auto& doc = getDocument(handle);
1153 if(page>=0 &&
size_t(page)<doc->pages.size()){
1154 auto& pg = doc->pages[size_t(page)];
1162void Gothic::doc_show(
int handle) {
1163 auto& doc = getDocument(handle);
1169 while(documents.size()>0 && documents.back()==
nullptr)
1170 documents.pop_back();
1173void Gothic::doc_setlevel(
int handle, std::string_view level) {
1174 auto& doc = getDocument(handle);
1178 std::string str {level};
1179 size_t bg = str.rfind(
'\\');
1180 if(bg!=std::string::npos)
1181 str = str.substr(bg+1);
1184 i = char(std::tolower(i));
1186 if(
auto w =
world()) {
1187 doc->showPlayer = (w->name()==str);
1191void Gothic::doc_setlevelcoords(
int handle,
int left,
int top,
int right,
int bottom) {
1192 auto& doc = getDocument(handle);
1195 doc->wbounds = Rect(left,top,right-left,bottom-top);
1198void Gothic::exitgame() {
1199 Log::i(
"Exiting, by script call (`exitgame`)");
1200 Tempest::SystemApi::exit();
1203void Gothic::printdebug(std::string_view msg) {
1205 Log::d(
"[zspy]: ",msg);
1208void Gothic::printdebugch(
int ch, std::string_view msg) {
1210 Log::d(
"[zspy,",ch,
"]: ",msg);
1213void Gothic::printdebuginst(std::string_view msg) {
1215 Log::d(
"[zspy]: ",msg);
1218void Gothic::printdebuginstch(
int ch, std::string_view msg) {
1220 Log::d(
"[zspy,",ch,
"]: ",msg);
std::string_view defaultSave() const
bool isVirtualShadow() const
bool isSoftwareShadow() const
std::u16string nestedPath(const std::initializer_list< const char16_t * > &name, Tempest::Dir::FileType type) const
std::u16string_view modPath() const
bool isMeshShading() const
std::u16string scriptPath() const
static const CommandLine & inst()
std::u16string_view rootPath() const
void dialogExec(const GameScript::DlgChoice &dlg, Npc &player, Npc &npc)
static std::u16string nestedPath(const std::initializer_list< const char16_t * > &name, Tempest::Dir::FileType type)
static void settingsSetI(std::string_view sec, std::string_view name, int val)
Tempest::Signal< void()> onSettingsChanged
bool isBenchmarkModeCi() const
auto loadParticleFx(std::string_view name, bool relaxed=false) -> const ParticleFx *
void setupGlobalScripts()
std::string_view defaultPlayer() const
static auto options() -> const Options &
Tempest::Signal< void(std::string_view, int, int, int, const GthFont &)> onPrintScreen
void openDialogPipe(Npc &player, Npc &npc, AiOuputPipe *&pipe)
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
void load(std::string_view slot)
static void settingsSetS(std::string_view sec, std::string_view name, std::string_view val)
bool isBenchmarkMode() const
Tempest::Signal< void(Npc &, Npc &, AiOuputPipe *&)> onDialogPipe
auto updateDialog(const GameScript::DlgChoice &dlg, Npc &player, Npc &npc) -> std::vector< GameScript::DlgChoice >
std::string_view defaultSave() const
std::string_view defaultWorld() const
static const CameraDefinitions & cameraDef()
zenkit::DaedalusScript loadScript(std::string_view datFile, const ScriptLang lang)
auto questLog() const -> const QuestLog *
static const FightAi & fai()
std::string_view menuMain() const
auto version() const -> const VersionInfo &
auto clearGame() -> std::unique_ptr< GameSession >
void startLoad(std::string_view banner, const std::function< std::unique_ptr< GameSession >(std::unique_ptr< GameSession > &&)> f)
bool isNpcInDialog(const Npc &npc) const
Tempest::Signal< void()> onStartLoading
SoundFx * loadSoundFx(std::string_view name)
std::unique_ptr< zenkit::DaedalusVm > createPhoenixVm(std::string_view datFile, const ScriptLang lang=ScriptLang::NONE)
auto gameSession() const -> const GameSession *
static int settingsGetI(std::string_view sec, std::string_view name)
Tempest::Signal< void(std::string_view)> onVideo
void save(std::string_view slot, std::string_view usrName)
static const MusicDefinitions & musicDef()
WorldView * worldView() const
std::string_view defaultGameDatFile() const
int loadingProgress() const
Tempest::Signal< void(const DocumentMenu::Show &)> onShowDocument
static void flushSettings()
uint32_t messageTime(std::string_view id) const
static std::string_view settingsGetS(std::string_view sec, std::string_view name)
void setupVmCommonApi(zenkit::DaedalusVm &vm)
bool isInGameAndAlive() const
bool isMarvinEnabled() const
static bool settingsHasSection(std::string_view sec)
static const SoundDefinitions & sfx()
static void settingsSetF(std::string_view sec, std::string_view name, float val)
std::string_view defaultOutputUnits() const
static float settingsGetF(std::string_view sec, std::string_view name)
auto loadingBanner() const -> const Tempest::Texture2d *
void setBenchmarkMode(Benchmark b)
void setLoadingProgress(int v)
std::function< bool(const Npc *)> isNpcInDialogFn
void setGame(std::unique_ptr< GameSession > &&w)
void emitGlobalSound(std::string_view sfx)
Tempest::Signal< void(const ChapterScreen::Show &)> onIntroChapter
void emitGlobalSoundWav(std::string_view wav)
SoundFx * loadSoundWavFx(std::string_view name)
Tempest::Signal< void(std::string_view)> onPrint
void setMarvinEnabled(bool m)
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)
std::string_view messageFromSvm(std::string_view id, int voice) const
void setupCommonScriptClasses(zenkit::DaedalusScript &sc)
std::string_view messageByName(std::string_view id) const
static auto invCatOrder() -> const std::vector< ItmFlags > &
auto loadVisualFx(std::string_view name) -> const VisualFx *
Tempest::Signal< void()> onWorldLoaded
Main application window handling game rendering and input.
static void loadVdfs(const std::vector< std::u16string > &modvdfs, bool modFilter)
static const GthFont & font(const float scale)
static bool hasFile(std::string_view fname)
static Tempest::Device & device()
static std::unique_ptr< zenkit::Read > getFileBuffer(std::string_view name)
static Tempest::Sound loadSoundBuffer(std::string_view name)
static void mountWork(const std::filesystem::path &path)
static bool isRtsmSupported()
static bool isVsmSupported()
static void setThreadName(const char *threadName)
static bool hasMeshShader()
static bool hasBindless()
Main application window for OpenGothic.
bool exists(const std::u16string &path)
std::u16string caseInsensitiveSegment(std::u16string_view path, const char16_t *segment, Tempest::Dir::FileType type)
Tempest::Size saveGameImageSize
Tempest::Size newChapterSize