OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
gamescript.cpp
Go to the documentation of this file.
1#include "gamescript.h"
2
3#include <Tempest/Log>
4#include <Tempest/SoundEffect>
5
6#include <cctype>
7
10#include "game/serialize.h"
11#include "utils/string_frm.h"
12#include "world/objects/npc.h"
13#include "world/objects/item.h"
16#include "graphics/visualfx.h"
17#include "utils/fileutil.h"
18#include "commandline.h"
19#include "gothic.h"
20
21using namespace Tempest;
22
23template <typename T>
24struct ScopeVar final {
25 ScopeVar(zenkit::DaedalusSymbol& sym, const std::shared_ptr<T>& h) : prev(sym.get_instance()), sym(sym) {
26 sym.set_instance(h);
27 }
28
29 ScopeVar(const ScopeVar&)=delete;
31 sym.set_instance(prev);
32 }
33
34 std::shared_ptr<zenkit::DaedalusInstance> prev;
35 zenkit::DaedalusSymbol& sym;
36 };
37
38struct ScopeCtx final {
40 prev = script.aiProcessPolicy;
41 script.aiProcessPolicy = pp;
42 }
43
45 script.aiProcessPolicy = prev;
46 }
47
50 };
51
52
53bool GameScript::GlobalOutput::output(Npc& npc, std::string_view text) {
54 return owner.aiOutput(npc,text,false);
55 }
56
57bool GameScript::GlobalOutput::outputSvm(Npc &npc, std::string_view text) {
58 return owner.aiOutputSvm(npc,text,false);
59 }
60
61bool GameScript::GlobalOutput::outputOv(Npc &npc, std::string_view text) {
62 return owner.aiOutputSvm(npc,text,true);
63 }
64
65bool GameScript::GlobalOutput::printScr(Npc& npc, int time, std::string_view msg, int x, int y, std::string_view font) {
66 auto& f = Resources::font(font, Resources::FontType::Normal, 1.0);
67 Gothic::inst().onPrintScreen(msg,x,y,time,f);
68 return true;
69 }
70
71bool GameScript::GlobalOutput::isFinished() {
72 return true;
73 }
74
76 for(auto& i:range)
77 i = -1;
78 }
79
80int GameScript::PerDist::at(PercType perc, int r) const {
81 if(perc>=PERC_Count)
82 return r;
83 auto rr = range[perc];
84 if(rr>0)
85 return rr;
86 return r;
87 }
88
89
91 :owner(owner), vm(createVm(Gothic::inst())) {
92 if (vm.global_self() == nullptr || vm.global_other() == nullptr || vm.global_item() == nullptr ||
93 vm.global_victim() == nullptr || vm.global_hero() == nullptr)
94 throw std::runtime_error("Cannot find script symbol SELF, OTHER, ITEM, VICTIM, or HERO! Cannot proceed!");
95
96 vmLang = Gothic::inst().settingsGetI("GAME", "language");
97 vm.register_exception_handler(zenkit::lenient_vm_exception_handler);
100 aiDefaultPipe.reset(new GlobalOutput(*this));
101 initCommon();
102 initSettings();
103 Gothic::inst().onSettingsChanged.bind(this,&GameScript::initSettings);
104 }
105
107 Gothic::inst().onSettingsChanged.ubind(this,&GameScript::initSettings);
108 }
109
110void GameScript::initCommon() {
111 bindExternal("hlp_random", &GameScript::hlp_random);
112 bindExternal("hlp_isvalidnpc", &GameScript::hlp_isvalidnpc);
113 bindExternal("hlp_isvaliditem", &GameScript::hlp_isvaliditem);
114 bindExternal("hlp_isitem", &GameScript::hlp_isitem);
115 bindExternal("hlp_getnpc", &GameScript::hlp_getnpc);
116 bindExternal("hlp_getinstanceid", &GameScript::hlp_getinstanceid);
117
118 bindExternal("wld_insertnpc", &GameScript::wld_insertnpc);
119 bindExternal("wld_removenpc", &GameScript::wld_removenpc);
120 bindExternal("wld_insertitem", &GameScript::wld_insertitem);
121 bindExternal("wld_settime", &GameScript::wld_settime);
122 bindExternal("wld_getday", &GameScript::wld_getday);
123 bindExternal("wld_playeffect", &GameScript::wld_playeffect);
124 bindExternal("wld_stopeffect", &GameScript::wld_stopeffect);
125 bindExternal("wld_getplayerportalguild", &GameScript::wld_getplayerportalguild);
126 bindExternal("wld_getformerplayerportalguild", &GameScript::wld_getformerplayerportalguild);
127 bindExternal("wld_setguildattitude", &GameScript::wld_setguildattitude);
128 bindExternal("wld_getguildattitude", &GameScript::wld_getguildattitude);
129 bindExternal("wld_exchangeguildattitudes", &GameScript::wld_exchangeguildattitudes);
130 bindExternal("wld_istime", &GameScript::wld_istime);
131 bindExternal("wld_isfpavailable", &GameScript::wld_isfpavailable);
132 bindExternal("wld_isnextfpavailable", &GameScript::wld_isnextfpavailable);
133 bindExternal("wld_ismobavailable", &GameScript::wld_ismobavailable);
134 bindExternal("wld_setmobroutine", &GameScript::wld_setmobroutine);
135 bindExternal("wld_getmobstate", &GameScript::wld_getmobstate);
136 bindExternal("wld_assignroomtoguild", &GameScript::wld_assignroomtoguild);
137 bindExternal("wld_detectnpc", &GameScript::wld_detectnpc);
138 bindExternal("wld_detectnpcex", &GameScript::wld_detectnpcex);
139 bindExternal("wld_detectitem", &GameScript::wld_detectitem);
140 bindExternal("wld_spawnnpcrange", &GameScript::wld_spawnnpcrange);
141 bindExternal("wld_sendtrigger", &GameScript::wld_sendtrigger);
142 bindExternal("wld_senduntrigger", &GameScript::wld_senduntrigger);
143 bindExternal("wld_israining", &GameScript::wld_israining);
144
145 bindExternal("mdl_setvisual", &GameScript::mdl_setvisual);
146 bindExternal("mdl_setvisualbody", &GameScript::mdl_setvisualbody);
147 bindExternal("mdl_setmodelfatness", &GameScript::mdl_setmodelfatness);
148 bindExternal("mdl_applyoverlaymds", &GameScript::mdl_applyoverlaymds);
149 bindExternal("mdl_applyoverlaymdstimed", &GameScript::mdl_applyoverlaymdstimed);
150 bindExternal("mdl_removeoverlaymds", &GameScript::mdl_removeoverlaymds);
151 bindExternal("mdl_setmodelscale", &GameScript::mdl_setmodelscale);
152 bindExternal("mdl_startfaceani", &GameScript::mdl_startfaceani);
153 bindExternal("mdl_applyrandomani", &GameScript::mdl_applyrandomani);
154 bindExternal("mdl_applyrandomanifreq", &GameScript::mdl_applyrandomanifreq);
155 bindExternal("mdl_applyrandomfaceani", &GameScript::mdl_applyrandomfaceani);
156
157 bindExternal("npc_settofightmode", &GameScript::npc_settofightmode);
158 bindExternal("npc_settofistmode", &GameScript::npc_settofistmode);
159 bindExternal("npc_isinstate", &GameScript::npc_isinstate);
160 bindExternal("npc_isinroutine", &GameScript::npc_isinroutine);
161 bindExternal("npc_wasinstate", &GameScript::npc_wasinstate);
162 bindExternal("npc_getdisttowp", &GameScript::npc_getdisttowp);
163 bindExternal("npc_exchangeroutine", &GameScript::npc_exchangeroutine);
164 bindExternal("npc_isdead", &GameScript::npc_isdead);
165 bindExternal("npc_knowsinfo", &GameScript::npc_knowsinfo);
166 bindExternal("npc_settalentskill", &GameScript::npc_settalentskill);
167 bindExternal("npc_gettalentskill", &GameScript::npc_gettalentskill);
168 bindExternal("npc_settalentvalue", &GameScript::npc_settalentvalue);
169 bindExternal("npc_gettalentvalue", &GameScript::npc_gettalentvalue);
170 bindExternal("npc_setrefusetalk", &GameScript::npc_setrefusetalk);
171 bindExternal("npc_refusetalk", &GameScript::npc_refusetalk);
172 bindExternal("npc_hasitems", &GameScript::npc_hasitems);
173 bindExternal("npc_hasspell", &GameScript::npc_hasspell);
174 bindExternal("npc_getinvitem", &GameScript::npc_getinvitem);
175 bindExternal("npc_getinvitembyslot", &GameScript::npc_getinvitembyslot);
176 bindExternal("npc_removeinvitem", &GameScript::npc_removeinvitem);
177 bindExternal("npc_removeinvitems", &GameScript::npc_removeinvitems);
178 bindExternal("npc_getbodystate", &GameScript::npc_getbodystate);
179 bindExternal("npc_getlookattarget", &GameScript::npc_getlookattarget);
180 bindExternal("npc_getdisttonpc", &GameScript::npc_getdisttonpc);
181 bindExternal("npc_hasequippedarmor", &GameScript::npc_hasequippedarmor);
182 bindExternal("npc_setperctime", &GameScript::npc_setperctime);
183 bindExternal("npc_percenable", &GameScript::npc_percenable);
184 bindExternal("npc_percdisable", &GameScript::npc_percdisable);
185 bindExternal("npc_getnearestwp", &GameScript::npc_getnearestwp);
186 bindExternal("npc_getnextwp", &GameScript::npc_getnextwp);
187 bindExternal("npc_clearaiqueue", &GameScript::npc_clearaiqueue);
188 bindExternal("npc_isplayer", &GameScript::npc_isplayer);
189 bindExternal("npc_getstatetime", &GameScript::npc_getstatetime);
190 bindExternal("npc_setstatetime", &GameScript::npc_setstatetime);
191 bindExternal("npc_changeattribute", &GameScript::npc_changeattribute);
192 bindExternal("npc_isonfp", &GameScript::npc_isonfp);
193 bindExternal("npc_getheighttonpc", &GameScript::npc_getheighttonpc);
194 bindExternal("npc_getequippedmeleeweapon", &GameScript::npc_getequippedmeleeweapon);
195 bindExternal("npc_getequippedrangedweapon", &GameScript::npc_getequippedrangedweapon);
196 bindExternal("npc_getequippedarmor", &GameScript::npc_getequippedarmor);
197 bindExternal("npc_canseenpc", &GameScript::npc_canseenpc);
198 bindExternal("npc_canseenpcfreelos", &GameScript::npc_canseenpcfreelos);
199 bindExternal("npc_canseeitem", &GameScript::npc_canseeitem);
200 bindExternal("npc_hasequippedweapon", &GameScript::npc_hasequippedweapon);
201 bindExternal("npc_hasequippedmeleeweapon", &GameScript::npc_hasequippedmeleeweapon);
202 bindExternal("npc_hasequippedrangedweapon", &GameScript::npc_hasequippedrangedweapon);
203 bindExternal("npc_getactivespell", &GameScript::npc_getactivespell);
204 bindExternal("npc_getactivespellisscroll", &GameScript::npc_getactivespellisscroll);
205 bindExternal("npc_getactivespellcat", &GameScript::npc_getactivespellcat);
206 bindExternal("npc_setactivespellinfo", &GameScript::npc_setactivespellinfo);
207 bindExternal("npc_getactivespelllevel", &GameScript::npc_getactivespelllevel);
208 bindExternal("npc_isinfightmode", &GameScript::npc_isinfightmode);
209 bindExternal("npc_settarget", &GameScript::npc_settarget);
210 bindExternal("npc_gettarget", &GameScript::npc_gettarget);
211 bindExternal("npc_getnexttarget", &GameScript::npc_getnexttarget);
212 bindExternal("npc_sendpassiveperc", &GameScript::npc_sendpassiveperc);
213 bindExternal("npc_sendsingleperc", &GameScript::npc_sendsingleperc);
214 bindExternal("npc_checkinfo", &GameScript::npc_checkinfo);
215 bindExternal("npc_getportalguild", &GameScript::npc_getportalguild);
216 bindExternal("npc_isinplayersroom", &GameScript::npc_isinplayersroom);
217 bindExternal("npc_getreadiedweapon", &GameScript::npc_getreadiedweapon);
218 bindExternal("npc_hasreadiedweapon", &GameScript::npc_hasreadiedweapon);
219 bindExternal("npc_hasreadiedmeleeweapon", &GameScript::npc_hasreadiedmeleeweapon);
220 bindExternal("npc_hasreadiedrangedweapon", &GameScript::npc_hasreadiedrangedweapon);
221 bindExternal("npc_hasrangedweaponwithammo", &GameScript::npc_hasrangedweaponwithammo);
222 bindExternal("npc_isdrawingspell", &GameScript::npc_isdrawingspell);
223 bindExternal("npc_isdrawingweapon", &GameScript::npc_isdrawingweapon);
224 bindExternal("npc_perceiveall", &GameScript::npc_perceiveall);
225 bindExternal("npc_stopani", &GameScript::npc_stopani);
226 bindExternal("npc_settrueguild", &GameScript::npc_settrueguild);
227 bindExternal("npc_gettrueguild", &GameScript::npc_gettrueguild);
228 bindExternal("npc_clearinventory", &GameScript::npc_clearinventory);
229 bindExternal("npc_getattitude", &GameScript::npc_getattitude);
230 bindExternal("npc_getpermattitude", &GameScript::npc_getpermattitude);
231 bindExternal("npc_setattitude", &GameScript::npc_setattitude);
232 bindExternal("npc_settempattitude", &GameScript::npc_settempattitude);
233 bindExternal("npc_hasbodyflag", &GameScript::npc_hasbodyflag);
234 bindExternal("npc_getlasthitspellid", &GameScript::npc_getlasthitspellid);
235 bindExternal("npc_getlasthitspellcat", &GameScript::npc_getlasthitspellcat);
236 bindExternal("npc_playani", &GameScript::npc_playani);
237 bindExternal("npc_isdetectedmobownedbynpc", &GameScript::npc_isdetectedmobownedbynpc);
238 bindExternal("npc_getdetectedmob", &GameScript::npc_getdetectedmob);
239 bindExternal("npc_isdetectedmobownedbyguild", &GameScript::npc_isdetectedmobownedbyguild);
240 bindExternal("npc_ownedbynpc", &GameScript::npc_ownedbynpc);
241 bindExternal("npc_canseesource", &GameScript::npc_canseesource);
242 bindExternal("npc_isincutscene", &GameScript::npc_isincutscene);
243 bindExternal("npc_getdisttoitem", &GameScript::npc_getdisttoitem);
244 bindExternal("npc_getheighttoitem", &GameScript::npc_getheighttoitem);
245 bindExternal("npc_getdisttoplayer", &GameScript::npc_getdisttoplayer);
246
247 bindExternal("ai_output", &GameScript::ai_output);
248 bindExternal("ai_stopprocessinfos", &GameScript::ai_stopprocessinfos);
249 bindExternal("ai_processinfos", &GameScript::ai_processinfos);
250 bindExternal("ai_standup", &GameScript::ai_standup);
251 bindExternal("ai_standupquick", &GameScript::ai_standupquick);
252 bindExternal("ai_continueroutine", &GameScript::ai_continueroutine);
253 bindExternal("ai_stoplookat", &GameScript::ai_stoplookat);
254 bindExternal("ai_lookat", &GameScript::ai_lookat);
255 bindExternal("ai_lookatnpc", &GameScript::ai_lookatnpc);
256 bindExternal("ai_removeweapon", &GameScript::ai_removeweapon);
257 bindExternal("ai_unreadyspell", &GameScript::ai_unreadyspell);
258 bindExternal("ai_turnaway", &GameScript::ai_turnaway);
259 bindExternal("ai_turntonpc", &GameScript::ai_turntonpc);
260 bindExternal("ai_whirlaround", &GameScript::ai_whirlaround);
261 bindExternal("ai_outputsvm", &GameScript::ai_outputsvm);
262 bindExternal("ai_outputsvm_overlay", &GameScript::ai_outputsvm_overlay);
263 bindExternal("ai_startstate", &GameScript::ai_startstate);
264 bindExternal("ai_playani", &GameScript::ai_playani);
265 bindExternal("ai_setwalkmode", &GameScript::ai_setwalkmode);
266 bindExternal("ai_wait", &GameScript::ai_wait);
267 bindExternal("ai_waitms", &GameScript::ai_waitms);
268 bindExternal("ai_aligntowp", &GameScript::ai_aligntowp);
269 bindExternal("ai_gotowp", &GameScript::ai_gotowp);
270 bindExternal("ai_gotofp", &GameScript::ai_gotofp);
271 bindExternal("ai_playanibs", &GameScript::ai_playanibs);
272 bindExternal("ai_equiparmor", &GameScript::ai_equiparmor);
273 bindExternal("ai_equipbestarmor", &GameScript::ai_equipbestarmor);
274 bindExternal("ai_equipbestmeleeweapon", &GameScript::ai_equipbestmeleeweapon);
275 bindExternal("ai_equipbestrangedweapon", &GameScript::ai_equipbestrangedweapon);
276 bindExternal("ai_usemob", &GameScript::ai_usemob);
277 bindExternal("ai_teleport", &GameScript::ai_teleport);
278 bindExternal("ai_stoppointat", &GameScript::ai_stoppointat);
279 bindExternal("ai_drawweapon", &GameScript::ai_drawweapon);
280 bindExternal("ai_readymeleeweapon", &GameScript::ai_readymeleeweapon);
281 bindExternal("ai_readyrangedweapon", &GameScript::ai_readyrangedweapon);
282 bindExternal("ai_readyspell", &GameScript::ai_readyspell);
283 bindExternal("ai_attack", &GameScript::ai_attack);
284 bindExternal("ai_flee", &GameScript::ai_flee);
285 bindExternal("ai_dodge", &GameScript::ai_dodge);
286 bindExternal("ai_unequipweapons", &GameScript::ai_unequipweapons);
287 bindExternal("ai_unequiparmor", &GameScript::ai_unequiparmor);
288 bindExternal("ai_gotonpc", &GameScript::ai_gotonpc);
289 bindExternal("ai_gotonextfp", &GameScript::ai_gotonextfp);
290 bindExternal("ai_aligntofp", &GameScript::ai_aligntofp);
291 bindExternal("ai_useitem", &GameScript::ai_useitem);
292 bindExternal("ai_useitemtostate", &GameScript::ai_useitemtostate);
293 bindExternal("ai_setnpcstostate", &GameScript::ai_setnpcstostate);
294 bindExternal("ai_finishingmove", &GameScript::ai_finishingmove);
295 bindExternal("ai_takeitem", &GameScript::ai_takeitem);
296 bindExternal("ai_gotoitem", &GameScript::ai_gotoitem);
297 bindExternal("ai_pointat", &GameScript::ai_pointat);
298 bindExternal("ai_pointatnpc", &GameScript::ai_pointatnpc);
299
300 bindExternal("mob_hasitems", &GameScript::mob_hasitems);
301 bindExternal("ai_printscreen", &GameScript::ai_printscreen);
302
303 bindExternal("ta_min", &GameScript::ta_min);
304
305 bindExternal("log_createtopic", &GameScript::log_createtopic);
306 bindExternal("log_settopicstatus", &GameScript::log_settopicstatus);
307 bindExternal("log_addentry", &GameScript::log_addentry);
308
309 bindExternal("equipitem", &GameScript::equipitem);
310 bindExternal("createinvitem", &GameScript::createinvitem);
311 bindExternal("createinvitems", &GameScript::createinvitems);
312
313 bindExternal("perc_setrange", &GameScript::perc_setrange);
314
315 bindExternal("info_addchoice", &GameScript::info_addchoice);
316 bindExternal("info_clearchoices", &GameScript::info_clearchoices);
317 bindExternal("infomanager_hasfinished", &GameScript::infomanager_hasfinished);
318
319 bindExternal("snd_play", &GameScript::snd_play);
320 bindExternal("snd_play3d", &GameScript::snd_play3d);
321
322 bindExternal("game_initgerman", &GameScript::game_initgerman);
323 bindExternal("game_initenglish", &GameScript::game_initenglish);
324
325 bindExternal("exitsession", &GameScript::exitsession);
326
327 // vm.validateExternals();
328
329 spells = std::make_unique<SpellDefinitions>(vm);
330 svm = std::make_unique<SvmDefinitions>(vm);
331
332 cFocusNorm = findFocus("Focus_Normal");
333 cFocusMelee = findFocus("Focus_Melee");
334 cFocusRange = findFocus("Focus_Ranged");
335 cFocusMage = findFocus("Focus_Magic");
336
337 ZS_Dead = aiState(findSymbolIndex("ZS_Dead")).funcIni;
338 ZS_Unconscious = aiState(findSymbolIndex("ZS_Unconscious")).funcIni;
339 ZS_Talk = aiState(findSymbolIndex("ZS_Talk")).funcIni;
340 ZS_Attack = aiState(findSymbolIndex("ZS_Attack")).funcIni;
341 ZS_MM_Attack = aiState(findSymbolIndex("ZS_MM_Attack")).funcIni;
342
343 spellFxInstanceNames = vm.find_symbol_by_name("spellFxInstanceNames");
344 spellFxAniLetters = vm.find_symbol_by_name("spellFxAniLetters");
345
346 if(spellFxInstanceNames==nullptr || spellFxAniLetters==nullptr) {
347 throw std::runtime_error("spellFxInstanceNames and/or spellFxAniLetters not found");
348 }
349
350 if(owner.version().game==2) {
351 auto* currency = vm.find_symbol_by_name("TRADE_CURRENCY_INSTANCE");
352 itMi_Gold = currency!=nullptr ? vm.find_symbol_by_name(currency->get_string()) : nullptr;
353 if(itMi_Gold!=nullptr){ // FIXME
354 auto item = vm.init_instance<zenkit::IItem>(itMi_Gold);
355 goldTxt = item->name;
356 }
357 auto* tradeMul = vm.find_symbol_by_name("TRADE_VALUE_MULTIPLIER");
358 tradeValMult = tradeMul != nullptr ? tradeMul->get_float() : 1.0f;
359
360 auto* vtime = vm.find_symbol_by_name("VIEW_TIME_PER_CHAR");
361 viewTimePerChar = vtime != nullptr ? vtime->get_float() : 550.f;
362 if(viewTimePerChar<=0.f)
363 viewTimePerChar = 550.f;
364
365 ItKE_lockpick = vm.find_symbol_by_name("ItKE_lockpick");
366 B_RefreshAtInsert = vm.find_symbol_by_name("B_RefreshAtInsert");
367 } else {
368 itMi_Gold = vm.find_symbol_by_name("ItMiNugget");
369 if(itMi_Gold!=nullptr) { // FIXME
370 auto item = vm.init_instance<zenkit::IItem>(itMi_Gold);
371 goldTxt = item->name;
372 }
373 //
374 tradeValMult = 1.f;
375 viewTimePerChar = 550.f;
376 ItKE_lockpick = vm.find_symbol_by_name("itkelockpick");
377 }
378
379 if(auto v = vm.find_symbol_by_name("DAM_CRITICAL_MULTIPLIER")) {
380 damCriticalMultiplier = v->get_int();
381 }
382
383 auto* gilMax = vm.find_symbol_by_name("GIL_MAX");
384 gilCount = gilMax!=nullptr ? size_t(gilMax->get_int()) : 0;
385
386 auto* tblSz = vm.find_symbol_by_name("TAB_ANZAHL");
387 gilTblSize = tblSz!=nullptr ? size_t(std::sqrt(tblSz->get_int())) : 0;
388 gilAttitudes.resize(gilCount*gilCount,ATT_HOSTILE);
389 wld_exchangeguildattitudes("GIL_ATTITUDES");
390
391 auto id = vm.find_symbol_by_name("Gil_Values");
392 if(id!=nullptr){
393 cGuildVal = vm.init_instance<zenkit::IGuildValues>(id);
394 for(size_t i=0;i<Guild::GIL_PUBLIC;++i){
395 cGuildVal->water_depth_knee [i]=cGuildVal->water_depth_knee [Guild::GIL_HUMAN];
396 cGuildVal->water_depth_chest [i]=cGuildVal->water_depth_chest [Guild::GIL_HUMAN];
397 cGuildVal->jumpup_height [i]=cGuildVal->jumpup_height [Guild::GIL_HUMAN];
398 cGuildVal->swim_time [i]=cGuildVal->swim_time [Guild::GIL_HUMAN];
399 cGuildVal->dive_time [i]=cGuildVal->dive_time [Guild::GIL_HUMAN];
400 cGuildVal->step_height [i]=cGuildVal->step_height [Guild::GIL_HUMAN];
401 cGuildVal->jumplow_height [i]=cGuildVal->jumplow_height [Guild::GIL_HUMAN];
402 cGuildVal->jumpmid_height [i]=cGuildVal->jumpmid_height [Guild::GIL_HUMAN];
403 cGuildVal->slide_angle [i]=cGuildVal->slide_angle [Guild::GIL_HUMAN];
404 cGuildVal->slide_angle2 [i]=cGuildVal->slide_angle2 [Guild::GIL_HUMAN];
405 cGuildVal->disable_autoroll [i]=cGuildVal->disable_autoroll [Guild::GIL_HUMAN];
406 cGuildVal->surface_align [i]=cGuildVal->surface_align [Guild::GIL_HUMAN];
407 cGuildVal->climb_heading_angle[i]=cGuildVal->climb_heading_angle[Guild::GIL_HUMAN];
408 cGuildVal->climb_horiz_angle [i]=cGuildVal->climb_horiz_angle [Guild::GIL_HUMAN];
409 cGuildVal->climb_ground_angle [i]=cGuildVal->climb_ground_angle [Guild::GIL_HUMAN];
410 cGuildVal->fight_range_base [i]=cGuildVal->fight_range_base [Guild::GIL_HUMAN];
411 cGuildVal->fight_range_fist [i]=cGuildVal->fight_range_fist [Guild::GIL_HUMAN];
412 cGuildVal->fight_range_g [i]=cGuildVal->fight_range_g [Guild::GIL_HUMAN];
413 cGuildVal->fight_range_1hs [i]=cGuildVal->fight_range_1hs [Guild::GIL_HUMAN];
414 cGuildVal->fight_range_1ha [i]=cGuildVal->fight_range_1ha [Guild::GIL_HUMAN];
415 cGuildVal->fight_range_2hs [i]=cGuildVal->fight_range_2hs [Guild::GIL_HUMAN];
416 cGuildVal->fight_range_2ha [i]=cGuildVal->fight_range_2ha [Guild::GIL_HUMAN];
417 cGuildVal->falldown_height [i]=cGuildVal->falldown_height [Guild::GIL_HUMAN];
418 cGuildVal->falldown_damage [i]=cGuildVal->falldown_damage [Guild::GIL_HUMAN];
419 cGuildVal->blood_disabled [i]=cGuildVal->blood_disabled [Guild::GIL_HUMAN];
420 cGuildVal->blood_max_distance [i]=cGuildVal->blood_max_distance [Guild::GIL_HUMAN];
421 cGuildVal->blood_amount [i]=cGuildVal->blood_amount [Guild::GIL_HUMAN];
422 cGuildVal->blood_flow [i]=cGuildVal->blood_flow [Guild::GIL_HUMAN];
423 cGuildVal->blood_emitter [i]=cGuildVal->blood_emitter [Guild::GIL_HUMAN];
424 cGuildVal->blood_texture [i]=cGuildVal->blood_texture [Guild::GIL_HUMAN];
425 cGuildVal->turn_speed [i]=cGuildVal->turn_speed [Guild::GIL_HUMAN];
426 }
427 }
428
430 dma.reset(new DirectMemory(*this, vm));
431 }
432
433void GameScript::initSettings() {
434 auto lang = Gothic::inst().settingsGetI("GAME", "language");
435 if(vmLang!=lang) {
436 vmLang = lang;
437 //vm = createVm(Gothic::inst());
438 initDialogs();
439 }
440 }
441
443 loadDialogOU();
444
445 dialogsInfo.clear();
446 vm.enumerate_instances_by_class_name("C_INFO", [this](zenkit::DaedalusSymbol& sym){
447 dialogsInfo.push_back(vm.init_instance<zenkit::IInfo>(&sym));
448 });
449 }
450
451void GameScript::loadDialogOU() {
452 // Based on https://github.com/auronen/Gothic-2-localization mod,
453 // at least in G2 .BIN should have a priority
454 const std::string prefix = std::string(Gothic::inst().defaultOutputUnits());
455 const std::array<std::string,2> names = {prefix + ".BIN", prefix + ".DAT"};
456
457 const auto version = Gothic::inst().version().game==1 ? zenkit::GameVersion::GOTHIC_1
458 : zenkit::GameVersion::GOTHIC_2;
459
460 for(auto& OU:names) {
461 if(Resources::hasFile(OU)) {
462 std::unique_ptr<zenkit::Read> read;
463 auto zen = Resources::openReader(OU, read);
464 dialogs = std::move(*zen->read_object<zenkit::CutsceneLibrary>(version));
465 return;
466 }
467
468 const size_t segment = OU.find_last_of("\\/");
469 if(segment!=std::string::npos && Resources::hasFile(OU.substr(segment+1))) {
470 std::unique_ptr<zenkit::Read> read;
471 auto zen = Resources::openReader(OU.substr(segment+1), read);
472 dialogs = std::move(*zen->read_object<zenkit::CutsceneLibrary>(version));
473 return;
474 }
475
476 char16_t str16[256] = {};
477 for(size_t i=0; OU[i] && i<255; ++i)
478 str16[i] = char16_t(OU[i]);
479
480 auto gcutscene = CommandLine::inst().cutscenePath();
481 auto full = FileUtil::caseInsensitiveSegment(gcutscene,str16,Dir::FT_File);
482 if(!FileUtil::exists(std::u16string(full)) && vmLang>=0) {
483 gcutscene = CommandLine::inst().cutscenePath(ScriptLang(vmLang));
484 full = FileUtil::caseInsensitiveSegment(gcutscene,str16,Dir::FT_File);
485 }
486
487 try {
488 auto buf = zenkit::Read::from(full);
489 if(buf==nullptr)
490 continue;
491 auto zen = zenkit::ReadArchive::from(buf.get());
492 if(zen==nullptr)
493 continue;
494 // auto zen = Resources::openReader(full);
495 dialogs = std::move(*zen->read_object<zenkit::CutsceneLibrary>(version));
496 return;
497 }
498 catch(...){
499 // loop to next possible path
500 }
501 }
502 Log::e("none of Zen-files for OU could be loaded");
503 }
504
505void GameScript::initializeInstanceNpc(const std::shared_ptr<zenkit::INpc>& npc, size_t instance) {
506 auto sym = vm.find_symbol_by_index(uint32_t(instance));
507
508 if(sym == nullptr) {
509 Tempest::Log::e("Cannot initialize NPC ", instance, ": Symbol not found.");
510 return;
511 }
512
513 vm.init_instance(npc, sym);
514
515 if(npc->daily_routine!=0) {
516 ScopeVar self(*vm.global_self(), npc);
517 auto* daily_routine = vm.find_symbol_by_index(uint32_t(npc->daily_routine));
518
519 if(daily_routine != nullptr) {
520 vm.call_function(daily_routine);
521 }
522 }
523 }
524
525void GameScript::initializeInstanceItem(const std::shared_ptr<zenkit::IItem>& item, size_t instance) {
526 auto sym = vm.find_symbol_by_index(uint32_t(instance));
527
528 if(sym == nullptr) {
529 Tempest::Log::e("Cannot initialize item ", instance, ": Symbol not found.");
530 return;
531 }
532
533 vm.init_instance(item, sym);
534 }
535
537 quests.save(fout);
538 fout.write(uint32_t(dlgKnownInfos.size()));
539 for(auto& i:dlgKnownInfos)
540 fout.write(uint32_t(i.first),uint32_t(i.second));
541
542 fout.write(gilAttitudes);
543 }
544
546 quests.load(fin);
547 uint32_t sz=0;
548 fin.read(sz);
549 for(size_t i=0;i<sz;++i){
550 uint32_t f=0,s=0;
551 fin.read(f,s);
552 dlgKnownInfos.insert(std::make_pair(f,s));
553 }
554
555 fin.read(gilAttitudes);
556 }
557
559 auto& dat = vm.symbols();
560 fout.write(uint32_t(dat.size()));
561 for(unsigned i = 0; i < dat.size(); ++i){
562 auto* sym = vm.find_symbol_by_index(i); // never returns nullptr
563 saveSym(fout,*sym);
564 }
565 }
566
568 std::string name;
569 uint32_t sz=0;
570 fin.read(sz);
571 for(size_t i=0;i<sz;++i){
572 auto t = uint32_t(zenkit::DaedalusDataType::VOID);
573 fin.read(t);
574 switch(zenkit::DaedalusDataType(t)) {
575 case zenkit::DaedalusDataType::INT:{
576 fin.read(name);
577 auto* s = findSymbol(name);
578
579 uint32_t size;
580 fin.read(size);
581
582 int v = 0;
583 for(unsigned j = 0; j < size; ++j) {
584 fin.read(v);
585 if (s != nullptr && !s->is_member() && !s->is_const()) {
586 s->set_int(v, uint16_t(j));
587 }
588 }
589
590 break;
591 }
592 case zenkit::DaedalusDataType::FLOAT:{
593 fin.read(name);
594 auto* s = findSymbol(name);
595
596 uint32_t size;
597 fin.read(size);
598
599 float v = 0;
600 for (unsigned j = 0; j < size; ++j) {
601 fin.read(v);
602 if (s != nullptr && !s->is_member() && !s->is_const()) {
603 s->set_float(v, uint16_t(j));
604 }
605 }
606
607 break;
608 }
609 case zenkit::DaedalusDataType::STRING:{
610 fin.read(name);
611 auto* s = findSymbol(name);
612
613 uint32_t size;
614 fin.read(size);
615
616 std::string v;
617 for (unsigned j = 0; j < size; ++j) {
618 fin.read(v);
619 if (s != nullptr && !s->is_member() && !s->is_const()) {
620 s->set_string(v, uint16_t(j));
621 }
622 }
623
624 break;
625 }
626 case zenkit::DaedalusDataType::INSTANCE:{
627 uint8_t dataClass=0;
628 fin.read(dataClass);
629 if(dataClass>0){
630 uint32_t id=0;
631 fin.read(name,id);
632 auto* s = findSymbol(name);
633 if (s == nullptr)
634 break;
635 if(dataClass==1) {
636 auto npc = world().npcById(id);
637 s->set_instance(npc ? npc->handlePtr() : nullptr);
638 }
639 else if(dataClass==2) {
640 auto itm = world().itmById(id);
641 s->set_instance(itm != nullptr ? itm->handlePtr() : nullptr);
642 }
643 else if(dataClass==3) {
644 uint32_t itmClass=0;
645 fin.read(itmClass);
646 if(auto npc = world().npcById(id)) {
647 auto itm = npc->getItem(itmClass);
648 s->set_instance(itm ? itm->handlePtr() : nullptr);
649 }
650 }
651 }
652 break;
653 }
654 default:
655 break;
656 }
657 }
658 }
659
661 fout.write(uint32_t(PERC_Count));
662 for(size_t i=0; i<PERC_Count; ++i) {
663 fout.write(perceptionRanges.range[i]);
664 }
665 }
666
668 uint32_t count = 0;
669 fin.read(count);
670 if(count!=PERC_Count) {
671 if(hasSymbolName("initPerceptions"))
672 vm.call_function("initPerceptions");
673 return;
674 }
675 for(size_t i=0; i<PERC_Count; ++i)
676 fin.read(perceptionRanges.range[i]);
677 }
678
680 for(uint32_t i=0;i<vm.symbols().size();++i){
681 auto* s = vm.find_symbol_by_index(i); // never returns nullptr
682 if(s->is_instance_of<zenkit::INpc>() || s->is_instance_of<zenkit::IItem>()){
683 s->set_instance(nullptr);
684 }
685 }
686 }
687
689 return quests;
690 }
691
692void GameScript::saveSym(Serialize &fout, zenkit::DaedalusSymbol& i) {
693 auto& w = world();
694 switch(i.type()) {
695 case zenkit::DaedalusDataType::INT:
696 if(i.count()>0 && !i.is_member() && !i.is_const()){
697 fout.write(i.type(), i.name(), i.count());
698
699 for (unsigned j = 0; j < i.count(); ++j)
700 fout.write(i.get_int(uint16_t(j)));
701 return;
702 }
703 break;
704 case zenkit::DaedalusDataType::FLOAT:
705 if(i.count()>0 && !i.is_member() && !i.is_const()){
706 fout.write(i.type(), i.name(), i.count());
707
708 for (unsigned j = 0; j < i.count(); ++j)
709 fout.write(i.get_float(uint16_t(j)));
710 return;
711 }
712 break;
713 case zenkit::DaedalusDataType::STRING:
714 if(i.count()>0 && !i.is_member() && !i.is_const()){
715 fout.write(i.type(), i.name(), i.count());
716
717 for (unsigned j = 0; j < i.count(); ++j)
718 fout.write(i.get_string(uint16_t(j)));
719 return;
720 }
721 break;
722 case zenkit::DaedalusDataType::INSTANCE:
723 fout.write(i.type());
724
725 if(i.is_instance_of<zenkit::INpc>()){
726 auto hnpc = reinterpret_cast<const zenkit::INpc*>(i.get_instance().get());
727 auto npc = reinterpret_cast<const Npc*>(hnpc==nullptr ? nullptr : hnpc->user_ptr);
728 fout.write(uint8_t(1),i.name(),world().npcId(npc));
729 }
730 else if(i.is_instance_of<zenkit::IItem>()){
731 auto item = reinterpret_cast<const zenkit::IItem*>(i.get_instance().get());
732 uint32_t id = w.itmId(item);
733 if(id!=uint32_t(-1) || item==nullptr) {
734 fout.write(uint8_t(2),i.name(),id);
735 } else {
736 uint32_t idNpc = uint32_t(-1);
737 for(uint32_t r=0; r<w.npcCount(); ++r) {
738 auto& n = *w.npcById(r);
739 if(n.itemCount(item->symbol_index())>0) {
740 idNpc = r;
741 fout.write(uint8_t(3),i.name(),idNpc,uint32_t(item->symbol_index()));
742 break;
743 }
744 }
745 if(idNpc==uint32_t(-1))
746 fout.write(uint8_t(2),i.name(),uint32_t(-1));
747 }
748 }
749 else if(i.is_instance_of<zenkit::IFocus>() ||
750 i.is_instance_of<zenkit::IGuildValues>() ||
751 i.is_instance_of<zenkit::IInfo>()) {
752 fout.write(uint8_t(0));
753 }
754 else {
755 fout.write(uint8_t(0));
756 }
757 return;
758 default:
759 break;
760 }
761 fout.write(uint32_t(zenkit::DaedalusDataType::VOID));
762 }
763
764void GameScript::fixNpcPosition(Npc& npc, float angle0, float distBias) {
765 auto& dyn = *world().physic();
766 auto pos0 = npc.position();
767
768 for(int r = 0; r<=800; r+=20) {
769 for(float ang = 0; ang<360; ang+=30.f) {
770 float a = float((ang+angle0)*M_PI/180.0);
771 float d = float(r)+distBias;
772 auto p = pos0+Vec3(std::cos(a)*d, 0, std::sin(a)*d);
773
774 auto ray = dyn.ray(p+Vec3(0,100,0), p+Vec3(0,-1000,0));
775 if(!ray.hasCol)
776 continue;
777 p.y = ray.v.y;
778 npc.setPosition(p);
779 if(!npc.hasCollision())
780 return;
781 }
782 }
783
784 npc.setPosition(pos0);
785 }
786
787void GameScript::eventPlayAni(Npc& npc, std::string_view ani) {
788 if(dma!=nullptr)
789 dma->eventPlayAni(ani);
790 }
791
792const World &GameScript::world() const {
793 return *owner.world();
794 }
795
797 return *owner.world();
798 }
799
800zenkit::IFocus GameScript::findFocus(std::string_view name) {
801 auto id = vm.find_symbol_by_name(name);
802 if(id==nullptr)
803 return {};
804 try {
805 return *vm.init_instance<zenkit::IFocus>(id);
806 }
807 catch(const zenkit::DaedalusScriptError&) {
808 return {};
809 }
810 }
811
812void GameScript::storeItem(Item *itm) {
813 auto* s = vm.global_item();
814 if(itm!=nullptr) {
815 s->set_instance(itm->handlePtr());
816 } else {
817 s->set_instance(nullptr);
818 }
819 }
820
821zenkit::DaedalusSymbol* GameScript::findSymbol(std::string_view s) {
822 return vm.find_symbol_by_name(s);
823 }
824
825zenkit::DaedalusSymbol* GameScript::findSymbol(const size_t s) {
826 return vm.find_symbol_by_index(uint32_t(s));
827 }
828
829size_t GameScript::findSymbolIndex(std::string_view name) {
830 auto sym = vm.find_symbol_by_name(name);
831 return sym == nullptr ? size_t(-1) : sym->index();
832 }
833
835 return vm.symbols().size();
836 }
837
839 auto it = aiStates.find(id.ptr);
840 if(it!=aiStates.end())
841 return it->second;
842 auto ins = aiStates.emplace(id.ptr,AiState(*this,id.ptr));
843 return ins.first->second;
844 }
845
846const zenkit::ISpell& GameScript::spellDesc(int32_t splId) {
847 auto& tag = spellFxInstanceNames->get_string(uint16_t(splId));
848 return spells->find(tag);
849 }
850
851const VisualFx* GameScript::spellVfx(int32_t splId) {
852 auto& tag = spellFxInstanceNames->get_string(uint16_t(splId));
853 string_frm name("spellFX_",tag);
854 return Gothic::inst().loadVisualFx(name);
855 }
856
857std::vector<GameScript::DlgChoice> GameScript::dialogChoices(std::shared_ptr<zenkit::INpc> player,
858 std::shared_ptr<zenkit::INpc> hnpc,
859 const std::vector<uint32_t>& except,
860 bool includeImp) {
861 ScopeVar self (*vm.global_self(), hnpc);
862 ScopeVar other(*vm.global_other(), player);
863 std::vector<zenkit::IInfo*> hDialog;
864 for(auto& info : dialogsInfo) {
865 if(info->npc==static_cast<int>(hnpc->symbol_index())) {
866 hDialog.push_back(info.get());
867 }
868 }
869
870 std::vector<DlgChoice> choice;
871 for(int important=includeImp ? 1 : 0;important>=0;--important){
872 for(auto& i:hDialog) {
873 const zenkit::IInfo& info = *i;
874 if(info.important!=important)
875 continue;
876 bool npcKnowsInfo = doesNpcKnowInfo(*player,vm.find_symbol_by_instance(info)->index());
877 if(npcKnowsInfo && !info.permanent)
878 continue;
879
880 if(info.important && info.permanent){
881 bool skip=false;
882 for(auto i:except)
883 if(static_cast<int>(i)==info.information){
884 skip=true;
885 break;
886 }
887 if(skip)
888 continue;
889 }
890
891 bool valid=true;
892 if(info.condition) {
893 auto* conditionSymbol = vm.find_symbol_by_index(uint32_t(info.condition));
894 if (conditionSymbol != nullptr) {
895 valid = vm.call_function<int>(conditionSymbol) != 0;
896 }
897 }
898 if(!valid)
899 continue;
900
901 DlgChoice ch;
902 ch.title = info.description;
903 ch.scriptFn = uint32_t(info.information);
904 ch.handle = i;
905 ch.isTrade = info.trade!=0;
906 ch.sort = info.nr;
907 choice.emplace_back(std::move(ch));
908 }
909 if(!choice.empty()){
910 sort(choice);
911 return choice;
912 }
913 }
914 sort(choice);
915 return choice;
916 }
917
918std::vector<GameScript::DlgChoice> GameScript::updateDialog(const GameScript::DlgChoice &dlg, Npc& player,Npc& npc) {
919 if(dlg.handle==nullptr)
920 return {};
921 const zenkit::IInfo& info = *dlg.handle;
922 std::vector<GameScript::DlgChoice> ret;
923
924 ScopeVar self (*vm.global_self(), npc.handlePtr());
925 ScopeVar other(*vm.global_other(), player.handlePtr());
926
927 for(size_t i=0;i<info.choices.size();++i){
928 auto& sub = info.choices[i];
930 ch.title = sub.text;
931 ch.scriptFn = uint32_t(sub.function);
932 ch.handle = dlg.handle;
933 ch.isTrade = false;
934 ch.sort = int(i);
935 ret.push_back(ch);
936 }
937
938 sort(ret);
939 return ret;
940 }
941
942void GameScript::exec(const GameScript::DlgChoice &dlg, Npc& player, Npc& npc) {
943 ScopeVar self (*vm.global_self(), npc.handlePtr());
944 ScopeVar other(*vm.global_other(), player.handlePtr());
945
946 zenkit::IInfo& info = *dlg.handle;
947
948 if(&player!=&npc)
949 player.stopAnim("");
950 auto& pl = player.handle();
951
952 if(info.information==int(dlg.scriptFn)) {
953 setNpcInfoKnown(pl,info);
954 } else {
955 for(size_t i=0;i<info.choices.size();){
956 if(info.choices[i].function==int(dlg.scriptFn))
957 info.choices.erase(info.choices.begin()+int(i)); else
958 ++i;
959 }
960 }
961 vm.call_function(vm.find_symbol_by_index(dlg.scriptFn));
962 }
963
964void GameScript::printCannotUseError(Npc& npc, int32_t atr, int32_t nValue) {
965 auto id = vm.find_symbol_by_name("G_CanNotUse");
966 if(id==nullptr)
967 return;
968
969 ScopeVar self(*vm.global_self(), npc.handlePtr());
970 vm.call_function<void>(id, npc.isPlayer(), atr, nValue);
971 }
972
973void GameScript::printCannotCastError(Npc &npc, int32_t plM, int32_t itM) {
974 auto id = vm.find_symbol_by_name("G_CanNotCast");
975 if(id==nullptr)
976 return;
977
978 ScopeVar self(*vm.global_self(), npc.handlePtr());
979 vm.call_function<void>(id, npc.isPlayer(), itM, plM);
980 }
981
983 auto id = vm.find_symbol_by_name("player_trade_not_enough_gold");
984 if(id==nullptr)
985 return;
986 ScopeVar self(*vm.global_self(), npc.handlePtr());
987 vm.call_function<void>(id);
988 }
989
991 auto id = vm.find_symbol_by_name("player_mob_missing_item");
992 if(id==nullptr) {
993 if(owner.version().game==1)
994 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
995 return;
996 }
997 ScopeVar self(*vm.global_self(), npc.handlePtr());
998 vm.call_function<void>(id);
999 }
1000
1002 auto id = vm.find_symbol_by_name("player_mob_missing_key");
1003 if(id==nullptr) {
1004 if(owner.version().game==1)
1005 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1006 return;
1007 }
1008 ScopeVar self(*vm.global_self(), npc.handlePtr());
1009 vm.call_function<void>(id);
1010 }
1011
1013 auto id = vm.find_symbol_by_name("player_mob_another_is_using");
1014 if(id==nullptr) {
1015 if(owner.version().game==1)
1016 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1017 return;
1018 }
1019 ScopeVar self(*vm.global_self(), npc.handlePtr());
1020 vm.call_function<void>(id);
1021 }
1022
1024 auto id = vm.find_symbol_by_name("player_mob_missing_key_or_lockpick");
1025 if(id==nullptr) {
1026 if(owner.version().game==1)
1027 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1028 return;
1029 }
1030 ScopeVar self(*vm.global_self(), npc.handlePtr());
1031 vm.call_function<void>(id);
1032 }
1033
1035 auto id = vm.find_symbol_by_name("player_mob_missing_lockpick");
1036 if(id==nullptr) {
1037 if(owner.version().game==1)
1038 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1039 return;
1040 }
1041 ScopeVar self(*vm.global_self(), npc.handlePtr());
1042 vm.call_function<void>(id);
1043 }
1044
1046 auto id = vm.find_symbol_by_name("player_mob_too_far_away");
1047 if(id==nullptr) {
1048 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1049 return;
1050 }
1051 ScopeVar self(*vm.global_self(), npc.handlePtr());
1052 vm.call_function<void>(id);
1053 }
1054
1055void GameScript::invokeState(const std::shared_ptr<zenkit::INpc>& hnpc, const std::shared_ptr<zenkit::INpc>& oth, const char *name) {
1056 auto id = vm.find_symbol_by_name(name);
1057 if(id==nullptr)
1058 return;
1059
1060 ScopeVar self (*vm.global_self(), hnpc);
1061 ScopeVar other(*vm.global_other(), oth);
1062 vm.call_function<void>(id);
1063 }
1064
1065int GameScript::invokeState(Npc* npc, Npc* oth, Npc* vic, ScriptFn fn) {
1066 if(!fn.isValid())
1067 return 0;
1068 if(oth==nullptr){
1069 // oth=npc; //FIXME: PC_Levelinspektor?
1070 }
1071 if(vic==nullptr){
1072 // vic=owner.player();
1073 }
1074
1075 if(fn==ZS_Talk){
1076 if(oth==nullptr || !oth->isPlayer()) {
1077 Log::e("unxepected perc acton");
1078 return 0;
1079 }
1080 }
1081
1082 ScopeCtx ctx (*this, npc!=nullptr ? npc->processPolicy() : NpcProcessPolicy::AiNormal);
1083 ScopeVar self (*vm.global_self(), npc != nullptr ? npc->handlePtr() : nullptr);
1084 ScopeVar other (*vm.global_other(), oth != nullptr ? oth->handlePtr() : nullptr);
1085 ScopeVar victim(*vm.global_victim(), vic != nullptr ? vic->handlePtr() : nullptr);
1086
1087 auto* sym = vm.find_symbol_by_index(uint32_t(fn.ptr));
1088 int ret = 0;
1089 if(sym!=nullptr && sym->rtype() == zenkit::DaedalusDataType::INT) {
1090 ret = vm.call_function<int>(sym);
1091 }
1092 else if(sym!=nullptr) {
1093 vm.call_function<void>(sym);
1094 ret = 0;
1095 }
1096
1097 if(vm.global_other()->is_instance_of<zenkit::INpc>()){
1098 auto oth2 = reinterpret_cast<zenkit::INpc*>(vm.global_other()->get_instance().get());
1099 if(oth!=nullptr && oth2!=&oth->handle()) {
1100 Npc* other = findNpc(oth2);
1101 npc->setOther(other);
1102 }
1103 }
1104 return ret;
1105 }
1106
1108 if(fn==size_t(-1) || fn == 0)
1109 return;
1110 auto functionSymbol = vm.find_symbol_by_index(uint32_t(fn.ptr));
1111
1112 if (functionSymbol == nullptr)
1113 return;
1114
1115 ScopeVar self(*vm.global_self(), npc->handlePtr());
1116 vm.call_function<void>(functionSymbol);
1117 }
1118
1119int GameScript::invokeMana(Npc &npc, Npc* target, int mana) {
1120 auto fn = vm.find_symbol_by_name("Spell_ProcessMana");
1121 if(fn==nullptr)
1123
1124 ScopeVar self (*vm.global_self(), npc.handlePtr());
1125 ScopeVar other(*vm.global_other(), target != nullptr ? target->handlePtr() : nullptr);
1126
1127 return vm.call_function<int>(fn,mana);
1128 }
1129
1130int GameScript::invokeManaRelease(Npc &npc, Npc* target, int mana) {
1131 auto fn = vm.find_symbol_by_name("Spell_ProcessMana_Release");
1132 if(fn==nullptr)
1134
1135 ScopeVar self (*vm.global_self(), npc.handlePtr());
1136 ScopeVar other(*vm.global_other(), target != nullptr ? target->handlePtr() : nullptr);
1137
1138 return vm.call_function<int>(fn,mana);
1139 }
1140
1141void GameScript::invokeSpell(Npc &npc, Npc* target, Item &it) {
1142 auto& tag = spellFxInstanceNames->get_string(uint16_t(it.spellId()));
1143 string_frm name("Spell_Cast_",tag);
1144 auto fn = vm.find_symbol_by_name(name);
1145 if(fn==nullptr)
1146 return;
1147
1148 // FIXME: actually set the spell level!
1149 int32_t splLevel = 0;
1150 ScopeVar self (*vm.global_self(), npc.handlePtr());
1151 ScopeVar other(*vm.global_other(), target != nullptr ? target->handlePtr() : nullptr);
1152 try {
1153 if(fn->count()==1) {
1154 // this is a leveled spell
1155 vm.call_function<void>(fn, splLevel);
1156 } else {
1157 vm.call_function<void>(fn);
1158 }
1159 }
1160 catch(...) {
1161 Log::d("unable to call spell-script: \"",name,"\'");
1162 }
1163 }
1164
1165int GameScript::invokeCond(Npc& npc, std::string_view func) {
1166 auto fn = vm.find_symbol_by_name(func);
1167 if(fn==nullptr) {
1168 Gothic::inst().onPrint("MOBSI::conditionFunc is not invalid");
1169 return 1;
1170 }
1171 ScopeVar self(*vm.global_self(), npc.handlePtr());
1172 return vm.call_function<int>(fn);
1173 }
1174
1175void GameScript::invokePickLock(Npc& npc, int bSuccess, int bBrokenOpen) {
1176 auto fn = vm.find_symbol_by_name("G_PickLock");
1177 if(fn==nullptr)
1178 return;
1179 ScopeVar self(*vm.global_self(), npc.handlePtr());
1180 vm.call_function<void>(fn, bSuccess, bBrokenOpen);
1181 }
1182
1184 if(B_RefreshAtInsert==nullptr || owner.version().game!=2)
1185 return;
1186 ScopeVar self(*vm.global_self(), npc.handlePtr());
1187 vm.call_function<void>(B_RefreshAtInsert);
1188 }
1189
1190CollideMask GameScript::canNpcCollideWithSpell(Npc& npc, Npc* shooter, int32_t spellId) {
1191 if(owner.version().game==1) {
1192 auto& spl = spellDesc(spellId);
1193 if(npc.isTargetableBySpell(TargetType(spl.target_collect_type)))
1194 return COLL_DOEVERYTHING; else
1195 return COLL_DONOTHING;
1196 }
1197
1198 auto fn = vm.find_symbol_by_name("C_CanNpcCollideWithSpell");
1199 if(fn==nullptr)
1200 return COLL_DOEVERYTHING;
1201
1202 ScopeVar self (*vm.global_self(), npc.handlePtr());
1203 ScopeVar other(*vm.global_other(), shooter->handlePtr());
1204 return CollideMask(vm.call_function<int>(fn, spellId));
1205 }
1206
1207// Gothic 1 only differentiates between the two worldmap types with and
1208// without the orc addition and does this inside the code, not the script
1210 size_t map = findSymbolIndex("itwrworldmap_orc");
1211 if(map==size_t(-1) || pl.itemCount(map)<1)
1212 map = findSymbolIndex("itwrworldmap");
1213
1214 if(map==size_t(-1) || pl.itemCount(map)<1)
1215 return -1;
1216
1217 pl.useItem(map);
1218
1219 return int(map);
1220 }
1221
1223 auto fn = vm.find_symbol_by_name("player_hotkey_screen_map");
1224 if(fn==nullptr) {
1225 if(owner.version().game==1)
1226 return playerHotKeyScreenMap_G1(pl);
1227 return -1;
1228 }
1229
1230 ScopeVar self(*vm.global_self(), pl.handlePtr());
1231 int map = vm.call_function<int>(fn);
1232 if(map>=0)
1233 pl.useItem(size_t(map));
1234 return map;
1235 }
1236
1238 auto opt = Gothic::inst().settingsGetI("GAME", "usePotionKeys");
1239 if(opt==0)
1240 return;
1241
1242 auto fn = vm.find_symbol_by_name("player_hotkey_lame_potion");
1243 if(fn==nullptr)
1244 return;
1245
1246 ScopeVar self(*vm.global_self(), pl.handlePtr());
1247 vm.call_function<void>(fn);
1248 }
1249
1251 auto opt = Gothic::inst().settingsGetI("GAME", "usePotionKeys");
1252 if(opt==0)
1253 return;
1254
1255 auto fn = vm.find_symbol_by_name("player_hotkey_lame_heal");
1256 if(fn==nullptr)
1257 return;
1258
1259 ScopeVar self(*vm.global_self(), pl.handlePtr());
1260 vm.call_function<void>(fn);
1261 }
1262
1263std::string_view GameScript::spellCastAnim(Npc&, Item &it) {
1264 if(spellFxAniLetters==nullptr)
1265 return "FIB";
1266 return spellFxAniLetters->get_string(uint16_t(it.spellId()));
1267 }
1268
1269bool GameScript::aiOutput(Npc &npc, std::string_view outputname, bool overlay) {
1270 string_frm name(outputname,".WAV");
1271 uint64_t dt = 0;
1272
1274 npc.setAiOutputBarrier(messageTime(outputname),overlay);
1275
1276 return true;
1277 }
1278
1279bool GameScript::aiOutputSvm(Npc &npc, std::string_view outputname, bool overlay) {
1280 if(overlay) {
1281 if(tickCount()<svmBarrier)
1282 return true;
1283 svmBarrier = tickCount()+messageTime(outputname);
1284 }
1285
1286 if(!outputname.empty())
1287 return aiOutput(npc,outputname,overlay);
1288 return true;
1289 }
1290
1291bool GameScript::isDead(const Npc &pl) {
1292 return pl.isInState(ZS_Dead);
1293 }
1294
1296 return pl.isInState(ZS_Unconscious);
1297 }
1298
1299bool GameScript::isTalk(const Npc &pl) {
1300 return pl.isInState(ZS_Talk);
1301 }
1302
1303bool GameScript::isAttack(const Npc& pl) const {
1304 return pl.isInState(ZS_Attack) || pl.isInState(ZS_MM_Attack);
1305 }
1306
1307std::string_view GameScript::messageFromSvm(std::string_view id, int voice) const {
1308 return svm->find(id,voice);
1309 }
1310
1311std::string_view GameScript::messageByName(std::string_view id) const {
1312 auto blk = dialogs.block_by_name(id);
1313 if(blk == nullptr)
1314 return "";
1315 auto msg = blk->get_message();
1316 if(msg == nullptr)
1317 return "";
1318 return msg->text;
1319 }
1320
1321uint32_t GameScript::messageTime(std::string_view id) const {
1322 static std::string tmp;
1323 tmp.assign(id);
1324
1325 uint32_t& time = msgTimings[tmp];
1326 if(time>0)
1327 return time;
1328
1329 string_frm name(id,".WAV");
1330 auto s = Resources::loadSoundBuffer(name);
1331 if(s.timeLength()>0) {
1332 time = uint32_t(s.timeLength());
1333 } else {
1334 auto txt = messageByName(id);
1335 time = uint32_t(float(txt.length())*viewTimePerChar);
1336 time = std::min(time, 16000u);
1337 }
1338 return time;
1339 }
1340
1342 auto id = vm.find_symbol_by_name("player_plunder_is_empty");
1343 if(id==nullptr) {
1344 if(owner.version().game==1)
1345 owner.player()->playAnimByName("T_DONTKNOW", BS_NONE);
1346 return;
1347 }
1348 ScopeVar self(*vm.global_self(), owner.player()->handlePtr());
1349 vm.call_function<void>(id);
1350 }
1351
1352void GameScript::useInteractive(const std::shared_ptr<zenkit::INpc>& hnpc, std::string_view func) {
1353 auto fn = vm.find_symbol_by_name(func);
1354 if(fn == nullptr)
1355 return;
1356
1357 ScopeVar self(*vm.global_self(),hnpc);
1358 try {
1359 vm.call_function<void>(fn);
1360 }
1361 catch (...) {
1362 Log::i("unable to use interactive [",func,"]");
1363 }
1364 }
1365
1366Attitude GameScript::guildAttitude(const Npc &p0, const Npc &p1) const {
1367 auto selfG = std::min<size_t>(gilCount-1,p0.guild());
1368 auto npcG = std::min<size_t>(gilCount-1,p1.guild());
1369 auto ret = gilAttitudes[selfG*gilCount+npcG];
1370 return Attitude(ret);
1371 }
1372
1373Attitude GameScript::personAttitude(const Npc &p0, const Npc &p1) const {
1374 if(!p0.isPlayer() && !p1.isPlayer())
1375 return guildAttitude(p0,p1);
1376
1377 Attitude att=ATT_NULL;
1378 const Npc& npc = p0.isPlayer() ? p1 : p0;
1379 att = npc.attitude();
1380 if(att!=ATT_NULL)
1381 return att;
1382 att = guildAttitude(p0,p1);
1383 return att;
1384 }
1385
1386bool GameScript::isFriendlyFire(const Npc& src, const Npc& dst) const {
1387 const int AIV_PARTYMEMBER = (owner.version().game==2) ? 15 : 36;
1388 if(src.isPlayer())
1389 return false;
1390 if(personAttitude(src, dst)==ATT_FRIENDLY)
1391 return true;
1392 if(src.handlePtr()->aivar[AIV_PARTYMEMBER]!=0 && dst.isPlayer())
1393 return true;
1394 return false;
1395 }
1396
1398 if(searchScheme(sc,"MOB_SIT"))
1399 return BS_SIT;
1400 if(searchScheme(sc,"MOB_LIE"))
1401 return BS_LIE;
1402 if(searchScheme(sc,"MOB_CLIMB"))
1403 return BS_CLIMB;
1404 if(searchScheme(sc,"MOB_NOTINTERRUPTABLE"))
1405 return BS_MOBINTERACT;
1407 }
1408
1410 onWldInstanceRemoved(&itm.handle());
1411 }
1412
1413void GameScript::onWldInstanceRemoved(const zenkit::DaedalusInstance* obj) {
1414 vm.find_symbol_by_instance(*obj)->set_instance(nullptr);
1415 }
1416
1417void GameScript::makeCurrent(Item* w) {
1418 if(w==nullptr)
1419 return;
1420 auto* s = vm.find_symbol_by_index(uint32_t(w->clsId()));
1421 if(s != nullptr)
1422 s->set_instance(w->handlePtr());
1423 }
1424
1425bool GameScript::searchScheme(std::string_view sc, std::string_view listName) {
1426 std::string_view list = findSymbol(listName)->get_string();
1427 for(size_t i=0; i<=list.size(); ++i) {
1428 if(i==list.size() || list[i]==',') {
1429 if(sc==list.substr(0,i))
1430 return true;
1431 if(i==list.size())
1432 break;
1433 list = list.substr(i+1);
1434 i = 0;
1435 }
1436 }
1437 return false;
1438 }
1439
1440zenkit::DaedalusVm GameScript::createVm(Gothic& gothic) {
1441 auto lang = gothic.settingsGetI("GAME", "language");
1442 auto script = gothic.loadScript(gothic.defaultGameDatFile(), ScriptLang(lang));
1443 auto exef = zenkit::DaedalusVmExecutionFlag::ALLOW_NULL_INSTANCE_ACCESS;
1444 if(DirectMemory::isRequired(script)) {
1445 exef |= zenkit::DaedalusVmExecutionFlag::IGNORE_CONST_SPECIFIER;
1446 }
1447 return zenkit::DaedalusVm(std::move(script), exef);
1448 }
1449
1450bool GameScript::hasSymbolName(std::string_view name) {
1451 return vm.find_symbol_by_name(name)!=nullptr;
1452 }
1453
1454uint64_t GameScript::tickCount() const {
1455 return owner.tickCount();
1456 }
1457
1458void GameScript::tick(uint64_t dt) {
1459 if(dma!=nullptr)
1460 dma->tick(dt);
1461 }
1462
1463uint32_t GameScript::rand(uint32_t max) {
1464 return uint32_t(randGen())%max;
1465 }
1466
1467Npc* GameScript::findNpc(zenkit::DaedalusSymbol* s) {
1468 if(s->is_instance_of<zenkit::INpc>()) {
1469 auto cNpc = reinterpret_cast<zenkit::INpc*>(s->get_instance().get());
1470 return findNpc(cNpc);
1471 }
1472 return nullptr;
1473 }
1474
1475Npc* GameScript::findNpc(zenkit::INpc *handle) {
1476 if(handle==nullptr)
1477 return nullptr;
1478 assert(handle->user_ptr); // engine bug, if null
1479 return reinterpret_cast<Npc*>(handle->user_ptr);
1480 }
1481
1482Npc* GameScript::findNpc(const std::shared_ptr<zenkit::INpc>& handle) {
1483 if(handle==nullptr)
1484 return nullptr;
1485 assert(handle->user_ptr); // engine bug, if null
1486 return reinterpret_cast<Npc*>(handle->user_ptr);
1487 }
1488
1489Npc* GameScript::findNpcById(const std::shared_ptr<zenkit::DaedalusInstance>& handle) {
1490 if(handle==nullptr)
1491 return nullptr;
1492 if(auto npc = dynamic_cast<const zenkit::INpc*>(handle.get())) {
1493 assert(npc->user_ptr); // engine bug, if null
1494 return reinterpret_cast<Npc*>(npc->user_ptr);
1495 }
1496 return findNpcById(handle->symbol_index());
1497 }
1498
1499Item *GameScript::findItem(zenkit::IItem* handle) {
1500 if(handle==nullptr)
1501 return nullptr;
1502 auto& itData = *handle;
1503 assert(itData.user_ptr); // engine bug, if null
1504 return reinterpret_cast<Item*>(itData.user_ptr);
1505 }
1506
1507Item *GameScript::findItemById(size_t id) {
1508 auto* handle = vm.find_symbol_by_index(uint32_t(id));
1509 if(handle==nullptr||!handle->is_instance_of<zenkit::IItem>())
1510 return nullptr;
1511 auto hitm = reinterpret_cast<zenkit::IItem*>(handle->get_instance().get());
1512 return findItem(hitm);
1513 }
1514
1515Item* GameScript::findItemById(const std::shared_ptr<zenkit::DaedalusInstance>& handle) {
1516 if(handle==nullptr)
1517 return nullptr;
1518 if(auto itm = dynamic_cast<const zenkit::IItem*>(handle.get())) {
1519 assert(itm->user_ptr); // engine bug, if null
1520 return reinterpret_cast<Item*>(itm->user_ptr);
1521 }
1522 return findItemById(handle->symbol_index());
1523 }
1524
1525Npc* GameScript::findNpcById(size_t id) {
1526 auto* handle = vm.find_symbol_by_index(uint32_t(id));
1527 if(handle==nullptr || !handle->is_instance_of<zenkit::INpc>())
1528 return nullptr;
1529
1530 auto hnpc = reinterpret_cast<zenkit::INpc*>(handle->get_instance().get());
1531 if(hnpc==nullptr) {
1532 auto obj = world().findNpcByInstance(id);
1533 handle->set_instance(obj ? obj->handlePtr() : nullptr);
1534 hnpc = reinterpret_cast<zenkit::INpc*>(handle->get_instance().get());
1535 }
1536 return findNpc(hnpc);
1537 }
1538
1539zenkit::IInfo* GameScript::findInfo(size_t id) {
1540 auto* sym = vm.find_symbol_by_index(uint32_t(id));
1541 if(sym==nullptr||!sym->is_instance_of<zenkit::IInfo>())
1542 return nullptr;
1543 auto* h = sym->get_instance().get();
1544 if(h==nullptr)
1545 Log::e("invalid c_info object: \"",sym->name(),"\"");
1546 return reinterpret_cast<zenkit::IInfo*>(h);
1547 }
1548
1550 world().removeItem(it);
1551 }
1552
1553void GameScript::setInstanceNPC(std::string_view name, Npc &npc) {
1554 auto sym = vm.find_symbol_by_name(name);
1555 if(sym == nullptr) {
1556 Tempest::Log::e("Cannot set NPC instance ", name, ": Symbol not found.");
1557 return;
1558 }
1559 sym->set_instance(npc.handlePtr());
1560 }
1561
1562void GameScript::setInstanceItem(Npc &holder, size_t itemId) {
1563 storeItem(holder.getItem(itemId));
1564 }
1565
1567 return aiDefaultPipe.get();
1568 }
1569
1571 if(player.isPlayer())
1572 return owner.openDlgOuput(player,npc);
1573 return owner.openDlgOuput(npc,player);
1574 }
1575
1577 auto id = vm.find_symbol_by_name("PLAYER_PERC_ASSESSMAGIC");
1578 if(id==nullptr)
1579 return ScriptFn();
1580
1581 if(id->count()>0)
1582 return ScriptFn(uint32_t(id->get_int()));
1583 return ScriptFn();
1584 }
1585
1587 auto id = vm.find_symbol_by_name("NPC_DAM_DIVE_TIME");
1588 if(id==nullptr)
1589 return 0;
1590 return id->get_int();
1591 }
1592
1594 return damCriticalMultiplier;
1595 }
1596
1597uint32_t GameScript::lockPickId() const {
1598 return ItKE_lockpick!=nullptr ? ItKE_lockpick->index() : 0;
1599 }
1600
1601std::string_view GameScript::menuMain() const {
1602 if(dma!=nullptr)
1603 return dma->menuMain();
1604 return ::menuMain;
1605 }
1606
1607bool GameScript::game_initgerman() {
1608 return true;
1609 }
1610
1611bool GameScript::game_initenglish() {
1612 return true;
1613 }
1614
1615void GameScript::wld_settime(int hour, int minute) {
1616 world().setDayTime(hour,minute);
1617 }
1618
1619int GameScript::wld_getday() {
1620 return int(owner.time().day());
1621 }
1622
1623void GameScript::wld_playeffect(std::string_view visual, std::shared_ptr<zenkit::DaedalusInstance> sourceId, std::shared_ptr<zenkit::DaedalusInstance> targetId,
1624 int effectLevel, int damage, int damageType, int isProjectile) {
1625 if(aiProcessPolicy>=NpcProcessPolicy::AiFar2)
1626 return;
1627
1628 if(isProjectile!=0 || damageType!=0 || damage!=0 || effectLevel!=0) {
1629 // TODO
1630 Log::i("effect not implemented [",visual.data(),"]");
1631 return;
1632 }
1633 const VisualFx* vfx = Gothic::inst().loadVisualFx(visual);
1634 if(vfx==nullptr) {
1635 Log::i("invalid effect [",visual.data(),"]");
1636 return;
1637 }
1638
1639 auto dstNpc = findNpcById(targetId);
1640 auto srcNpc = findNpcById(sourceId);
1641
1642 auto dstItm = findItemById(targetId);
1643 auto srcItm = findItemById(sourceId);
1644
1645 if(srcNpc!=nullptr && dstNpc!=nullptr) {
1646 srcNpc->startEffect(*dstNpc,*vfx);
1647 } else
1648 if(srcItm!=nullptr && dstItm!=nullptr){
1649 Effect e(*vfx,world(),srcItm->position());
1650 e.setActive(true);
1651 world().runEffect(std::move(e));
1652 }
1653 }
1654
1655void GameScript::wld_stopeffect(std::string_view visual) {
1656 const VisualFx* vfx = Gothic::inst().loadVisualFx(visual);
1657 if(vfx==nullptr) {
1658 Log::i("invalid effect [",visual.data(),"]");
1659 return;
1660 }
1661 if(auto w = owner.world())
1662 w->stopEffect(*vfx);
1663 }
1664
1665int GameScript::wld_getplayerportalguild() {
1666 int32_t g = GIL_NONE;
1667 if(auto p = world().player())
1668 g = world().guildOfRoom(p->portalName());
1669 return g;
1670 }
1671
1672int GameScript::wld_getformerplayerportalguild() {
1673 int32_t g = GIL_NONE;
1674 if(auto p = world().player())
1675 g = world().guildOfRoom(p->formerPortalName());
1676 return g;
1677 }
1678
1679void GameScript::wld_setguildattitude(int gil1, int att, int gil2) {
1680 if(gil1<0 || gil2<0 || gil1>=int(gilCount) || gil2>=int(gilCount))
1681 return;
1682 gilAttitudes[size_t(gil1)*gilCount+size_t(gil2)] = att;
1683 }
1684
1685int GameScript::wld_getguildattitude(int gil1, int gil2) {
1686 if(gil1<0 || gil2<0 || gil1>=int(gilCount) || gil2>=int(gilCount))
1687 return ATT_HOSTILE; // error
1688 return gilAttitudes[size_t(gil1)*gilCount+size_t(gil2)];
1689 }
1690
1691void GameScript::wld_exchangeguildattitudes(std::string_view name) {
1692 auto guilds = vm.find_symbol_by_name(name);
1693 if(guilds==nullptr)
1694 return;
1695 for(size_t i=0;i<gilTblSize;++i) {
1696 for(size_t r=0;r<gilTblSize;++r)
1697 gilAttitudes[i*gilCount+r] = guilds->get_int(uint16_t(i * gilTblSize + r));
1698 }
1699 }
1700
1701bool GameScript::wld_istime(int hour0, int min0, int hour1, int min1) {
1702 gtime begin{hour0,min0}, end{hour1,min1};
1703 gtime now = owner.time();
1704 now = gtime(0,now.hour(),now.minute());
1705
1706 if(begin<=end && begin<=now && now<end)
1707 return true;
1708 else if(end<begin && (now<end || begin<=now))
1709 return true;
1710 else
1711 return 0;
1712 }
1713
1714bool GameScript::wld_isfpavailable(std::shared_ptr<zenkit::INpc> self, std::string_view name) {
1715 if(self==nullptr){
1716 return false;
1717 }
1718
1719 auto wp = world().findFreePoint(*findNpc(self.get()),name);
1720 return wp!=nullptr;
1721 }
1722
1723bool GameScript::wld_isnextfpavailable(std::shared_ptr<zenkit::INpc> self, std::string_view name) {
1724 if(self==nullptr){
1725 return false;
1726 }
1727 auto fp = world().findNextFreePoint(*findNpc(self.get()),name);
1728 return fp != nullptr;
1729 }
1730
1731bool GameScript::wld_ismobavailable(std::shared_ptr<zenkit::INpc> self, std::string_view name) {
1732 auto npc = findNpc(self);
1733 if(npc==nullptr) {
1734 return false;
1735 }
1736
1737 auto wp = world().availableMob(*npc, name);
1738 return wp != nullptr;
1739 }
1740
1741void GameScript::wld_setmobroutine(int h, int m, std::string_view name, int st) {
1742 world().setMobRoutine(gtime(h,m), name, st);
1743 }
1744
1745int GameScript::wld_getmobstate(std::shared_ptr<zenkit::INpc> npcRef, std::string_view scheme) {
1746 auto npc = findNpc(npcRef);
1747
1748 if(npc==nullptr) {
1749 return -1;
1750 }
1751
1752 auto mob = world().availableMob(*npc,scheme);
1753 if(mob==nullptr) {
1754 return -1;
1755 }
1756
1757 return std::max(0,mob->stateId());
1758 }
1759
1760void GameScript::wld_assignroomtoguild(std::string_view name, int g) {
1761 world().assignRoomToGuild(name,g);
1762 }
1763
1764bool GameScript::wld_detectnpc(std::shared_ptr<zenkit::INpc> npcRef, int inst, int state, int guild) {
1765 auto npc = findNpc(npcRef);
1766 if(npc==nullptr) {
1767 return false;
1768 }
1769
1770 Npc* ret =nullptr;
1771 float dist=std::numeric_limits<float>::max();
1772
1773 world().detectNpc(npc->position(), float(npc->handle().senses_range), [inst,state,guild,&ret,&dist,npc](Npc& n){
1774 if((inst ==-1 || int32_t(n.instanceSymbol())==inst) &&
1775 (state==-1 || n.isInState(uint32_t(state))) &&
1776 (guild==-1 || int32_t(n.guild())==guild) &&
1777 (&n!=npc) && !n.isDead()) {
1778 float d = n.qDistTo(*npc);
1779 if(d<dist){
1780 ret = &n;
1781 dist = d;
1782 }
1783 }
1784 });
1785 if(ret)
1786 vm.global_other()->set_instance(ret->handlePtr());
1787 return ret != nullptr;
1788 }
1789
1790bool GameScript::wld_detectnpcex(std::shared_ptr<zenkit::INpc> npcRef, int inst, int state, int guild, int player) {
1791 auto npc = findNpc(npcRef);
1792 if(npc==nullptr) {
1793 return false;
1794 }
1795 Npc* ret =nullptr;
1796 float dist=std::numeric_limits<float>::max();
1797
1798 world().detectNpc(npc->position(), float(npc->handle().senses_range), [inst,state,guild,&ret,&dist,npc,player](Npc& n){
1799 if((inst ==-1 || int32_t(n.instanceSymbol())==inst) &&
1800 (state==-1 || n.isInState(uint32_t(state))) &&
1801 (guild==-1 || int32_t(n.guild())==guild) &&
1802 (&n!=npc) && !n.isDead() &&
1803 (player!=0 || !n.isPlayer())) {
1804 float d = n.qDistTo(*npc);
1805 if(d<dist){
1806 ret = &n;
1807 dist = d;
1808 }
1809 }
1810 });
1811 if(ret)
1812 vm.global_other()->set_instance(ret->handlePtr());
1813 return ret != nullptr;
1814 }
1815
1816bool GameScript::wld_detectitem(std::shared_ptr<zenkit::INpc> npcRef, int flags) {
1817 auto npc = findNpc(npcRef);
1818 if(npc==nullptr) {
1819 return false;
1820 }
1821
1822 Item* ret =nullptr;
1823 float dist=std::numeric_limits<float>::max();
1824 world().detectItem(npc->position(), float(npc->handle().senses_range), [npc,&ret,&dist,flags](Item& it) {
1825 if((it.handle().main_flag&flags)==0)
1826 return;
1827 float d = (npc->position()-it.position()).quadLength();
1828 if(d<dist) {
1829 ret = &it;
1830 dist= d;
1831 }
1832 });
1833
1834 if(ret)
1835 vm.global_item()->set_instance(ret->handlePtr());
1836 return ret != nullptr;
1837 }
1838
1839void GameScript::wld_spawnnpcrange(std::shared_ptr<zenkit::INpc> npcRef, int clsId, int count, float lifeTime) {
1840 auto at = findNpc(npcRef);
1841 if(at==nullptr || clsId<=0)
1842 return;
1843
1844 (void)lifeTime;
1845 for(int32_t i=0;i<count;++i) {
1846 auto* npc = world().addNpc(size_t(clsId),at->position());
1847 fixNpcPosition(*npc,at->rotation()-90 + 360.f*float(i)/float(count),100);
1848 }
1849 }
1850
1851void GameScript::wld_sendtrigger(std::string_view triggerTarget) {
1852 if(triggerTarget.empty())
1853 return;
1854 auto& world = *owner.world();
1855 const TriggerEvent evt(std::string{triggerTarget},"",world.tickCount(),TriggerEvent::T_Trigger);
1856 world.triggerEvent(evt);
1857 }
1858
1859void GameScript::wld_senduntrigger(std::string_view triggerTarget) {
1860 if(triggerTarget.empty())
1861 return;
1862 auto& world = *owner.world();
1863 const TriggerEvent evt(std::string{triggerTarget},"",world.tickCount(),TriggerEvent::T_Untrigger);
1864 world.triggerEvent(evt);
1865 }
1866
1867bool GameScript::wld_israining() {
1868 static bool first=true;
1869 if(first){
1870 Log::e("not implemented call [wld_israining]");
1871 first=false;
1872 }
1873 return false;
1874 }
1875
1876void GameScript::mdl_setvisual(std::shared_ptr<zenkit::INpc> npcRef, std::string_view visual) {
1877 auto npc = findNpc(npcRef);
1878 if(npc==nullptr)
1879 return;
1880 npc->setVisual(visual);
1881 }
1882
1883void GameScript::mdl_setvisualbody(std::shared_ptr<zenkit::INpc> npcRef, std::string_view body, int bodyTexNr, int bodyTexColor, std::string_view head, int headTexNr, int teethTexNr, int armor) {
1884 auto npc = findNpc(npcRef);
1885 if(npc==nullptr)
1886 return;
1887
1888 npc->setVisualBody(headTexNr,teethTexNr,bodyTexNr,bodyTexColor,body,head);
1889 if(armor>0) {
1890 if(npc->itemCount(uint32_t(armor))==0)
1891 npc->addItem(uint32_t(armor),1);
1892 npc->useItem(uint32_t(armor),Item::NSLOT,true);
1893 }
1894 }
1895
1896void GameScript::mdl_setmodelfatness(std::shared_ptr<zenkit::INpc> npcRef, float fat) {
1897 auto npc = findNpc(npcRef);
1898 if(npc!=nullptr)
1899 npc->setFatness(fat);
1900 }
1901
1902void GameScript::mdl_applyoverlaymds(std::shared_ptr<zenkit::INpc> npcRef, std::string_view overlayname) {
1903 auto npc = findNpc(npcRef);
1904 if(npc!=nullptr) {
1905 auto skelet = Resources::loadSkeleton(overlayname);
1906 npc->addOverlay(skelet,0);
1907 }
1908 }
1909
1910void GameScript::mdl_applyoverlaymdstimed(std::shared_ptr<zenkit::INpc> npcRef, std::string_view overlayname, int ticks) {
1911 auto npc = findNpc(npcRef);
1912 if(npc!=nullptr && ticks>0) {
1913 auto skelet = Resources::loadSkeleton(overlayname);
1914 npc->addOverlay(skelet,uint64_t(ticks));
1915 }
1916 }
1917
1918void GameScript::mdl_removeoverlaymds(std::shared_ptr<zenkit::INpc> npcRef, std::string_view overlayname) {
1919 auto npc = findNpc(npcRef);
1920 if(npc!=nullptr) {
1921 auto skelet = Resources::loadSkeleton(overlayname);
1922 npc->delOverlay(skelet);
1923 }
1924}
1925
1926void GameScript::mdl_setmodelscale(std::shared_ptr<zenkit::INpc> npcRef, float x, float y, float z) {
1927 auto npc = findNpc(npcRef);
1928 if(npcRef!=nullptr)
1929 npc->setScale(x,y,z);
1930 }
1931
1932void GameScript::mdl_startfaceani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view ani, float intensity, float time) {
1933 if(npcRef!=nullptr)
1934 findNpc(npcRef.get())->startFaceAnim(ani,intensity,uint64_t(time*1000.f));
1935 }
1936
1937void GameScript::mdl_applyrandomani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view s1, std::string_view s0) {
1938 (void)npcRef;
1939 (void)s1;
1940 (void)s0;
1941
1942 static bool first=true;
1943 if(first){
1944 Log::e("not implemented call [mdl_applyrandomani]");
1945 first=false;
1946 }
1947 }
1948
1949void GameScript::mdl_applyrandomanifreq(std::shared_ptr<zenkit::INpc> npcRef, std::string_view s1, float f0) {
1950 (void)f0;
1951 (void)s1;
1952 (void)npcRef;
1953
1954 static bool first=true;
1955 if(first){
1956 Log::e("not implemented call [mdl_applyrandomanifreq]");
1957 first=false;
1958 }
1959 }
1960
1961void GameScript::mdl_applyrandomfaceani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view name, float timeMin, float timeMinVar, float timeMax, float timeMaxVar, float probMin) {
1962 (void)probMin;
1963 (void)timeMaxVar;
1964 (void)timeMax;
1965 (void)timeMinVar;
1966 (void)timeMin;
1967 (void)name;
1968 (void)npcRef;
1969
1970 static bool first=true;
1971 if(first){
1972 Log::e("not implemented call [mdl_applyrandomfaceani]");
1973 first=false;
1974 }
1975 }
1976
1977void GameScript::wld_insertnpc(int npcInstance, std::string_view spawnpoint) {
1978 if(npcInstance<=0)
1979 return;
1980
1981 auto npc = world().addNpc(size_t(npcInstance),spawnpoint);
1982 if(npc!=nullptr)
1983 fixNpcPosition(*npc,0,0);
1984 }
1985
1986void GameScript::wld_removenpc(int npcInstance) {
1987 if(auto npc = findNpcById(size_t(npcInstance)))
1988 world().removeNpc(*npc);
1989 }
1990
1991void GameScript::wld_insertitem(int itemInstance, std::string_view spawnpoint) {
1992 if(spawnpoint.empty() || itemInstance<=0)
1993 return;
1994
1995 world().addItem(size_t(itemInstance),spawnpoint);
1996 }
1997
1998void GameScript::npc_settofightmode(std::shared_ptr<zenkit::INpc> npcRef, int weaponSymbol) {
1999 if(npcRef!=nullptr && weaponSymbol>=0)
2000 findNpc(npcRef.get())->setToFightMode(size_t(weaponSymbol));
2001 }
2002
2003void GameScript::npc_settofistmode(std::shared_ptr<zenkit::INpc> npcRef) {
2004 auto npc = findNpc(npcRef);
2005 if(npc!=nullptr)
2006 npc->setToFistMode();
2007 }
2008
2009bool GameScript::npc_isinstate(std::shared_ptr<zenkit::INpc> npcRef, int stateFn) {
2010 auto npc = findNpc(npcRef);
2011 if(npc!=nullptr)
2012 return npc->isInState(uint32_t(stateFn));
2013 return false;
2014 }
2015
2016bool GameScript::npc_isinroutine(std::shared_ptr<zenkit::INpc> npcRef, int stateFn) {
2017 auto npc = findNpc(npcRef);
2018 if(npc!=nullptr)
2019 return npc->isInRoutine(uint32_t(stateFn));
2020 return false;
2021 }
2022
2023bool GameScript::npc_wasinstate(std::shared_ptr<zenkit::INpc> npcRef, int stateFn) {
2024 auto npc = findNpc(npcRef);
2025 if(npc!=nullptr)
2026 return npc->wasInState(uint32_t(stateFn));
2027 return false;
2028 }
2029
2030int GameScript::npc_getdisttowp(std::shared_ptr<zenkit::INpc> npcRef, std::string_view wpname) {
2031 auto npc = findNpc(npcRef);
2032 //NOTE: in CoM, some way-point and free-points share same name - need to be precise
2033 auto* wp = world().findWayPoint(wpname);
2034
2035 if(npc!=nullptr && wp!=nullptr){
2036 float ret = std::sqrt(npc->qDistTo(wp));
2037 if(ret<float(std::numeric_limits<int32_t>::max()))
2038 return int32_t(ret); else
2039 return std::numeric_limits<int32_t>::max();
2040 } else {
2041 return std::numeric_limits<int32_t>::max();
2042 }
2043 }
2044
2045void GameScript::npc_exchangeroutine(std::shared_ptr<zenkit::INpc> npcRef, std::string_view rname) {
2046 auto npc = findNpc(npcRef);
2047 if(npc!=nullptr) {
2048 auto& v = npc->handle();
2049 string_frm name("Rtn_",rname,'_',v.id);
2050
2051 auto* sym = vm.find_symbol_by_name(name);
2052 size_t d = sym != nullptr ? sym->index() : 0;
2053 if(d>0)
2054 npc->excRoutine(d);
2055 }
2056 }
2057
2058bool GameScript::npc_isdead(std::shared_ptr<zenkit::INpc> npcRef) {
2059 auto npc = findNpc(npcRef);
2060 return npc==nullptr || isDead(*npc);
2061 }
2062
2063bool GameScript::npc_knowsinfo(std::shared_ptr<zenkit::INpc> npcRef, int infoinstance) {
2064 auto npc = findNpc(npcRef);
2065 if(!npc)
2066 return false;
2067
2068 zenkit::INpc& vnpc = npc->handle();
2069 return doesNpcKnowInfo(vnpc, uint32_t(infoinstance));
2070 }
2071
2072void GameScript::npc_settalentskill(std::shared_ptr<zenkit::INpc> npcRef, int t, int lvl) {
2073 auto npc = findNpc(npcRef);
2074 if(npc!=nullptr)
2075 npc->setTalentSkill(Talent(t),lvl);
2076 }
2077
2078int GameScript::npc_gettalentskill(std::shared_ptr<zenkit::INpc> npcRef, int skillId) {
2079 auto npc = findNpc(npcRef);
2080 return npc==nullptr ? 0 : npc->talentSkill(Talent(skillId));
2081 }
2082
2083void GameScript::npc_settalentvalue(std::shared_ptr<zenkit::INpc> npcRef, int t, int lvl) {
2084 auto npc = findNpc(npcRef);
2085 if(npc!=nullptr)
2086 npc->setTalentValue(Talent(t),lvl);
2087 }
2088
2089int GameScript::npc_gettalentvalue(std::shared_ptr<zenkit::INpc> npcRef, int skillId) {
2090 auto npc = findNpc(npcRef);
2091 return npc==nullptr ? 0 : npc->talentValue(Talent(skillId));
2092 }
2093
2094void GameScript::npc_setrefusetalk(std::shared_ptr<zenkit::INpc> npcRef, int timeSec) {
2095 auto npc = findNpc(npcRef);
2096 if(npc)
2097 npc->setRefuseTalk(uint64_t(std::max(timeSec*1000,0)));
2098 }
2099
2100bool GameScript::npc_refusetalk(std::shared_ptr<zenkit::INpc> npcRef) {
2101 auto npc = findNpc(npcRef);
2102 return npc && npc->isRefuseTalk();
2103 }
2104
2105int GameScript::npc_hasitems(std::shared_ptr<zenkit::INpc> npcRef, int itemId) {
2106 auto npc = findNpc(npcRef);
2107 return npc!=nullptr ? int(npc->itemCount(uint32_t(itemId))) : 0;
2108 }
2109
2110bool GameScript::npc_hasspell(std::shared_ptr<zenkit::INpc> npcRef, int splId) {
2111 auto npc = findNpc(npcRef);
2112 return npc!=nullptr && npc->inventory().hasSpell(splId);
2113 }
2114
2115int GameScript::npc_getinvitem(std::shared_ptr<zenkit::INpc> npcRef, int itemId) {
2116 auto npc = findNpc(npcRef);
2117 auto itm = npc==nullptr ? nullptr : npc->getItem(uint32_t(itemId));
2118 storeItem(itm);
2119 if(itm!=nullptr) {
2120 return int(itm->handle().symbol_index());
2121 }
2122 return -1;
2123 }
2124
2125// This seems specific to Gothic 1, where the inventory was grouped into categories.
2126// In the shared inventory, these categories are just following one after the other.
2127// So we should transform this to iterate over the different categories in the
2128// shared inventory
2129int GameScript::npc_getinvitembyslot(std::shared_ptr<zenkit::INpc> npcRef, int cat, int slotnr) {
2130 auto npc = findNpc(npcRef);
2131 if(npc==nullptr) {
2132 storeItem(nullptr);
2133 return 0;
2134 }
2135
2136 // The category flag names were global, but for the scripts, only npc_getinvitembyslot
2137 // ever used them, so as long as nobody implements the Gothic 1 inventory, they can
2138 // be limited to the comments below.
2140 switch(cat) {
2141 case 1: // INV_WEAPON
2143 break;
2144 case 2: // INV_ARMOR
2145 f = ITM_CAT_ARMOR;
2146 break;
2147 case 3: // INV_RUNE
2148 f = ITM_CAT_RUNE;
2149 break;
2150 case 4: // INV_MAGIC
2151 f = ITM_CAT_MAGIC;
2152 break;
2153 case 5: // INV_FOOD
2154 f = ITM_CAT_FOOD;
2155 break;
2156 case 6: // INV_POTION
2157 f = ITM_CAT_POTION;
2158 break;
2159 case 7: // INV_DOC
2160 f = ITM_CAT_DOCS;
2161 break;
2162 case 8: // INV_MISC
2164 break;
2165 default:
2166 Log::e("Unknown item category ", cat);
2167 storeItem(nullptr);
2168 return 0;
2169 }
2170
2171 auto itm = npc==nullptr ? nullptr : npc->inventory().findByFlags(f, uint32_t(slotnr));
2172 // Store the found item in the global item var
2173 storeItem(itm);
2174
2175 return itm!=nullptr ? int(itm->count()) : 0;
2176 }
2177
2178int GameScript::npc_removeinvitem(std::shared_ptr<zenkit::INpc> npcRef, int itemId) {
2179 auto npc = findNpc(npcRef);
2180 if(npc!=nullptr)
2181 npc->delItem(uint32_t(itemId),1);
2182 return 0;
2183 }
2184
2185int GameScript::npc_removeinvitems(std::shared_ptr<zenkit::INpc> npcRef, int itemId, int amount) {
2186 auto npc = findNpc(npcRef);
2187
2188 if(npc!=nullptr && amount>0)
2189 npc->delItem(uint32_t(itemId),uint32_t(amount));
2190
2191 return 0;
2192 }
2193
2194int GameScript::npc_getbodystate(std::shared_ptr<zenkit::INpc> npcRef) {
2195 auto npc = findNpc(npcRef);
2196
2197 if(npc!=nullptr)
2198 return int32_t(npc->bodyState());
2199 return int32_t(0);
2200 }
2201
2202std::shared_ptr<zenkit::INpc> GameScript::npc_getlookattarget(std::shared_ptr<zenkit::INpc> npcRef) {
2203 auto npc = findNpc(npcRef);
2204 return npc && npc->lookAtTarget() ? npc->lookAtTarget()->handlePtr() : nullptr;
2205 }
2206
2207int GameScript::npc_getdisttonpc(std::shared_ptr<zenkit::INpc> aRef, std::shared_ptr<zenkit::INpc> bRef) {
2208 auto a = findNpc(aRef);
2209 auto b = findNpc(bRef);
2210
2211 if(a==nullptr || b==nullptr)
2212 return std::numeric_limits<int32_t>::max();
2213
2214 float ret = std::sqrt(a->qDistTo(*b));
2215 if(ret>float(std::numeric_limits<int32_t>::max()))
2216 return std::numeric_limits<int32_t>::max();
2217 return int(ret);
2218 }
2219
2220bool GameScript::npc_hasequippedarmor(std::shared_ptr<zenkit::INpc> npcRef) {
2221 auto npc = findNpc(npcRef);
2222 return npc!=nullptr && npc->currentArmor()!=nullptr;
2223 }
2224
2225void GameScript::npc_setperctime(std::shared_ptr<zenkit::INpc> npcRef, float sec) {
2226 auto npc = findNpc(npcRef);
2227 if(npc)
2228 npc->setPerceptionTime(uint64_t(sec*1000));
2229 }
2230
2231void GameScript::npc_percenable(std::shared_ptr<zenkit::INpc> npcRef, int pr, int fn) {
2232 auto npc = findNpc(npcRef);
2233 if(npc && fn>=0)
2234 npc->setPerceptionEnable(PercType(pr),size_t(fn));
2235 }
2236
2237void GameScript::npc_percdisable(std::shared_ptr<zenkit::INpc> npcRef, int pr) {
2238 auto npc = findNpc(npcRef);
2239 if(npc)
2241 }
2242
2243std::string GameScript::npc_getnearestwp(std::shared_ptr<zenkit::INpc> npcRef) {
2244 auto npc = findNpc(npcRef);
2245 auto wp = npc ? world().findWayPoint(npc->position()) : nullptr;
2246 if(wp)
2247 return wp->name;
2248 return "";
2249 }
2250
2251std::string GameScript::npc_getnextwp(std::shared_ptr<zenkit::INpc> npcRef) {
2252 auto npc = findNpc(npcRef);
2253 auto wp = npc ? world().findNextWayPoint(*npc) : nullptr;
2254 if(wp)
2255 return wp->name;
2256 return "";
2257 }
2258
2259void GameScript::npc_clearaiqueue(std::shared_ptr<zenkit::INpc> npcRef) {
2260 auto npc = findNpc(npcRef);
2261 if(npc)
2262 npc->clearAiQueue();
2263 }
2264
2265bool GameScript::npc_isplayer(std::shared_ptr<zenkit::INpc> npcRef) {
2266 auto npc = findNpc(npcRef);
2267 return npc && npc->isPlayer();
2268 }
2269
2270int GameScript::npc_getstatetime(std::shared_ptr<zenkit::INpc> npcRef) {
2271 auto npc = findNpc(npcRef);
2272 if(npc)
2273 return int32_t(npc->stateTime()/1000);
2274 return 0;
2275 }
2276
2277void GameScript::npc_setstatetime(std::shared_ptr<zenkit::INpc> npcRef, int val) {
2278 auto npc = findNpc(npcRef);
2279 if(npc)
2280 npc->setStateTime(val*1000);
2281 }
2282
2283void GameScript::npc_changeattribute(std::shared_ptr<zenkit::INpc> npcRef, int atr, int val) {
2284 auto npc = findNpc(npcRef);
2285 if(npc!=nullptr && atr>=0)
2286 npc->changeAttribute(Attribute(atr),val,false);
2287 }
2288
2289bool GameScript::npc_isonfp(std::shared_ptr<zenkit::INpc> npcRef, std::string_view val) {
2290 auto npc = findNpc(npcRef);
2291 if(npc==nullptr)
2292 return false;
2293
2294 auto w = npc->currentWayPoint();
2295 if(w==nullptr || !MoveAlgo::isClose(*npc,*w,MAX_AI_USE_DISTANCE) || !w->checkName(val))
2296 return false;
2297 return w->isFreePoint();
2298 }
2299
2300int GameScript::npc_getheighttonpc(std::shared_ptr<zenkit::INpc> aRef, std::shared_ptr<zenkit::INpc> bRef) {
2301 auto a = findNpc(aRef);
2302 auto b = findNpc(bRef);
2303 float ret = 0;
2304 if(a!=nullptr && b!=nullptr)
2305 ret = std::abs(a->position().y - b->position().y);
2306 return int32_t(ret);
2307 }
2308
2309std::shared_ptr<zenkit::IItem> GameScript::npc_getequippedmeleeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2310 auto npc = findNpc(npcRef);
2311 if(npc!=nullptr && npc->currentMeleeWeapon() != nullptr) {
2312 return npc->currentMeleeWeapon()->handlePtr();
2313 }
2314 return nullptr;
2315 }
2316
2317std::shared_ptr<zenkit::IItem> GameScript::npc_getequippedrangedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2318 auto npc = findNpc(npcRef);
2319 if(npc!=nullptr && npc->currentRangedWeapon() != nullptr) {
2320 return npc->currentRangedWeapon()->handlePtr();
2321 }
2322 return nullptr;
2323 }
2324
2325std::shared_ptr<zenkit::IItem> GameScript::npc_getequippedarmor(std::shared_ptr<zenkit::INpc> npcRef) {
2326 auto npc = findNpc(npcRef);
2327 if(npc!=nullptr && npc->currentArmor()!=nullptr) {
2328 return npc->currentArmor()->handlePtr();
2329 }
2330 return nullptr;
2331 }
2332
2333bool GameScript::npc_canseenpc(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> otherRef) {
2334 // 'see' functions are intended as ray-cast, ignoring hnpc->senses mask
2335 // https://discord.com/channels/989316194148433950/989333514543587339/1226664463697182760
2336 auto other = findNpc(otherRef);
2337 auto npc = findNpc(npcRef);
2338
2339 if(npc!=nullptr && other!=nullptr){
2340 return npc->canSeeNpc(*other,false);
2341 }
2342 return false;
2343 }
2344
2345bool GameScript::npc_canseenpcfreelos(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> otherRef) {
2346 auto npc = findNpc(npcRef);
2347 auto oth = findNpc(otherRef);
2348
2349 if(npc!=nullptr && oth!=nullptr){
2350 return npc->canSeeNpc(*oth,true);
2351 }
2352 return false;
2353 }
2354
2355bool GameScript::npc_canseeitem(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::IItem> itemRef) {
2356 auto npc = findNpc(npcRef);
2357 auto itm = findItem(itemRef.get());
2358
2359 if(npc!=nullptr && itm!=nullptr){
2360 return npc->canSeeItem(*itm,false);
2361 }
2362 return false;
2363 }
2364
2365bool GameScript::npc_hasequippedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2366 auto npc = findNpc(npcRef);
2367 return (npc!=nullptr &&
2368 (npc->currentMeleeWeapon()!=nullptr ||
2369 npc->currentRangedWeapon()!=nullptr));
2370 }
2371
2372bool GameScript::npc_hasequippedmeleeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2373 auto npc = findNpc(npcRef);
2374 return npc!=nullptr && npc->currentMeleeWeapon()!=nullptr;
2375 }
2376
2377bool GameScript::npc_hasequippedrangedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2378 auto npc = findNpc(npcRef);
2379 return npc!=nullptr && npc->currentRangedWeapon()!=nullptr;
2380 }
2381
2382int GameScript::npc_getactivespell(std::shared_ptr<zenkit::INpc> npcRef) {
2383 auto npc = findNpc(npcRef);
2384 if(npc==nullptr)
2385 return -1;
2386
2387 Item* w = npc->activeWeapon();
2388 if(w==nullptr || !w->isSpellOrRune())
2389 return -1;
2390
2391 makeCurrent(w);
2392 return w->spellId();
2393 }
2394
2395bool GameScript::npc_getactivespellisscroll(std::shared_ptr<zenkit::INpc> npcRef) {
2396 auto npc = findNpc(npcRef);
2397 if(npc==nullptr)
2398 return false;
2399
2400 Item* w = npc->activeWeapon();
2401 if(w==nullptr || !w->isSpell())
2402 return false;
2403
2404 return true;
2405 }
2406
2407bool GameScript::npc_isinfightmode(std::shared_ptr<zenkit::INpc> npcRef, int modeI) {
2408 auto npc = findNpc(npcRef);
2409 auto mode = FightMode(modeI);
2410
2411 if(npc==nullptr){
2412 return false;
2413 }
2414
2415 auto st = npc->weaponState();
2416 bool ret = false;
2417 if(mode==FightMode::FMODE_NONE){
2418 ret = (st==WeaponState::NoWeapon);
2419 }
2420 else if(mode==FightMode::FMODE_FIST){
2421 ret = (st==WeaponState::Fist);
2422 }
2423 else if(mode==FightMode::FMODE_MELEE){
2424 ret = (st==WeaponState::W1H || st==WeaponState::W2H);
2425 }
2426 else if(mode==FightMode::FMODE_FAR){
2427 ret = (st==WeaponState::Bow || st==WeaponState::CBow);
2428 }
2429 else if(mode==FightMode::FMODE_MAGIC){
2430 ret = (st==WeaponState::Mage);
2431 }
2432 return ret;
2433 }
2434
2435void GameScript::npc_settarget(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> otherRef) {
2436 auto oth = findNpc(otherRef);
2437 auto npc = findNpc(npcRef);
2438 if(npc)
2439 npc->setTarget(oth);
2440 }
2441
2449bool GameScript::npc_gettarget(std::shared_ptr<zenkit::INpc> npcRef) {
2450 auto npc = findNpc(npcRef);
2451 auto s = vm.global_other();
2452
2453 if(npc!=nullptr && npc->target()) {
2454 s->set_instance(npc->target()->handlePtr());
2455 return true;
2456 }
2457
2458 s->set_instance(nullptr);
2459 return false;
2460 }
2461
2462bool GameScript::npc_getnexttarget(std::shared_ptr<zenkit::INpc> npcRef) {
2463 auto npc = findNpc(npcRef);
2464 Npc* ret = nullptr;
2465
2466 if(npc!=nullptr){
2467 float dist = float(npc->handle().senses_range);
2468 dist*=dist;
2469
2470 world().detectNpc(npc->position(),float(npc->handle().senses_range),[&,npc](Npc& oth){
2471 if(&oth!=npc && !oth.isDown() && oth.isEnemy(*npc) && npc->canSenseNpc(oth,true)!=SensesBit::SENSE_NONE){
2472 float qd = oth.qDistTo(*npc);
2473 if(qd<dist){
2474 dist=qd;
2475 ret = &oth;
2476 }
2477 }
2478 return false;
2479 });
2480 if(ret!=nullptr)
2481 npc->setTarget(ret);
2482 }
2483
2484 auto s = vm.global_other();
2485 if(ret!=nullptr) {
2486 s->set_instance(ret->handlePtr());
2487 return true;
2488 } else {
2489 s->set_instance(nullptr);
2490 return false;
2491 }
2492 }
2493
2494void GameScript::npc_sendpassiveperc(std::shared_ptr<zenkit::INpc> npcRef, int id, std::shared_ptr<zenkit::INpc> victimRef, std::shared_ptr<zenkit::INpc> otherRef) {
2495 auto npc = findNpc(npcRef);
2496 auto other = findNpc(otherRef);
2497 auto victim = findNpc(victimRef);
2498
2499 if(npc && other && victim)
2500 world().sendPassivePerc(*npc,*other,*victim,id);
2501 else if(npc && other)
2502 world().sendPassivePerc(*npc,*other,id);
2503 }
2504
2505void GameScript::npc_sendsingleperc(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> otherRef, int id) {
2506 auto other = findNpc(otherRef);
2507 auto npc = findNpc(npcRef);
2508
2509 if(npc && other)
2510 other->perceptionProcess(*npc,nullptr,0,PercType(id));
2511 }
2512
2513bool GameScript::npc_checkinfo(std::shared_ptr<zenkit::INpc> npcRef, int imp) {
2514 auto n = findNpc(npcRef);
2515 auto hero = findNpc(vm.global_other());
2516 if(n==nullptr || hero==nullptr)
2517 return false;
2518
2519 auto& pl = hero->handle();
2520 auto& npc = n->handle();
2521 for(auto& info:dialogsInfo) {
2522 if(info->npc!=int32_t(npc.symbol_index()) || info->important!=imp)
2523 continue;
2524 bool npcKnowsInfo = doesNpcKnowInfo(pl,info->symbol_index());
2525 if(npcKnowsInfo && !info->permanent)
2526 continue;
2527 bool valid=false;
2528 if(info->condition) {
2529 auto* conditionSymbol = vm.find_symbol_by_index(uint32_t(info->condition));
2530 if (conditionSymbol != nullptr)
2531 valid = vm.call_function<int>(conditionSymbol)!=0;
2532 }
2533 if(valid) {
2534 return true;
2535 }
2536 }
2537 return false;
2538 }
2539
2540int GameScript::npc_getportalguild(std::shared_ptr<zenkit::INpc> npcRef) {
2541 int32_t g = GIL_NONE;
2542 auto npc = findNpc(npcRef);
2543 if(npc!=nullptr)
2544 g = world().guildOfRoom(npc->portalName());
2545 return g;
2546 }
2547
2548bool GameScript::npc_isinplayersroom(std::shared_ptr<zenkit::INpc> npcRef) {
2549 auto npc = findNpc(npcRef);
2550 auto pl = world().player();
2551
2552 if(npc!=nullptr && pl!=nullptr) {
2553 auto g1 = pl ->portalName();
2554 auto g2 = npc->portalName();
2555 if(g1==g2)
2556 return true;
2557 }
2558 return false;
2559 }
2560
2561std::shared_ptr<zenkit::IItem> GameScript::npc_getreadiedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2562 auto npc = findNpc(npcRef);
2563 if(npc==nullptr) {
2564 return 0;
2565 }
2566
2567 auto ret = npc->activeWeapon();
2568 if(ret!=nullptr) {
2569 makeCurrent(ret);
2570 return ret->handlePtr();
2571 } else {
2572 return nullptr;
2573 }
2574 }
2575
2576bool GameScript::npc_hasreadiedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2577 auto npc = findNpc(npcRef);
2578 if(npc==nullptr)
2579 return false;
2580 auto ws = npc->weaponState();
2581 return (ws==WeaponState::W1H || ws==WeaponState::W2H ||
2583 }
2584
2585bool GameScript::npc_hasreadiedmeleeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2586 auto npc = findNpc(npcRef);
2587 if(npc==nullptr) {
2588 return false;
2589 }
2590 auto ws = npc->weaponState();
2591 return ws==WeaponState::W1H || ws==WeaponState::W2H;
2592 }
2593
2594bool GameScript::npc_hasreadiedrangedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2595 auto npc = findNpc(npcRef);
2596 if(npc==nullptr)
2597 return false;
2598 auto ws = npc->weaponState();
2599 return ws==WeaponState::Bow || ws==WeaponState::CBow;
2600 }
2601
2602bool GameScript::npc_hasrangedweaponwithammo(std::shared_ptr<zenkit::INpc> npcRef) {
2603 auto npc = findNpc(npcRef);
2604 return npc!=nullptr && npc->inventory().hasRangedWeaponWithAmmo();
2605 }
2606
2607int GameScript::npc_isdrawingspell(std::shared_ptr<zenkit::INpc> npcRef) {
2608 auto npc = findNpc(npcRef);
2609 if(npc==nullptr)
2610 return 0;
2611
2612 auto ret = npc->activeWeapon();
2613 if(ret==nullptr || !ret->isSpell())
2614 return 0;
2615
2616 makeCurrent(ret);
2617 return int32_t(ret->clsId());
2618 }
2619
2620int GameScript::npc_isdrawingweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2621 auto npc = findNpc(npcRef);
2622 if(npc==nullptr)
2623 return 0;
2624
2625 auto ret = npc->activeWeapon();
2626 if(ret==nullptr || !ret->isSpell())
2627 return 0;
2628
2629 makeCurrent(ret);
2630 return int32_t(ret->clsId());
2631 }
2632
2633void GameScript::npc_perceiveall(std::shared_ptr<zenkit::INpc> npcRef) {
2634 (void)npcRef; // nop
2635 }
2636
2637void GameScript::npc_stopani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view name) {
2638 auto npc = findNpc(npcRef);
2639 if(npc!=nullptr)
2640 npc->stopAnim(name);
2641 }
2642
2643int GameScript::npc_settrueguild(std::shared_ptr<zenkit::INpc> npcRef, int gil) {
2644 auto npc = findNpc(npcRef);
2645 if(npc!=nullptr)
2646 npc->setTrueGuild(gil);
2647 return 0;
2648 }
2649
2650int GameScript::npc_gettrueguild(std::shared_ptr<zenkit::INpc> npcRef) {
2651 auto npc = findNpc(npcRef);
2652 if(npc!=nullptr)
2653 return npc->trueGuild();
2654 return int32_t(GIL_NONE);
2655 }
2656
2657void GameScript::npc_clearinventory(std::shared_ptr<zenkit::INpc> npcRef) {
2658 auto npc = findNpc(npcRef);
2659 if(npc!=nullptr)
2660 npc->clearInventory();
2661 }
2662
2663int GameScript::npc_getattitude(std::shared_ptr<zenkit::INpc> aRef, std::shared_ptr<zenkit::INpc> bRef) {
2664 auto a = findNpc(aRef);
2665 auto b = findNpc(bRef);
2666
2667 if(a!=nullptr && b!=nullptr){
2668 auto att=personAttitude(*a,*b);
2669 return att; //TODO: temp attitudes
2670 }
2671 return ATT_NEUTRAL;
2672 }
2673
2674int GameScript::npc_getpermattitude(std::shared_ptr<zenkit::INpc> aRef, std::shared_ptr<zenkit::INpc> bRef) {
2675 auto a = findNpc(aRef);
2676 auto b = findNpc(bRef);
2677
2678 if(a!=nullptr && b!=nullptr){
2679 auto att=personAttitude(*a,*b);
2680 return att;
2681 }
2682 return ATT_NEUTRAL;
2683 }
2684
2685void GameScript::npc_setattitude(std::shared_ptr<zenkit::INpc> npcRef, int att) {
2686 auto npc = findNpc(npcRef);
2687 if(npc!=nullptr)
2688 npc->setAttitude(Attitude(att));
2689 }
2690
2691void GameScript::npc_settempattitude(std::shared_ptr<zenkit::INpc> npcRef, int att) {
2692 auto npc = findNpc(npcRef);
2693 if(npc!=nullptr)
2694 npc->setTempAttitude(Attitude(att));
2695 }
2696
2697bool GameScript::npc_hasbodyflag(std::shared_ptr<zenkit::INpc> npcRef, int bodyflag) {
2698 auto npc = findNpc(npcRef);
2699 if(npc==nullptr)
2700 return false;
2701 return npc->hasStateFlag(BodyState(bodyflag));
2702 }
2703
2704int GameScript::npc_getlasthitspellid(std::shared_ptr<zenkit::INpc> npcRef) {
2705 auto npc = findNpc(npcRef);
2706 if(npc==nullptr){
2707 return 0;
2708 }
2709 return npc->lastHitSpellId();
2710 }
2711
2712int GameScript::npc_getlasthitspellcat(std::shared_ptr<zenkit::INpc> npcRef) {
2713 auto npc = findNpc(npcRef);
2714 if(npc==nullptr)
2715 return SPELL_GOOD;
2716
2717 const int id = npc->lastHitSpellId();
2718 auto& spell = spellDesc(id);
2719 return spell.spell_type;
2720 }
2721
2722void GameScript::npc_playani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view name) {
2723 auto npc = findNpc(npcRef);
2724 if(npc!=nullptr)
2725 npc->playAnimByName(name,BS_NONE);
2726 }
2727
2728bool GameScript::npc_isdetectedmobownedbynpc(std::shared_ptr<zenkit::INpc> usrRef, std::shared_ptr<zenkit::INpc> npcRef) {
2729 auto npc = findNpc(npcRef);
2730 auto usr = findNpc(usrRef);
2731
2732 if(npc!=nullptr && usr!=nullptr && usr->interactive()!=nullptr){
2733 auto* inst = vm.find_symbol_by_index(npc->instanceSymbol());
2734 auto ow = usr->interactive()->ownerName();
2735 return inst->name() == ow;
2736 }
2737 return false;
2738 }
2739
2740bool GameScript::npc_isdetectedmobownedbyguild(std::shared_ptr<zenkit::INpc> npcRef, int guild) {
2741 static bool first=true;
2742 if(first){
2743 Log::e("not implemented call [npc_isdetectedmobownedbyguild]");
2744 first=false;
2745 }
2746
2747 auto npc = findNpc(npcRef);
2748 (void)guild;
2749
2750 if(npc!=nullptr && npc->detectedMob()!=nullptr) {
2751 auto ow = npc->detectedMob()->ownerName();
2752 (void)ow;
2753 //vm.setReturn(inst.name==ow ? 1 : 0);
2754 return false;
2755 }
2756 return false;
2757 }
2758
2759std::string GameScript::npc_getdetectedmob(std::shared_ptr<zenkit::INpc> npcRef) {
2760 auto usr = findNpc(npcRef);
2761 if(usr!=nullptr && usr->detectedMob()!=nullptr){
2762 auto i = usr->detectedMob();
2763 return std::string(i->schemeName());
2764 }
2765 return "";
2766 }
2767
2768bool GameScript::npc_ownedbynpc(std::shared_ptr<zenkit::IItem> itmRef, std::shared_ptr<zenkit::INpc> npcRef) {
2769 auto npc = findNpc(npcRef);
2770 auto itm = findItem(itmRef.get());
2771 if(itm==nullptr || npc==nullptr) {
2772 return false;
2773 }
2774
2775 auto* sym = vm.find_symbol_by_index(uint32_t(itm->handle().owner));
2776 return sym != nullptr && npc->handlePtr()==sym->get_instance();
2777 }
2778
2779bool GameScript::npc_canseesource(std::shared_ptr<zenkit::INpc> npcRef) {
2780 auto self = findNpc(npcRef);
2781 if(!self)
2782 return false;
2783 return self->canSeeSource();
2784 }
2785
2786// Used (only?) in Gothic 1 in B_AssessEnemy, to prevent attacks during cutscenes.
2787bool GameScript::npc_isincutscene(std::shared_ptr<zenkit::INpc> npcRef) {
2788 auto npc = findNpc(npcRef);
2789 auto w = Gothic::inst().world();
2790 if(w==nullptr)
2791 return false;
2792
2793 if(npc!=nullptr && owner.isNpcInDialog(*npc))
2794 return true;
2795
2796 return false;
2797 }
2798
2799int GameScript::npc_getdisttoitem(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::IItem> itmRef) {
2800 auto itm = findItem(itmRef.get());
2801 auto npc = findNpc(npcRef);
2802 if(itm==nullptr || npc==nullptr) {
2803 return std::numeric_limits<int32_t>::max();
2804 }
2805 auto dp = itm->position()-npc->position();
2806 return int32_t(dp.length());
2807 }
2808
2809int GameScript::npc_getheighttoitem(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::IItem> itmRef) {
2810 auto itm = findItem(itmRef.get());
2811 auto npc = findNpc(npcRef);
2812 if(itm==nullptr || npc==nullptr) {
2813 return std::numeric_limits<int32_t>::max();
2814 }
2815 auto dp = int32_t(itm->position().y-npc->position().y);
2816 return std::abs(dp);
2817 }
2818
2819int GameScript::npc_getdisttoplayer(std::shared_ptr<zenkit::INpc> npcRef) {
2820 auto pl = world().player();
2821 auto npc = findNpc(npcRef);
2822 if(pl==nullptr || npc==nullptr) {
2823 return std::numeric_limits<int32_t>::max();
2824 }
2825 auto dp = pl->position()-npc->position();
2826 auto l = dp.length();
2827 if(l>float(std::numeric_limits<int32_t>::max())) {
2828 return std::numeric_limits<int32_t>::max();
2829 }
2830 return int32_t(l);
2831 }
2832
2833int GameScript::npc_getactivespellcat(std::shared_ptr<zenkit::INpc> npcRef) {
2834 auto npc = findNpc(npcRef);
2835 if(npc==nullptr)
2836 return SPELL_GOOD;
2837
2838 const Item* w = npc->activeWeapon();
2839 if(w==nullptr || !w->isSpellOrRune())
2840 return SPELL_GOOD;
2841
2842 const int id = w->spellId();
2843 auto& spell = spellDesc(id);
2844 return spell.spell_type;
2845 }
2846
2847int GameScript::npc_setactivespellinfo(std::shared_ptr<zenkit::INpc> npcRef, int v) {
2848 auto npc = findNpc(npcRef);
2849 if(npc!=nullptr)
2850 npc->setActiveSpellInfo(v);
2851 return 0;
2852 }
2853
2854int GameScript::npc_getactivespelllevel(std::shared_ptr<zenkit::INpc> npcRef) {
2855 int v = 0;
2856 auto npc = findNpc(npcRef);
2857 if(npc!=nullptr)
2858 v = npc->activeSpellLevel();
2859 return v;
2860 }
2861
2862void GameScript::ai_processinfos(std::shared_ptr<zenkit::INpc> npcRef) {
2863 auto npc = findNpc(npcRef);
2864 auto pl = owner.player();
2865 if(pl!=nullptr && npc!=nullptr) {
2866 aiOutOrderId=0;
2867 npc->aiPush(AiQueue::aiProcessInfo(*pl));
2868 }
2869 }
2870
2871void GameScript::ai_output(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> targetRef, std::string_view outputname) {
2872 auto target = findNpc(targetRef);
2873 auto self = findNpc(selfRef);
2874
2875 if(target==nullptr && owner.version().game==1)
2876 target = findNpc(vm.global_other());
2877
2878 if(self!=nullptr && target!=nullptr) {
2879 self->aiPush(AiQueue::aiOutput(*target,outputname,aiOutOrderId));
2880 ++aiOutOrderId;
2881 }
2882 }
2883
2884void GameScript::ai_stopprocessinfos(std::shared_ptr<zenkit::INpc> selfRef) {
2885 auto self = findNpc(selfRef);
2886 if(self) {
2887 self->aiPush(AiQueue::aiStopProcessInfo(aiOutOrderId));
2888 ++aiOutOrderId;
2889 }
2890 }
2891
2892void GameScript::ai_standup(std::shared_ptr<zenkit::INpc> selfRef) {
2893 auto self = findNpc(selfRef);
2894 if(self!=nullptr)
2895 self->aiPush(AiQueue::aiStandup());
2896 }
2897
2898void GameScript::ai_standupquick(std::shared_ptr<zenkit::INpc> selfRef) {
2899 auto self = findNpc(selfRef);
2900 if(self!=nullptr)
2901 self->aiPush(AiQueue::aiStandupQuick());
2902 }
2903
2904void GameScript::ai_continueroutine(std::shared_ptr<zenkit::INpc> selfRef) {
2905 auto self = findNpc(selfRef);
2906 if(self!=nullptr)
2907 self->aiPush(AiQueue::aiContinueRoutine());
2908 }
2909
2910void GameScript::ai_stoplookat(std::shared_ptr<zenkit::INpc> selfRef) {
2911 auto self = findNpc(selfRef);
2912 if(self!=nullptr)
2913 self->aiPush(AiQueue::aiStopLookAt());
2914 }
2915
2916void GameScript::ai_lookat(std::shared_ptr<zenkit::INpc> selfRef, std::string_view waypoint) {
2917 auto self = findNpc(selfRef);
2918 auto to = world().findPoint(waypoint);
2919 if(self!=nullptr)
2920 self->aiPush(AiQueue::aiLookAt(to));
2921 }
2922
2923void GameScript::ai_lookatnpc(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> npcRef) {
2924 auto npc = findNpc(npcRef);
2925 auto self = findNpc(selfRef);
2926 if(self!=nullptr)
2927 self->aiPush(AiQueue::aiLookAtNpc(npc));
2928 }
2929
2930void GameScript::ai_removeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
2931 auto npc = findNpc(npcRef);
2932 if(npc!=nullptr)
2934 }
2935
2936void GameScript::ai_unreadyspell(std::shared_ptr<zenkit::INpc> npcRef) {
2937 auto npc = findNpc(npcRef);
2938 if(npc!=nullptr)
2940 }
2941
2942void GameScript::ai_turnaway(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> npcRef) {
2943 auto npc = findNpc(npcRef);
2944 auto self = findNpc(selfRef);
2945 if(self!=nullptr)
2946 self->aiPush(AiQueue::aiTurnAway(npc));
2947 }
2948
2949void GameScript::ai_turntonpc(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> npcRef) {
2950 auto npc = findNpc(npcRef);
2951 auto self = findNpc(selfRef);
2952 if(self!=nullptr)
2953 self->aiPush(AiQueue::aiTurnToNpc(npc));
2954 }
2955
2956void GameScript::ai_whirlaround(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> npcRef) {
2957 auto npc = findNpc(npcRef);
2958 auto self = findNpc(selfRef);
2959 if(self!=nullptr)
2960 self->aiPush(AiQueue::aiWhirlToNpc(npc));
2961 }
2962
2963void GameScript::ai_outputsvm(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> targetRef, std::string_view name) {
2964 auto target = findNpc(targetRef);
2965 auto self = findNpc(selfRef);
2966
2967 if(target==nullptr && owner.version().game==1)
2968 target = findNpc(vm.global_other());
2969
2970 if(self!=nullptr && target!=nullptr) {
2971 self->aiPush(AiQueue::aiOutputSvm(*target,name,aiOutOrderId));
2972 ++aiOutOrderId;
2973 }
2974 }
2975
2976void GameScript::ai_outputsvm_overlay(std::shared_ptr<zenkit::INpc> selfRef, std::shared_ptr<zenkit::INpc> targetRef, std::string_view name) {
2977 auto target = findNpc(targetRef);
2978 auto self = findNpc(selfRef);
2979
2980 if(target==nullptr && owner.version().game==1)
2981 target = findNpc(vm.global_other());
2982
2983 if(self!=nullptr && target!=nullptr) {
2984 self->aiPush(AiQueue::aiOutputSvmOverlay(*target,name,aiOutOrderId));
2985 ++aiOutOrderId;
2986 }
2987 }
2988
2989void GameScript::ai_startstate(std::shared_ptr<zenkit::INpc> selfRef, int func, int state, std::string_view wp) {
2990 auto self = findNpc(selfRef);
2991 if(self!=nullptr && func>0) {
2992 Npc* oth = findNpc(vm.global_other());
2993 Npc* vic = findNpc(vm.global_victim());
2994 auto& st = aiState(size_t(func));
2995 self->aiPush(AiQueue::aiStartState(st.funcIni,state,oth,vic,wp));
2996 }
2997 }
2998
2999void GameScript::ai_playani(std::shared_ptr<zenkit::INpc> npcRef, std::string_view name) {
3000 auto npc = findNpc(npcRef);
3001 if(npc!=nullptr)
3002 npc->aiPush(AiQueue::aiPlayAnim(name));
3003 }
3004
3005void GameScript::ai_setwalkmode(std::shared_ptr<zenkit::INpc> npcRef, int modeBits) {
3006 int32_t weaponBit = 0x80;
3007 auto npc = findNpc(npcRef);
3008
3009 int32_t mode = modeBits & (~weaponBit);
3010 if(npc!=nullptr && mode>=0 && mode<=3){ //TODO: weapon flags
3012 }
3013 }
3014
3015void GameScript::ai_wait(std::shared_ptr<zenkit::INpc> npcRef, float ms) {
3016 auto npc = findNpc(npcRef);
3017 if(npc!=nullptr && ms>0)
3018 npc->aiPush(AiQueue::aiWait(uint64_t(ms*1000)));
3019 }
3020
3021void GameScript::ai_waitms(std::shared_ptr<zenkit::INpc> npcRef, int ms) {
3022 auto npc = findNpc(npcRef);
3023 if(npc!=nullptr && ms>0)
3024 npc->aiPush(AiQueue::aiWait(uint64_t(ms)));
3025 }
3026
3027void GameScript::ai_aligntowp(std::shared_ptr<zenkit::INpc> npcRef) {
3028 auto npc = findNpc(npcRef);
3029 if(npc)
3031 }
3032
3033void GameScript::ai_gotowp(std::shared_ptr<zenkit::INpc> npcRef, std::string_view waypoint) {
3034 auto npc = findNpc(npcRef);
3035 if(npc==nullptr)
3036 return;
3037
3038 auto to = world().findWayPoint(npc->position(), waypoint);
3039 if(to!=nullptr) {
3040 npc->aiPush(AiQueue::aiGoToPoint(*to));
3041 return;
3042 }
3043
3044 // in vanilla 'ai_gotowp' sometimes is used incorrectly, so we need to check all other points
3045 to = world().findPoint(waypoint, false);
3046 if(to!=nullptr)
3047 npc->aiPush(AiQueue::aiGoToPoint(*to));
3048 }
3049
3050void GameScript::ai_gotofp(std::shared_ptr<zenkit::INpc> npcRef, std::string_view waypoint) {
3051 auto npc = findNpc(npcRef);
3052
3053 if(npc) {
3054 auto to = world().findFreePoint(*npc,waypoint);
3055 if(to!=nullptr)
3056 npc->aiPush(AiQueue::aiGoToPoint(*to));
3057 }
3058 }
3059
3060void GameScript::ai_playanibs(std::shared_ptr<zenkit::INpc> npcRef, std::string_view ani, int bs) {
3061 auto npc = findNpc(npcRef);
3062 if(npc!=nullptr)
3063 npc->aiPush(AiQueue::aiPlayAnimBs(ani,BodyState(bs)));
3064 }
3065
3066void GameScript::ai_equiparmor(std::shared_ptr<zenkit::INpc> npcRef, int id) {
3067 auto npc = findNpc(npcRef);
3068 if(npc!=nullptr)
3069 npc->aiPush(AiQueue::aiEquipArmor(id));
3070 }
3071
3072void GameScript::ai_equipbestarmor(std::shared_ptr<zenkit::INpc> npcRef) {
3073 auto npc = findNpc(npcRef);
3074 if(npc!=nullptr)
3076 }
3077
3078int GameScript::ai_equipbestmeleeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
3079 auto npc = findNpc(npcRef);
3080 if(npc!=nullptr)
3082 return 0;
3083 }
3084
3085int GameScript::ai_equipbestrangedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
3086 auto npc = findNpc(npcRef);
3087 if(npc!=nullptr)
3089 return 0;
3090 }
3091
3092bool GameScript::ai_usemob(std::shared_ptr<zenkit::INpc> npcRef, std::string_view tg, int state) {
3093 auto npc = findNpc(npcRef);
3094 if(npc!=nullptr)
3095 npc->aiPush(AiQueue::aiUseMob(tg,state));
3096 return 0;
3097 }
3098
3099void GameScript::ai_teleport(std::shared_ptr<zenkit::INpc> npcRef, std::string_view tg) {
3100 auto npc = findNpc(npcRef);
3101 auto pt = world().findPoint(tg);
3102 if(npc!=nullptr && pt!=nullptr)
3103 npc->aiPush(AiQueue::aiTeleport(*pt));
3104 }
3105
3106void GameScript::ai_stoppointat(std::shared_ptr<zenkit::INpc> npcRef) {
3107 auto npc = findNpc(npcRef);
3108 if(npc!=nullptr)
3110 }
3111
3112void GameScript::ai_drawweapon(std::shared_ptr<zenkit::INpc> npcRef) {
3113 auto npc = findNpc(npcRef);
3114 if(npc!=nullptr)
3116 }
3117
3118void GameScript::ai_readymeleeweapon(std::shared_ptr<zenkit::INpc> npcRef) {
3119 auto npc = findNpc(npcRef);
3120 if(npc!=nullptr)
3122 }
3123
3124void GameScript::ai_readyrangedweapon(std::shared_ptr<zenkit::INpc> npcRef) {
3125 auto npc = findNpc(npcRef);
3126 if(npc!=nullptr)
3128 }
3129
3130void GameScript::ai_readyspell(std::shared_ptr<zenkit::INpc> npcRef, int spell, int mana) {
3131 auto npc = findNpc(npcRef);
3132 if(npc!=nullptr && mana>0)
3133 npc->aiPush(AiQueue::aiReadySpell(spell,mana));
3134 }
3135
3136void GameScript::ai_attack(std::shared_ptr<zenkit::INpc> npcRef) {
3137 auto npc = findNpc(npcRef);
3138 if(npc!=nullptr)
3139 npc->aiPush(AiQueue::aiAttack());
3140 }
3141
3142void GameScript::ai_flee(std::shared_ptr<zenkit::INpc> npcRef) {
3143 auto npc = findNpc(npcRef);
3144 if(npc!=nullptr)
3145 npc->aiPush(AiQueue::aiFlee());
3146 }
3147
3148void GameScript::ai_dodge(std::shared_ptr<zenkit::INpc> npcRef) {
3149 auto npc = findNpc(npcRef);
3150 if(npc!=nullptr)
3151 npc->aiPush(AiQueue::aiDodge());
3152 }
3153
3154void GameScript::ai_unequipweapons(std::shared_ptr<zenkit::INpc> npcRef) {
3155 auto npc = findNpc(npcRef);
3156 if(npc!=nullptr)
3158 }
3159
3160void GameScript::ai_unequiparmor(std::shared_ptr<zenkit::INpc> npcRef) {
3161 auto npc = findNpc(npcRef);
3162 if(npc!=nullptr)
3164 }
3165
3166void GameScript::ai_gotonpc(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> toRef) {
3167 auto to = findNpc(toRef);
3168 auto npc = findNpc(npcRef);
3169 if(npc!=nullptr)
3170 npc->aiPush(AiQueue::aiGoToNpc(to));
3171 }
3172
3173void GameScript::ai_gotonextfp(std::shared_ptr<zenkit::INpc> npcRef, std::string_view to) {
3174 auto npc = findNpc(npcRef);
3175 if(npc!=nullptr)
3176 npc->aiPush(AiQueue::aiGoToNextFp(to));
3177 }
3178
3179void GameScript::ai_aligntofp(std::shared_ptr<zenkit::INpc> npcRef) {
3180 auto npc = findNpc(npcRef);
3181 if(npc!=nullptr)
3183 }
3184
3185void GameScript::ai_useitem(std::shared_ptr<zenkit::INpc> npcRef, int item) {
3186 auto npc = findNpc(npcRef);
3187 if(npc)
3188 npc->aiPush(AiQueue::aiUseItem(item));
3189 }
3190
3191void GameScript::ai_useitemtostate(std::shared_ptr<zenkit::INpc> npcRef, int item, int state) {
3192 auto npc = findNpc(npcRef);
3193 if(npc)
3194 npc->aiPush(AiQueue::aiUseItemToState(item,state));
3195 }
3196
3197void GameScript::ai_setnpcstostate(std::shared_ptr<zenkit::INpc> npcRef, int state, int radius) {
3198 auto npc = findNpc(npcRef);
3199 if(npc && state>0)
3200 npc->aiPush(AiQueue::aiSetNpcsToState(size_t(state),radius));
3201 }
3202
3203void GameScript::ai_finishingmove(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> othRef) {
3204 auto oth = findNpc(othRef);
3205 auto npc = findNpc(npcRef);
3206 if(npc!=nullptr && oth!=nullptr)
3207 npc->aiPush(AiQueue::aiFinishingMove(*oth));
3208 }
3209
3210void GameScript::ai_takeitem(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::IItem> itmRef) {
3211 auto itm = findItem(itmRef.get());
3212 auto npc = findNpc(npcRef);
3213 if(npc!=nullptr && itm!=nullptr)
3214 npc->aiPush(AiQueue::aiTakeItem(*itm));
3215 }
3216
3217void GameScript::ai_gotoitem(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::IItem> itmRef) {
3218 auto itm = findItem(itmRef.get());
3219 auto npc = findNpc(npcRef);
3220 if(npc!=nullptr && itm!=nullptr)
3221 npc->aiPush(AiQueue::aiGotoItem(*itm));
3222 }
3223
3224void GameScript::ai_pointat(std::shared_ptr<zenkit::INpc> npcRef, std::string_view waypoint) {
3225 auto npc = findNpc(npcRef);
3226 auto to = world().findPoint(waypoint);
3227 if(npc!=nullptr && to!=nullptr)
3228 npc->aiPush(AiQueue::aiPointAt(*to));
3229 }
3230
3231void GameScript::ai_pointatnpc(std::shared_ptr<zenkit::INpc> npcRef, std::shared_ptr<zenkit::INpc> otherRef) {
3232 auto other = findNpc(otherRef);
3233 auto npc = findNpc(npcRef);
3234 if(npc!=nullptr && other!=nullptr)
3235 npc->aiPush(AiQueue::aiPointAtNpc(*other));
3236 }
3237
3238int GameScript::ai_printscreen(std::string_view msg, int posx, int posy, std::string_view font, int timesec) {
3239 auto npc = findNpc(vm.global_self());
3240 if(npc==nullptr)
3241 npc = owner.player();
3242 if(npc==nullptr) {
3243 Gothic::inst().onPrintScreen(msg,posx,posy,timesec,Resources::font(font,Resources::FontType::Normal,1.0));
3244 return 0;
3245 }
3246 npc->aiPush(AiQueue::aiPrintScreen(timesec,font,posx,posy,msg));
3247 return 0;
3248 }
3249
3250int GameScript::mob_hasitems(std::string_view tag, int item) {
3251 return int(world().hasItems(tag,uint32_t(item)));
3252 }
3253
3254void GameScript::ta_min(std::shared_ptr<zenkit::INpc> npcRef, int start_h, int start_m, int stop_h, int stop_m, int action, std::string_view waypoint) {
3255 auto npc = findNpc(npcRef);
3256 if(npc!=nullptr)
3257 npc->addRoutine(gtime(start_h,start_m),gtime(stop_h,stop_m),uint32_t(action),waypoint);
3258 }
3259
3260void GameScript::log_createtopic(std::string_view topicName, int section) {
3261 if(section==QuestLog::Mission || section==QuestLog::Note)
3262 quests.add(topicName,QuestLog::Section(section));
3263 }
3264
3265void GameScript::log_settopicstatus(std::string_view topicName, int status) {
3266 if(status==int32_t(QuestLog::Status::Running) ||
3267 status==int32_t(QuestLog::Status::Success) ||
3268 status==int32_t(QuestLog::Status::Failed ) ||
3269 status==int32_t(QuestLog::Status::Obsolete))
3270 quests.setStatus(topicName,QuestLog::Status(status));
3271 }
3272
3273void GameScript::log_addentry(std::string_view topicName, std::string_view entry) {
3274 quests.addEntry(topicName,entry);
3275 }
3276
3277void GameScript::equipitem(std::shared_ptr<zenkit::INpc> npcRef, int cls) {
3278 auto self = findNpc(npcRef);
3279 if(self!=nullptr) {
3280 if(self->itemCount(uint32_t(cls))==0)
3281 self->addItem(uint32_t(cls),1);
3282 self->useItem(uint32_t(cls),Item::NSLOT,true);
3283 }
3284 }
3285
3286void GameScript::createinvitem(std::shared_ptr<zenkit::INpc> npcRef, int itemInstance) {
3287 auto self = findNpc(npcRef);
3288 if(self!=nullptr) {
3289 Item* itm = self->addItem(uint32_t(itemInstance),1);
3290 storeItem(itm);
3291 }
3292 }
3293
3294void GameScript::createinvitems(std::shared_ptr<zenkit::INpc> npcRef, int itemInstance, int amount) {
3295 auto self = findNpc(npcRef);
3296 if(self!=nullptr && amount>0) {
3297 Item* itm = self->addItem(uint32_t(itemInstance),size_t(amount));
3298 storeItem(itm);
3299 }
3300 }
3301
3302void GameScript::perc_setrange(int perc, int dist) {
3303 if(perc<0 || perc>=PERC_Count)
3304 return;
3305 perceptionRanges.range[perc] = dist;
3306 }
3307
3308int GameScript::hlp_getinstanceid(std::shared_ptr<zenkit::DaedalusInstance> instance) {
3309 // Log::d("hlp_getinstanceid: name \"",handle.name,"\" not found");
3310 return instance == nullptr ? -1 : int(instance->symbol_index());
3311 }
3312
3313int GameScript::hlp_random(int bound) {
3314 uint32_t mod = uint32_t(std::max(1,bound));
3315 return int32_t(randGen() % mod);
3316 }
3317
3318bool GameScript::hlp_isvalidnpc(std::shared_ptr<zenkit::INpc> npcRef) {
3319 auto self = findNpc(npcRef);
3320 return self != nullptr;
3321 }
3322
3323bool GameScript::hlp_isitem(std::shared_ptr<zenkit::IItem> itemRef, int instanceSymbol) {
3324 auto item = findItem(itemRef.get());
3325 if(item!=nullptr){
3326 auto& v = item->handle();
3327 return int(v.symbol_index()) == instanceSymbol;
3328 } else {
3329 return false;
3330 }
3331 }
3332
3333bool GameScript::hlp_isvaliditem(std::shared_ptr<zenkit::IItem> itemRef) {
3334 auto item = findItem(itemRef.get());
3335 return item!=nullptr;
3336 }
3337
3338std::shared_ptr<zenkit::INpc> GameScript::hlp_getnpc(int instanceSymbol) {
3339 auto npc = findNpcById(uint32_t(instanceSymbol));
3340 if(npc != nullptr)
3341 return npc->handlePtr();
3342 else
3343 return nullptr;
3344 }
3345
3346void GameScript::info_addchoice(int infoInstance, std::string_view text, int func) {
3347 auto info = findInfo(uint32_t(infoInstance));
3348 if(info==nullptr)
3349 return;
3350 zenkit::IInfoChoice choice {};
3351 choice.text = text;
3352 choice.function = func;
3353 info->add_choice(choice);
3354 }
3355
3356void GameScript::info_clearchoices(int infoInstance) {
3357 auto info = findInfo(uint32_t(infoInstance));
3358 if(info==nullptr)
3359 return;
3360 info->choices.clear();
3361 }
3362
3363bool GameScript::infomanager_hasfinished() {
3364 return !owner.isInDialog();
3365 }
3366
3367void GameScript::snd_play(std::string_view fileS) {
3368 if(aiProcessPolicy>=NpcProcessPolicy::AiFar2)
3369 return;
3370
3371 std::string file {fileS};
3372 for(auto& c:file)
3373 c = char(std::toupper(c));
3375 }
3376
3377void GameScript::snd_play3d(std::shared_ptr<zenkit::INpc> npcRef, std::string_view fileS) {
3378 if(aiProcessPolicy>=NpcProcessPolicy::AiFar2)
3379 return;
3380
3381 std::string file {fileS};
3382 Npc* npc = findNpc(npcRef);
3383 if(npc==nullptr)
3384 return;
3385 for(auto& c:file)
3386 c = char(std::toupper(c));
3387 auto sfx = ::Sound(*owner.world(),::Sound::T_3D,file,npc->position(),0.f,false);
3388 sfx.play();
3389 }
3390
3391void GameScript::exitsession() {
3392 owner.exitSession();
3393 }
3394
3395void GameScript::sort(std::vector<GameScript::DlgChoice> &dlg) {
3396 std::sort(dlg.begin(),dlg.end(),[](const GameScript::DlgChoice& l,const GameScript::DlgChoice& r){
3397 return std::tie(l.sort,l.scriptFn)<std::tie(r.sort,r.scriptFn); // small hack with scriptfn to reproduce behavior of original game
3398 });
3399 }
3400
3401void GameScript::setNpcInfoKnown(const zenkit::INpc& npc, const zenkit::IInfo& info) {
3402 auto id = std::make_pair(vm.find_symbol_by_instance(npc)->index(),vm.find_symbol_by_instance(info)->index());
3403 dlgKnownInfos.insert(id);
3404 }
3405
3406bool GameScript::doesNpcKnowInfo(const zenkit::INpc& npc, size_t infoInstance) const {
3407 auto id = std::make_pair(vm.find_symbol_by_instance(npc)->index(),infoInstance);
3408 return dlgKnownInfos.find(id)!=dlgKnownInfos.end();
3409 }
static AiAction aiPlayAnimBs(std::string_view ani, BodyState bs)
Definition aiqueue.cpp:154
static AiAction aiTakeItem(Item &item)
Definition aiqueue.cpp:380
static AiAction aiReadyRangedWeapon()
Definition aiqueue.cpp:255
static AiAction aiEquipBestRangedWeapon()
Definition aiqueue.cpp:207
static AiAction aiFlee()
Definition aiqueue.cpp:275
static AiAction aiAlignToWp()
Definition aiqueue.cpp:352
static AiAction aiUseMob(std::string_view name, int st)
Definition aiqueue.cpp:213
static AiAction aiUseItemToState(int32_t id, int32_t state)
Definition aiqueue.cpp:228
static AiAction aiGotoItem(Item &item)
Definition aiqueue.cpp:387
static AiAction aiRemoveWeapon()
Definition aiqueue.cpp:95
static AiAction aiDrawWeapon()
Definition aiqueue.cpp:243
static AiAction aiStopLookAt()
Definition aiqueue.cpp:89
static AiAction aiLookAtNpc(Npc *other)
Definition aiqueue.cpp:82
static AiAction aiPointAtNpc(Npc &other)
Definition aiqueue.cpp:401
static AiAction aiAttack()
Definition aiqueue.cpp:269
static AiAction aiWhirlToNpc(Npc *other)
Definition aiqueue.cpp:115
static AiAction aiTurnAway(Npc *other)
Definition aiqueue.cpp:101
static AiAction aiPointAt(const WayPoint &to)
Definition aiqueue.cpp:394
static AiAction aiStartState(ScriptFn stateFn, int behavior, Npc *other, Npc *victim, std::string_view wp)
Definition aiqueue.cpp:136
static AiAction aiTurnToNpc(Npc *other)
Definition aiqueue.cpp:108
static AiAction aiUnEquipArmor()
Definition aiqueue.cpp:293
static AiAction aiReadyMeleeWeapon()
Definition aiqueue.cpp:249
static AiAction aiPrintScreen(int time, std::string_view font, int x, int y, std::string_view msg)
Definition aiqueue.cpp:414
static AiAction aiOutputSvmOverlay(Npc &to, std::string_view text, int order)
Definition aiqueue.cpp:324
static AiAction aiReadySpell(int32_t spell, int32_t mana)
Definition aiqueue.cpp:261
static AiAction aiDodge()
Definition aiqueue.cpp:281
static AiAction aiSetNpcsToState(ScriptFn func, int32_t radius)
Definition aiqueue.cpp:358
static AiAction aiUseItem(int32_t id)
Definition aiqueue.cpp:221
static AiAction aiUnEquipWeapons()
Definition aiqueue.cpp:287
static AiAction aiLookAt(const WayPoint *to)
Definition aiqueue.cpp:75
static AiAction aiEquipBestArmor()
Definition aiqueue.cpp:195
static AiAction aiStandup()
Definition aiqueue.cpp:169
static AiAction aiProcessInfo(Npc &other)
Definition aiqueue.cpp:299
static AiAction aiTeleport(const WayPoint &to)
Definition aiqueue.cpp:236
static AiAction aiOutputSvm(Npc &to, std::string_view text, int order)
Definition aiqueue.cpp:315
static AiAction aiSetWalkMode(WalkBit w)
Definition aiqueue.cpp:366
static AiAction aiContinueRoutine()
Definition aiqueue.cpp:340
static AiAction aiGoToNpc(Npc *other)
Definition aiqueue.cpp:122
static AiAction aiGoToPoint(const WayPoint &to)
Definition aiqueue.cpp:181
static AiAction aiAlignToFp()
Definition aiqueue.cpp:346
static AiAction aiWait(uint64_t dt)
Definition aiqueue.cpp:162
static AiAction aiEquipArmor(int32_t id)
Definition aiqueue.cpp:188
static AiAction aiFinishingMove(Npc &other)
Definition aiqueue.cpp:373
static AiAction aiGoToNextFp(std::string_view fp)
Definition aiqueue.cpp:129
static AiAction aiStopProcessInfo(int order)
Definition aiqueue.cpp:333
static AiAction aiEquipBestMeleeWeapon()
Definition aiqueue.cpp:201
static AiAction aiStopPointAt()
Definition aiqueue.cpp:408
static AiAction aiPlayAnim(std::string_view ani)
Definition aiqueue.cpp:147
static AiAction aiStandupQuick()
Definition aiqueue.cpp:175
static AiAction aiOutput(Npc &to, std::string_view text, int order)
Definition aiqueue.cpp:306
size_t funcIni
Definition aistate.h:11
std::u16string cutscenePath() const
static const CommandLine & inst()
static bool isRequired(zenkit::DaedalusScript &vm)
void printMobMissingKeyOrLockpick(Npc &npc)
void invokeRefreshAtInsert(Npc &npc)
size_t findSymbolIndex(std::string_view s)
Attitude guildAttitude(const Npc &p0, const Npc &p1) const
void printCannotCastError(Npc &npc, int32_t plM, int32_t itM)
void initDialogs()
uint32_t lockPickId() const
void printNothingToGet()
int invokeMana(Npc &npc, Npc *target, int mana)
bool hasSymbolName(std::string_view fn)
bool isTalk(const Npc &pl)
size_t symbolsCount() const
void printCannotUseError(Npc &npc, int32_t atr, int32_t nValue)
std::string_view spellCastAnim(Npc &npc, Item &fn)
std::string_view menuMain() const
uint32_t messageTime(std::string_view id) const
void playerHotLameHeal(Npc &pl)
int playerHotKeyScreenMap(Npc &pl)
void loadPerc(Serialize &fin)
void loadQuests(Serialize &fin)
int invokeCond(Npc &npc, std::string_view func)
void exec(const DlgChoice &dlg, Npc &player, Npc &npc)
bool isFriendlyFire(const Npc &src, const Npc &dst) const
std::string_view messageByName(std::string_view id) const
void printMobAnotherIsUsing(Npc &npc)
const AiState & aiState(ScriptFn id)
void initializeInstanceItem(const std::shared_ptr< zenkit::IItem > &item, size_t instance)
void fixNpcPosition(Npc &npc, float angle0, float distBias)
void useInteractive(const std::shared_ptr< zenkit::INpc > &hnpc, std::string_view func)
void loadVar(Serialize &fin)
int npcDamDiveTime()
void onWldItemRemoved(const Item &itm)
void invokeState(const std::shared_ptr< zenkit::INpc > &hnpc, const std::shared_ptr< zenkit::INpc > &hother, const char *name)
void removeItem(Item &it)
void printMobMissingItem(Npc &npc)
auto questLog() const -> const QuestLog &
zenkit::DaedalusSymbol * findSymbol(std::string_view s)
const zenkit::ISpell & spellDesc(int32_t splId)
std::string_view messageFromSvm(std::string_view id, int voice) const
AiOuputPipe * openAiOuput()
void tick(uint64_t dt)
void resetVarPointers()
uint64_t tickCount() const
void printMobMissingKey(Npc &npc)
int playerHotKeyScreenMap_G1(Npc &pl)
AiOuputPipe * openDlgOuput(Npc &player, Npc &npc)
void printMobTooFar(Npc &npc)
GameScript(GameSession &owner)
bool isUnconscious(const Npc &pl)
void setInstanceItem(Npc &holder, size_t itemId)
void saveQuests(Serialize &fout)
int32_t criticalDamageMultiplyer() const
auto updateDialog(const GameScript::DlgChoice &dlg, Npc &player, Npc &npc) -> std::vector< GameScript::DlgChoice >
ScriptFn playerPercAssessMagic()
void invokeSpell(Npc &npc, Npc *target, Item &fn)
void playerHotLamePotion(Npc &pl)
uint32_t rand(uint32_t max)
Attitude personAttitude(const Npc &p0, const Npc &p1) const
int invokeManaRelease(Npc &npc, Npc *target, int mana)
void printCannotBuyError(Npc &npc)
auto dialogChoices(std::shared_ptr< zenkit::INpc > self, std::shared_ptr< zenkit::INpc > npc, const std::vector< uint32_t > &except, bool includeImp) -> std::vector< DlgChoice >
const VisualFx * spellVfx(int32_t splId)
bool isAttack(const Npc &pl) const
const World & world() const
auto canNpcCollideWithSpell(Npc &npc, Npc *shooter, int32_t spellId) -> CollideMask
void initializeInstanceNpc(const std::shared_ptr< zenkit::INpc > &npc, size_t instance)
void savePerc(Serialize &fout)
void printMobMissingLockpick(Npc &npc)
void invokeItem(Npc *npc, ScriptFn fn)
bool isDead(const Npc &pl)
void invokePickLock(Npc &npc, int bSuccess, int bBrokenOpen)
void saveVar(Serialize &fout)
void setInstanceNPC(std::string_view name, Npc &npc)
void eventPlayAni(Npc &npc, std::string_view ani)
BodyState schemeToBodystate(std::string_view sc)
uint64_t tickCount() const
Definition gamesession.h:59
void exitSession()
bool isNpcInDialog(const Npc &npc) const
bool isInDialog() const
auto version() const -> const VersionInfo &
AiOuputPipe * openDlgOuput(Npc &player, Npc &npc)
const World * world() const
Definition gamesession.h:42
gtime time() const
Definition gamesession.h:56
Tempest::Signal< void()> onSettingsChanged
Definition gothic.h:182
Tempest::Signal< void(std::string_view, int, int, int, const GthFont &)> onPrintScreen
Definition gothic.h:173
const World * world() const
Definition gothic.cpp:278
static Gothic & inst()
Definition gothic.cpp:249
zenkit::DaedalusScript loadScript(std::string_view datFile, const ScriptLang lang)
Definition gothic.cpp:720
auto version() const -> const VersionInfo &
Definition gothic.cpp:263
static int settingsGetI(std::string_view sec, std::string_view name)
Definition gothic.cpp:807
std::string_view defaultGameDatFile() const
Definition gothic.cpp:697
void setupVmCommonApi(zenkit::DaedalusVm &vm)
Definition gothic.cpp:953
void emitGlobalSound(std::string_view sfx)
Definition gothic.cpp:395
Tempest::Signal< void(std::string_view)> onPrint
Definition gothic.h:174
void setupCommonScriptClasses(zenkit::DaedalusScript &sc)
Definition gothic.cpp:761
auto loadVisualFx(std::string_view name) -> const VisualFx *
Definition gothic.cpp:383
Definition item.h:14
int32_t spellId() const
Definition item.cpp:258
bool isSpellOrRune() const
Definition item.cpp:224
size_t count() const
Definition item.cpp:270
const std::shared_ptr< zenkit::IItem > & handlePtr()
Definition item.h:85
size_t clsId() const
Definition item.cpp:313
bool isSpell() const
Definition item.cpp:228
const zenkit::IItem & handle() const
Definition item.h:83
@ NSLOT
Definition item.h:16
Tempest::Vec3 position() const
Definition item.cpp:180
static bool isClose(const Npc &npc, const Npc &p, float dist)
Definition movealgo.cpp:709
Definition npc.h:25
void setFatness(float f)
Definition npc.cpp:925
auto mapHeadBone() const -> Tempest::Vec3
Definition npc.cpp:3378
void setToFightMode(const size_t item)
Definition npc.cpp:3182
void setTalentSkill(Talent t, int32_t lvl)
Definition npc.cpp:1125
float qDistTo(const Tempest::Vec3 pos) const
auto weaponState() const -> WeaponState
Definition npc.cpp:3621
void stopAnim(std::string_view ani)
Definition npc.cpp:979
Attitude attitude() const
Definition npc.h:226
void addRoutine(gtime s, gtime e, uint32_t callback, std::string_view point)
Definition npc.cpp:4174
const std::shared_ptr< zenkit::INpc > & handlePtr() const
Definition npc.h:328
uint32_t guild() const
Definition npc.cpp:1223
void clearAiQueue()
Definition npc.cpp:4369
auto currentWayPoint() const -> const WayPoint *
Definition npc.h:370
auto playAnimByName(std::string_view name, BodyState bs) -> const Animation::Sequence *
Definition npc.cpp:937
bool isInState(ScriptFn stateFn) const
Definition npc.cpp:4153
bool canSeeNpc(const Npc &oth, bool freeLos) const
Definition npc.cpp:4403
void setActiveSpellInfo(int32_t info)
Definition npc.cpp:3883
Npc * lookAtTarget() const
Definition npc.cpp:690
bool wasInState(ScriptFn stateFn) const
Definition npc.cpp:4162
void setVisualBody(int32_t headTexNr, int32_t teethTexNr, int32_t bodyVer, int32_t bodyColor, std::string_view body, std::string_view head)
Definition npc.cpp:843
void aiPush(AiQueue::AiAction &&a)
Definition npc.cpp:3218
size_t itemCount(size_t id) const
Definition npc.cpp:3454
void setRefuseTalk(uint64_t milis)
Definition npc.cpp:1167
void setPerceptionDisable(PercType t)
Definition npc.cpp:4011
int32_t talentSkill(Talent t) const
Definition npc.cpp:1132
BodyState bodyState() const
Definition npc.cpp:3147
bool setPosition(float x, float y, float z)
Definition npc.cpp:388
void setPerceptionEnable(PercType t, size_t fn)
Definition npc.cpp:4006
int32_t talentValue(Talent t) const
Definition npc.cpp:1143
int32_t activeSpellLevel() const
Definition npc.cpp:3887
auto detectedMob() const -> Interactive *
Definition npc.cpp:4145
void setVisual(std::string_view visual)
Definition npc.cpp:743
auto inventory() const -> const Inventory &
Definition npc.h:330
Item * currentMeleeWeapon()
Definition npc.cpp:3362
bool isPlayer() const
Definition npc.cpp:539
void setTalentValue(Talent t, int32_t lvl)
Definition npc.cpp:1138
Item * activeWeapon()
Definition npc.cpp:3458
void setToFistMode()
Definition npc.cpp:3208
void startFaceAnim(std::string_view anim, float intensity, uint64_t duration)
Definition npc.cpp:983
auto position() const -> Tempest::Vec3
Definition npc.cpp:628
void excRoutine(size_t callback)
Definition npc.cpp:4191
void changeAttribute(Attribute a, int32_t val, bool allowUnconscious)
Definition npc.cpp:1177
void clearInventory()
Definition npc.cpp:3354
void useItem(size_t item)
Definition npc.cpp:3470
void setStateTime(int64_t time)
Definition npc.cpp:4170
zenkit::INpc & handle()
Definition npc.h:327
uint32_t instanceSymbol() const
Definition npc.cpp:1219
int32_t lastHitSpellId() const
Definition npc.h:390
void setAiOutputBarrier(uint64_t dt, bool overlay)
Definition npc.cpp:2940
bool isRefuseTalk() const
Definition npc.cpp:1155
Item * currentArmor()
Definition npc.cpp:3358
void setAttitude(Attitude att)
Definition npc.cpp:1274
void delItem(size_t id, uint32_t amount)
Definition npc.cpp:3466
auto processPolicy() const -> NpcProcessPolicy
Definition npc.h:109
void setTarget(Npc *t)
Definition npc.cpp:2907
bool isTargetableBySpell(TargetType t) const
Definition npc.cpp:2984
void setTempAttitude(Attitude att)
Definition npc.cpp:1284
Item * currentRangedWeapon()
Definition npc.cpp:3366
bool isInRoutine(ScriptFn stateFn) const
Definition npc.cpp:4157
void setScale(float x, float y, float z)
Definition npc.cpp:930
void addOverlay(std::string_view sk, uint64_t time)
Definition npc.cpp:758
Item * addItem(size_t id, size_t amount)
Definition npc.cpp:3233
uint64_t stateTime() const
Definition npc.cpp:4166
Item * getItem(size_t id)
Definition npc.cpp:3462
void setTrueGuild(int32_t g)
Definition npc.cpp:1240
bool canSeeItem(const Item &it, bool freeLos) const
Definition npc.cpp:4487
bool hasCollision() const
Definition npc.h:320
Npc * target() const
Definition npc.cpp:2916
void delOverlay(std::string_view sk)
Definition npc.cpp:769
void setOther(Npc *ot)
Definition npc.cpp:2924
bool hasStateFlag(BodyState flg) const
Definition npc.cpp:3174
void setPerceptionTime(uint64_t time)
Definition npc.cpp:3998
auto portalName() -> std::string_view
Definition npc.cpp:694
int32_t trueGuild() const
Definition npc.cpp:1244
void save(Serialize &fout)
Definition questlog.cpp:37
void setStatus(std::string_view name, Status s)
Definition questlog.cpp:17
void addEntry(std::string_view name, std::string_view entry)
Definition questlog.cpp:25
void load(Serialize &fin)
Definition questlog.cpp:45
Quest & add(std::string_view name, Section s)
Definition questlog.cpp:7
@ Mission
Definition questlog.h:21
static auto openReader(std::string_view name, std::unique_ptr< zenkit::Read > &read) -> std::unique_ptr< zenkit::ReadArchive >
static const Skeleton * loadSkeleton(std::string_view name)
static const GthFont & font(const float scale)
static bool hasFile(std::string_view fname)
static Tempest::Sound loadSoundBuffer(std::string_view name)
bool isValid() const
Definition gamescript.h:38
size_t ptr
Definition gamescript.h:36
void write(const Arg &... a)
Definition serialize.h:76
void read(Arg &... a)
Definition serialize.h:81
Definition sound.h:5
@ T_3D
Definition sound.h:9
std::string name
Definition waypoint.h:35
static const float talkRange
Definition worldsound.h:40
Definition world.h:31
void sendPassivePerc(Npc &self, Npc &other, int32_t perc)
Definition world.cpp:697
int32_t guildOfRoom(const Tempest::Vec3 &pos)
Definition world.cpp:1040
void addDlgSound(std::string_view s, const Tempest::Vec3 &pos, float range, uint64_t &timeLen)
Definition world.cpp:799
void runEffect(Effect &&e)
Definition world.cpp:234
void triggerEvent(const TriggerEvent &e)
Definition world.cpp:495
const WayPoint * findNextWayPoint(const Npc &npc) const
Definition world.cpp:941
DynamicWorld * physic() const
Definition world.h:81
Interactive * availableMob(const Npc &pl, std::string_view name)
Definition world.cpp:483
uint64_t tickCount() const
Definition world.cpp:387
Npc * addNpc(std::string_view name, std::string_view at)
Definition world.cpp:590
const WayPoint * findNextFreePoint(const Npc &pos, std::string_view name) const
Definition world.cpp:923
const WayPoint * findPoint(std::string_view name, bool inexact=true) const
Definition world.cpp:871
Item * itmById(uint32_t id)
Definition world.cpp:228
uint32_t npcId(const Npc *ptr) const
Definition world.cpp:200
Npc * findNpcByInstance(size_t instance, size_t n=0)
Definition world.cpp:307
void removeItem(Item &it)
Definition world.cpp:629
void detectItem(const Tempest::Vec3 &p, const float r, const std::function< void(Item &)> &f)
Definition world.cpp:977
Npc * npcById(uint32_t id)
Definition world.cpp:204
Item * addItem(size_t itemInstance, std::string_view at)
Definition world.cpp:609
void detectNpc(const Tempest::Vec3 &p, const float r, const std::function< void(Npc &)> &f)
Definition world.cpp:973
const WayPoint * findFreePoint(const Npc &pos, std::string_view name) const
Definition world.cpp:897
void setMobRoutine(gtime time, std::string_view scheme, int32_t state)
Definition world.cpp:558
const WayPoint * findWayPoint(std::string_view name) const
Definition world.cpp:875
void setDayTime(int32_t h, int32_t min)
Definition world.cpp:391
void assignRoomToGuild(std::string_view room, int32_t guildId)
Definition world.cpp:1027
void removeNpc(Npc &npc)
Definition world.cpp:605
Npc * player() const
Definition world.h:111
int64_t day() const
Definition gametime.h:17
int64_t hour() const
Definition gametime.h:20
int64_t minute() const
Definition gametime.h:21
static CommandLine * instance
@ GIL_PUBLIC
Definition constants.h:27
@ GIL_HUMAN
Definition constants.h:10
@ GIL_NONE
Definition constants.h:9
CollideMask
Definition constants.h:252
@ COLL_DOEVERYTHING
Definition constants.h:254
@ COLL_DONOTHING
Definition constants.h:253
Talent
Definition constants.h:435
WalkBit
Definition constants.h:209
NpcProcessPolicy
Definition constants.h:506
TargetType
Definition constants.h:288
Attitude
Definition constants.h:234
@ ATT_NEUTRAL
Definition constants.h:237
@ ATT_HOSTILE
Definition constants.h:235
@ ATT_NULL
Definition constants.h:239
@ ATT_FRIENDLY
Definition constants.h:238
@ MAX_AI_USE_DISTANCE
Definition constants.h:129
ItmFlags
Definition constants.h:312
@ ITM_CAT_MAGIC
Definition constants.h:323
@ ITM_CAT_NONE
Definition constants.h:313
@ ITM_CAT_NF
Definition constants.h:314
@ ITM_CAT_FOOD
Definition constants.h:318
@ ITM_CAT_FF
Definition constants.h:315
@ ITM_CAT_RUNE
Definition constants.h:322
@ ITM_CAT_DOCS
Definition constants.h:319
@ ITM_CAT_MUN
Definition constants.h:316
@ ITM_CAT_POTION
Definition constants.h:320
@ ITM_CAT_ARMOR
Definition constants.h:317
@ ITM_CAT_LIGHT
Definition constants.h:321
@ SPL_SENDSTOP
Definition constants.h:478
Attribute
Definition constants.h:462
BodyState
Definition constants.h:140
@ BS_SIT
Definition constants.h:165
@ BS_NONE
Definition constants.h:141
@ BS_CLIMB
Definition constants.h:163
@ BS_LIE
Definition constants.h:166
@ BS_MOBINTERACT
Definition constants.h:169
@ BS_MOBINTERACT_INTERRUPT
Definition constants.h:170
@ SPELL_GOOD
Definition constants.h:263
FightMode
Definition constants.h:201
ScriptLang
Definition constants.h:518
PercType
Definition constants.h:396
@ PERC_Count
Definition constants.h:432
bool exists(const std::u16string &path)
Definition fileutil.cpp:15
std::u16string caseInsensitiveSegment(std::u16string_view path, const char16_t *segment, Tempest::Dir::FileType type)
zenkit::IInfo * handle
Definition gamescript.h:55
int32_t range[PERC_Count]
Definition gamescript.h:60
int at(PercType perc, int r) const
NpcProcessPolicy prev
ScopeCtx(GameScript &script, NpcProcessPolicy pp)
GameScript & script
ScopeVar(const ScopeVar &)=delete
ScopeVar(zenkit::DaedalusSymbol &sym, const std::shared_ptr< T > &h)
std::shared_ptr< zenkit::DaedalusInstance > prev
zenkit::DaedalusSymbol & sym