ScriptManager.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /*
  2. * Copyright (C) 2022 The V-Gears Team
  3. *
  4. * This file is part of V-Gears
  5. *
  6. * V-Gears is free software: you can redistribute it and/or modify it under
  7. * terms of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3.0 (GPLv3) of the License.
  9. *
  10. * V-Gears is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include "core/ConfigVar.h"
  16. #include "core/DebugDraw.h"
  17. #include "core/Logger.h"
  18. #include "core/ScriptManager.h"
  19. #include "core/ScriptManagerBinds.h"
  20. #include "core/ScriptManagerCommands.h"
  21. #include "core/Timer.h"
  22. #include "core/Utilites.h"
  23. #include "core/XmlScriptsFile.h"
  24. #include "LuaIncludes.h"
  25. ConfigVar cv_debug_script(
  26. "debug_script", "Debug script flags. 0x01 - System, 0x02 - Entity, 0x04 - Ui.", "0"
  27. );
  28. /**
  29. * Script manager singleton.
  30. */
  31. template<>ScriptManager *Ogre::Singleton<ScriptManager>::msSingleton = nullptr;
  32. Ogre::String script_entity_type[] = {"SYSTEM", "ENTITY", "UI", "BATTLE"};
  33. /**
  34. * Compares the priority of two scripts.
  35. *
  36. * @param[in] a The first script to compare.
  37. * @param[in] b The second script to compare.
  38. * @return True if the script a has a lower priority number than b, false if a has a higher or
  39. * equal priority than b.
  40. */
  41. bool priority_queue_compare(QueueScript a, QueueScript b){return a.priority < b.priority;}
  42. ScriptManager::ScriptManager():
  43. system_table_name_("System"), entity_table_name_("EntityContainer"), ui_table_name_("UiContainer")
  44. {
  45. lua_state_ = lua_open();
  46. sol::register_main_thread(lua_state_);
  47. luaopen_base(lua_state_);
  48. luaopen_string(lua_state_);
  49. luaopen_table(lua_state_);
  50. luaopen_math(lua_state_);
  51. luaopen_io(lua_state_);
  52. InitBinds();
  53. InitCmd();
  54. // TODO: Use the one in data/ ?
  55. RunFile("system/system.lua");
  56. XmlScriptsFile scripts("./data/scripts/_scripts.xml");
  57. scripts.LoadScripts();
  58. }
  59. ScriptManager::~ScriptManager(){lua_close(lua_state_);}
  60. void ScriptManager::Input(const VGears::Event& event){
  61. if (
  62. event.event != ""
  63. && (event.type == VGears::ET_KEY_PRESS || event.type == VGears::ET_KEY_REPEAT_WAIT)
  64. ){
  65. Ogre::String argument2 = "";
  66. if (event.type == VGears::ET_KEY_PRESS) argument2 = "Press";
  67. else if (event.type == VGears::ET_KEY_REPEAT_WAIT) argument2 = "Repeat";
  68. const Ogre::String source_key = KeyToString(
  69. static_cast<OIS::KeyCode>(event.param1)
  70. );
  71. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  72. ScriptRequest(
  73. &script_entity_[i], "on_button", 100,
  74. event.event, argument2, false, false, source_key, true
  75. );
  76. }
  77. return;
  78. }
  79. if (
  80. (event.type == VGears::ET_KEY_PRESS || event.type == VGears::ET_KEY_REPEAT_WAIT)
  81. && (
  82. // Only the keys that scripts read by themselves reach them as raw keys: the letters and the
  83. // space bar, to type in the name menu and for the debug keys of the battle. Everything else
  84. // goes through the game events of config.cfg, so it can be remapped.
  85. // TODO: Do this the other way: block keys NOT TO pass to scripts.
  86. event.param1 == OIS::KC_SPACE
  87. || event.param1 == OIS::KC_A || event.param1 == OIS::KC_B || event.param1 == OIS::KC_C
  88. || event.param1 == OIS::KC_D || event.param1 == OIS::KC_E || event.param1 == OIS::KC_F
  89. || event.param1 == OIS::KC_G || event.param1 == OIS::KC_H || event.param1 == OIS::KC_I
  90. || event.param1 == OIS::KC_J || event.param1 == OIS::KC_K || event.param1 == OIS::KC_L
  91. || event.param1 == OIS::KC_M || event.param1 == OIS::KC_N || event.param1 == OIS::KC_O
  92. || event.param1 == OIS::KC_P || event.param1 == OIS::KC_Q || event.param1 == OIS::KC_R
  93. || event.param1 == OIS::KC_S || event.param1 == OIS::KC_T || event.param1 == OIS::KC_U
  94. || event.param1 == OIS::KC_V || event.param1 == OIS::KC_W || event.param1 == OIS::KC_X
  95. || event.param1 == OIS::KC_Y || event.param1 == OIS::KC_Z || event.param1 == OIS::KC_X
  96. )
  97. ){
  98. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  99. Ogre::String argument2 = "";
  100. if (event.type == VGears::ET_KEY_PRESS) argument2 = "Press";
  101. else if (event.type == VGears::ET_KEY_REPEAT_WAIT) argument2 = "Repeat";
  102. ScriptRequest(
  103. &script_entity_[i], "on_button", 100,
  104. KeyToString((OIS::KeyCode) ((int) event.param1)), argument2, false, false
  105. );
  106. }
  107. }
  108. }
  109. void ScriptManager::Update(const ScriptManager::Type type){
  110. // Resort all queue. This will give correct info for debug draw.
  111. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  112. if (script_entity_[i].type == type){
  113. if (script_entity_[i].queue.size() > 0){
  114. if (script_entity_[i].resort == true){
  115. std::stable_sort(
  116. script_entity_[i].queue.begin(),
  117. script_entity_[i].queue.end(), priority_queue_compare
  118. );
  119. script_entity_[i].resort = false;
  120. }
  121. }
  122. }
  123. }
  124. // Draw debug before update. This way it will be possible to see
  125. // scripts that run once
  126. int debug = cv_debug_script.GetI();
  127. if (debug != 0){
  128. DEBUG_DRAW.SetTextAlignment(DEBUG_DRAW.LEFT);
  129. DEBUG_DRAW.SetScreenSpace(true);
  130. int y = 34;
  131. for (unsigned int i = 0; i < 3; ++ i){
  132. if ((debug & (1 << i)) != 0){
  133. DEBUG_DRAW.SetColour(Ogre::ColourValue(0.8f, 0.8f, 0.0f, 1.0f));
  134. DEBUG_DRAW.Text(
  135. 10.0f, static_cast<float>(y), "Script \"" + script_entity_type[i] + "\" entity:"
  136. );
  137. y += 16;
  138. for (unsigned int j = 0; j < script_entity_.size(); ++ j){
  139. if (script_entity_[j].type == i){
  140. Ogre::String text = script_entity_[j].name;
  141. unsigned int queue_size = script_entity_[j].queue.size();
  142. if (queue_size > 0){
  143. text += ": ";
  144. DEBUG_DRAW.SetColour(Ogre::ColourValue(0.8f, 0.8f, 0.0f, 1.0f));
  145. }
  146. else DEBUG_DRAW.SetColour(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
  147. for (unsigned int k = 0; k < queue_size; ++ k){
  148. if (k > 0) text += ", ";
  149. text +=
  150. "("
  151. + Ogre::StringConverter::toString(script_entity_[j].queue[k].priority)
  152. + ")" + script_entity_[j].queue[k].function;
  153. if (script_entity_[j].queue[k].wait == true){
  154. if (script_entity_[j].queue[k].seconds_to_wait != 0){
  155. text +=
  156. ":wait("+ Ogre::StringConverter::toString(
  157. script_entity_[j].queue[k].seconds_to_wait
  158. ) + ")";
  159. }
  160. }
  161. }
  162. DEBUG_DRAW.Text(20.0f, static_cast<float>(y), text);
  163. y += 16;
  164. }
  165. }
  166. }
  167. }
  168. }
  169. // Before updating entities, call on_update on the system timer.
  170. RunString("Timer.update()");
  171. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  172. if (script_entity_[i].type == type){
  173. if (script_entity_[i].queue.size() > 0){
  174. current_script_id_.entity = script_entity_[i].name;
  175. current_script_id_.function = script_entity_[i].queue[0].function;
  176. if (script_entity_[i].queue[0].wait == false){
  177. if (script_entity_[i].queue[0].yield == false){
  178. LOG_TRIVIAL(
  179. "[SCRIPT] Start script \"" + current_script_id_.function
  180. + "\" for entity \"" + current_script_id_.entity + "\"."
  181. );
  182. for (const ScriptId& waiting : script_entity_[i].queue[0].paused_scripts_start)
  183. ContinueScriptExecution(waiting);
  184. script_entity_[i].queue[0].paused_scripts_start.clear();
  185. sol::object table = GetTableByEntityName(
  186. type, current_script_id_.entity,
  187. script_entity_[i].queue[0].state
  188. );
  189. sol::object function;
  190. if (table.get_type() == sol::type::table)
  191. function = table.as<sol::table>()[current_script_id_.function];
  192. if (
  193. table.get_type() == sol::type::table
  194. && function.get_type() == sol::type::function
  195. ){
  196. int ret = 0;
  197. lua_State* thread = script_entity_[i].queue[0].state;
  198. sol::coroutine coroutine(thread, sol::function(function));
  199. auto result = coroutine(
  200. table, script_entity_[i].queue[0].argument1,
  201. script_entity_[i].queue[0].argument2,
  202. script_entity_[i].queue[0].argument3
  203. );
  204. if (result.valid()){
  205. // The coroutine yields with the bound function
  206. // return value on top of the thread stack; a
  207. // completed script leaves its return value there.
  208. if (lua_gettop(thread) > 0 && lua_isnumber(thread, -1))
  209. ret = static_cast<int>(lua_tointeger(thread, -1));
  210. lua_settop(thread, 0);
  211. }
  212. else{
  213. sol::error error = result;
  214. std::string msg = "LUA error in entity " + script_entity_[i].name
  215. + " (" + current_script_id_.entity + ") " + " in function "
  216. + script_entity_[i].queue[0].function + " ("
  217. + current_script_id_.function + ")";
  218. if (lua_tostring(thread, -1) != nullptr){
  219. msg = msg + ". Details: " + std::string(
  220. lua_tostring(thread, -1)
  221. );
  222. }
  223. msg = msg + ". Exception: " + error.what();
  224. LOG_ERROR(msg);
  225. lua_settop(thread, 0);
  226. }
  227. if (ret == 0){
  228. LOG_TRIVIAL(
  229. "[SCRIPT] Script \"" + current_script_id_.function
  230. + "\" for entity \"" + current_script_id_.entity + "\" finished."
  231. );
  232. if (script_entity_[i].queue[0].function != "on_update")
  233. RemoveEntityTopScript(script_entity_[i]);
  234. }
  235. else if (ret == 1){
  236. LOG_TRIVIAL(
  237. "[SCRIPT] Script \"" + current_script_id_.function
  238. + "\" for entity \"" + current_script_id_.entity
  239. + "\" not paused and will be continued next cycle."
  240. );
  241. script_entity_[i].queue[0].yield = true;
  242. }
  243. else{
  244. LOG_TRIVIAL(
  245. "[SCRIPT] Script \"" + current_script_id_.function
  246. + "\" for entity \"" + current_script_id_.entity
  247. + "\" not finished yet."
  248. );
  249. script_entity_[i].queue[0].yield = true;
  250. script_entity_[i].queue[0].wait = true;
  251. }
  252. }
  253. else{
  254. LOG_WARNING(
  255. "Script \"" + current_script_id_.function + "\" for entity \""
  256. + current_script_id_.entity + "\" doesn't exist."
  257. );
  258. RemoveEntityTopScript(script_entity_[i]);
  259. }
  260. }
  261. else{
  262. LOG_TRIVIAL(
  263. "[SCRIPT] Continue function \"" + current_script_id_.function
  264. + "\" for entity \"" + current_script_id_.entity + "\"."
  265. );
  266. int ret = 0;
  267. lua_State* thread = script_entity_[i].queue[0].state;
  268. // sol.hpp bundles a compat53 shim: on LuaJIT this maps
  269. // to the native 2-argument lua_resume.
  270. int status = lua_resume(thread, nullptr, 0);
  271. if (status == LUA_YIELD || status == 0){
  272. if (lua_gettop(thread) > 0 && lua_isnumber(thread, -1))
  273. ret = static_cast<int>(lua_tointeger(thread, -1));
  274. lua_settop(thread, 0);
  275. }
  276. else{
  277. const char* error = lua_tostring(thread, -1);
  278. LOG_WARNING(
  279. Ogre::String(error != nullptr ? error : "unknown lua error")
  280. );
  281. lua_settop(thread, 0);
  282. }
  283. if (ret == 0){ // Finished.
  284. LOG_TRIVIAL(
  285. "[SCRIPT] Script \"" + current_script_id_.function
  286. + "\" for entity \"" + current_script_id_.entity + "\" finished."
  287. );
  288. // Stop yield for on_update.
  289. script_entity_[i].queue[0].yield = false;
  290. if (script_entity_[i].queue[0].function != "on_update")
  291. RemoveEntityTopScript(script_entity_[i]);
  292. }
  293. else if (ret == 1){
  294. LOG_TRIVIAL(
  295. "[SCRIPT] Script \"" + current_script_id_.function
  296. + "\" for entity \"" + current_script_id_.entity
  297. + "\" not paused and will be continued next cycle."
  298. );
  299. }
  300. else{
  301. LOG_TRIVIAL(
  302. "[SCRIPT] Script \"" + current_script_id_.function
  303. + "\" for entity \"" + current_script_id_.entity
  304. + "\" not finished yet."
  305. );
  306. script_entity_[i].queue[0].wait = true;
  307. }
  308. }
  309. }
  310. else if (script_entity_[i].queue[0].seconds_to_wait > 0){
  311. script_entity_[i].queue[0].seconds_to_wait
  312. -= Timer::getSingleton().GetGameTimeDelta();
  313. script_entity_[i].queue[0].seconds_to_wait
  314. = (script_entity_[i].queue[0].seconds_to_wait < 0)
  315. ? 0 : script_entity_[i].queue[0].seconds_to_wait;
  316. if (script_entity_[i].queue[0].seconds_to_wait == 0)
  317. script_entity_[i].queue[0].wait = false;
  318. }
  319. }
  320. }
  321. }
  322. }
  323. void ScriptManager::UpdateDebug(){}
  324. void ScriptManager::OnResize(){}
  325. void ScriptManager::ClearField(){}
  326. void ScriptManager::ClearBattle(){
  327. // Remove all battle script entities.
  328. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  329. if (script_entity_[i].type == BATTLE){
  330. while(script_entity_[i].queue.size() > 0)
  331. ScriptManager::RemoveEntityTopScript(script_entity_[i]);
  332. script_entity_.erase(script_entity_.begin() + i);
  333. i --;
  334. }
  335. }
  336. }
  337. void ScriptManager::ClearWorld(){}
  338. void ScriptManager::RunString(const Ogre::String& lua){
  339. int pre_eval_index = lua_gettop(lua_state_);
  340. if (luaL_dostring(lua_state_, lua.c_str()) == 1)
  341. LOG_ERROR("ScriptManager::RunString error: " + Ogre::String(lua_tostring(lua_state_, -1)));
  342. else{
  343. int index = lua_gettop(lua_state_);
  344. if (index > 0 && index != pre_eval_index){
  345. auto str = lua_tostring(lua_state_, index);
  346. LOG_CONSOLE(
  347. str != nullptr ? str : "ScriptManager::RunString: (nullptr; try tostring(...)?)"
  348. );
  349. lua_pop(lua_state_, 1);
  350. }
  351. }
  352. }
  353. void ScriptManager::RunFile(const Ogre::String& file){
  354. if (luaL_dofile(lua_state_, ("./data/" + file).c_str()) == 1)
  355. LOG_ERROR(Ogre::String(lua_tostring(lua_state_, -1)));
  356. }
  357. void ScriptManager::AddEntity(
  358. const ScriptManager::Type type, const Ogre::String& entity_name, Entity* entity
  359. ){
  360. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  361. if (script_entity_[i].type == type && script_entity_[i].name == entity_name){
  362. LOG_ERROR(
  363. "Script \"" + script_entity_type[type] + "\" entity \""
  364. + entity_name + "\" already exist in script manager."
  365. );
  366. return;
  367. }
  368. }
  369. sol::object table = GetTableByEntityName(type, entity_name, lua_state_);
  370. if (table.get_type() == sol::type::table){
  371. ScriptEntity script_entity;
  372. script_entity.name = entity_name;
  373. script_entity.type = type;
  374. // Initialize entity field for model entity.
  375. if (entity != nullptr) table.as<sol::table>()["entity"] = entity;
  376. // Check the "on_start" script
  377. sol::object on_start = table.as<sol::table>()["on_start"];
  378. if (on_start.get_type() == sol::type::function){
  379. QueueScript script;
  380. script.function = "on_start";
  381. script.priority = 1;
  382. script.state = lua_newthread(lua_state_);
  383. // The thread must not be garbage collected,
  384. // so it need to be stored.
  385. script.state_id = luaL_ref(lua_state_, LUA_REGISTRYINDEX);
  386. script.seconds_to_wait = 0;
  387. script.wait = false;
  388. script.yield = false;
  389. script_entity.queue.push_back(script);
  390. }
  391. // Check the "on_update" script.
  392. sol::object on_update = table.as<sol::table>()["on_update"];
  393. if (on_update.get_type() == sol::type::function){
  394. QueueScript script;
  395. script.function = "on_update";
  396. script.priority = 999;
  397. script.state = lua_newthread(lua_state_);
  398. // The thread must not be garbage collected,
  399. // so it need to be stored.
  400. script.state_id = luaL_ref(lua_state_, LUA_REGISTRYINDEX);
  401. script.seconds_to_wait = 0;
  402. script.wait = false;
  403. script.yield = false;
  404. script_entity.queue.push_back(script);
  405. }
  406. script_entity_.push_back(script_entity);
  407. }
  408. }
  409. void ScriptManager::RemoveEntity(const ScriptManager::Type type, const Ogre::String& entity_name){
  410. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  411. if (script_entity_[i].type == type && script_entity_[i].name == entity_name){
  412. while(script_entity_[i].queue.size() > 0)
  413. ScriptManager::RemoveEntityTopScript(script_entity_[i]);
  414. script_entity_.erase(script_entity_.begin() + i);
  415. return;
  416. }
  417. }
  418. }
  419. void ScriptManager::RemoveEntityTopScript(ScriptEntity& entity){
  420. if (entity.queue.size() > 0){
  421. // Delete the thread.
  422. luaL_unref(lua_state_, LUA_REGISTRYINDEX, entity.queue[0].state_id);
  423. for (const ScriptId& waiting : entity.queue[0].paused_scripts_end)
  424. ContinueScriptExecution(waiting);
  425. entity.queue[0].paused_scripts_end.clear();
  426. entity.queue.erase(entity.queue.begin());
  427. }
  428. }
  429. sol::object ScriptManager::GetTableByEntityName(
  430. const ScriptManager::Type type, const Ogre::String& name, lua_State* state
  431. ) const{
  432. // Get the real table by name
  433. Ogre::StringVector table_path = StringTokenise(name, ".");
  434. sol::state_view lua(state);
  435. sol::object table = lua.globals();
  436. switch(type){
  437. case ScriptManager::SYSTEM:
  438. table = table.as<sol::table>()[system_table_name_]; break;
  439. case ScriptManager::ENTITY:
  440. table = table.as<sol::table>()[entity_table_name_]; break;
  441. case ScriptManager::UI:
  442. table = table.as<sol::table>()[ui_table_name_]; break;
  443. case ScriptManager::BATTLE:
  444. table = table.as<sol::table>()[battle_table_name_]; break;
  445. }
  446. if (table.get_type() != sol::type::table) return sol::make_object(state, sol::nil);
  447. for (unsigned int i = 0; i < table_path.size(); ++ i){
  448. table = table.as<sol::table>()[table_path[i]];
  449. if (table.get_type() != sol::type::table) return sol::make_object(state, sol::nil);
  450. }
  451. return table;
  452. }
  453. QueueScript* ScriptManager::GetScriptByScriptId(const ScriptId& script) const{
  454. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  455. if (script.entity == script_entity_[i].name){
  456. for (unsigned int j = 0; j < script_entity_[i].queue.size(); ++ j){
  457. if (script.function == script_entity_[i].queue[j].function)
  458. return (QueueScript*) &(script_entity_[i].queue[j]);
  459. }
  460. return nullptr;
  461. }
  462. }
  463. return nullptr;
  464. }
  465. ScriptEntity* ScriptManager::GetScriptEntityByName(
  466. const Type type, const Ogre::String& entity_name
  467. ) const{
  468. for (unsigned int i = 0; i < script_entity_.size(); ++ i){
  469. if (script_entity_[i].type == type && script_entity_[i].name == entity_name)
  470. return (ScriptEntity*) &(script_entity_[i]);
  471. }
  472. return nullptr;
  473. }
  474. const ScriptId ScriptManager::GetCurrentScriptId() const{return current_script_id_;}
  475. void ScriptManager::ContinueScriptExecution(const ScriptId& script){
  476. QueueScript* script_pointer = GetScriptByScriptId(script);
  477. if (script_pointer == nullptr){
  478. LOG_ERROR(
  479. "There is no script \"" + script.function + "\" for entity \""
  480. + script.entity + "\". Can't continue script execution."
  481. );
  482. return;
  483. }
  484. script_pointer->wait = false;
  485. }
  486. int ScriptManager::ScriptWait(const float seconds){
  487. LOG_TRIVIAL(
  488. "script:wait: We set script wait for "
  489. + Ogre::StringConverter::toString(seconds) + " seconds."
  490. );
  491. QueueScript* script = GetScriptByScriptId(current_script_id_);
  492. if (script == nullptr){
  493. LOG_ERROR("script:wait: Currently no any script running.");
  494. return 1;
  495. }
  496. if (seconds == 0) return 1;
  497. script->seconds_to_wait = seconds;
  498. return -1;
  499. }
  500. void ScriptManager::ScriptRequest(
  501. const Type type, const char* entity, const char* function, const int priority
  502. ){
  503. LOG_TRIVIAL(
  504. "[SCRIPT] script:request: Request function \"" + Ogre::String(function) + "\" for entity \""
  505. + Ogre::String(entity) + "\" with priority " + Ogre::StringConverter::toString(priority) + "."
  506. );
  507. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  508. if (script_entity == nullptr){
  509. LOG_WARNING(
  510. "[SCRIPT] script:request: Entity \"" + Ogre::String(entity) + "\" doesn't exist."
  511. );
  512. return;
  513. }
  514. bool added = ScriptRequest(script_entity, function, priority, "", "", false, false);
  515. if (added == false){
  516. LOG_WARNING(
  517. "Script '" + Ogre::String(function) + "' for entity '"
  518. + Ogre::String(entity) + "' doesn't exist."
  519. );
  520. }
  521. }
  522. int ScriptManager::ScriptRequestStartSync(
  523. const Type type, const char* entity, const char* function, const int priority
  524. ){
  525. LOG_TRIVIAL(
  526. "[SCRIPT] script:request_start_sync: Request function \""
  527. + Ogre::String(function) + "\" for entity \"" + Ogre::String(entity)
  528. + "\" with priority " + Ogre::StringConverter::toString(priority) + "."
  529. );
  530. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  531. if (script_entity == nullptr){
  532. LOG_ERROR(
  533. "[SCRIPT] script:request_start_sync: Entity \"" + Ogre::String(entity)
  534. + "\" doesn't exist.");
  535. return 1;
  536. }
  537. // A script that already started can't be waited for to start: nothing would resume the caller.
  538. for (const QueueScript& queued : script_entity->queue){
  539. if (queued.function == function && queued.yield){
  540. LOG_TRIVIAL(
  541. "[SCRIPT] script:request_start_sync: Function \"" + Ogre::String(function)
  542. + "\" for entity \"" + Ogre::String(entity) + "\" already started."
  543. );
  544. return 1;
  545. }
  546. }
  547. bool added = ScriptRequest(script_entity, function, priority, "", "", true, false);
  548. if (added == false){
  549. LOG_WARNING(
  550. "Script '" + Ogre::String(function) + "' for entity '"
  551. + Ogre::String(entity) + "' doesn't exist."
  552. );
  553. return 1;
  554. }
  555. return -1;
  556. }
  557. int ScriptManager::ScriptRequestEndSync(
  558. const Type type, const char* entity, const char* function, const int priority
  559. ){
  560. LOG_TRIVIAL(
  561. "[SCRIPT] script:request_end_sync: Request function \""
  562. + Ogre::String(function) + "\" for entity \"" + Ogre::String(entity)
  563. + "\" with priority " + Ogre::StringConverter::toString(priority) + "."
  564. );
  565. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  566. if (script_entity == nullptr){
  567. LOG_ERROR(
  568. "[SCRIPT] script:request_end_sync: Entity \"" + Ogre::String(entity)
  569. + "\" doesn't exist."
  570. );
  571. return 1;
  572. }
  573. bool added = ScriptRequest(script_entity, function, priority, "", "", false, true);
  574. if (added == false){
  575. LOG_WARNING(
  576. "Script '" + Ogre::String(function) + "' for entity '"
  577. + Ogre::String(entity) + "' doesn't exist."
  578. );
  579. return 1;
  580. }
  581. return -1;
  582. }
  583. bool ScriptManager::ScriptRequest(
  584. ScriptEntity* script_entity, const Ogre::String& function, const int priority,
  585. const Ogre::String& argument1, const Ogre::String& argument2, bool start_sync,
  586. bool end_sync, const Ogre::String& argument3, bool allow_duplicate
  587. ){
  588. if (script_entity == nullptr) return false;
  589. sol::object table
  590. = GetTableByEntityName(script_entity->type, script_entity->name, lua_state_);
  591. sol::object table_function;
  592. if (table.get_type() == sol::type::table)
  593. table_function = table.as<sol::table>()[function];
  594. if (table_function.get_type() == sol::type::function){
  595. QueueScript script;
  596. script.function = function;
  597. script.argument1 = argument1;
  598. script.argument2 = argument2;
  599. script.argument3 = argument3;
  600. script.priority = priority;
  601. script.state = lua_newthread(lua_state_);
  602. // The thread must not be garbage collected,
  603. // so it need to be stored.
  604. script.state_id = luaL_ref(lua_state_, LUA_REGISTRYINDEX);
  605. script.seconds_to_wait = 0;
  606. script.wait = false;
  607. script.yield = false;
  608. if (start_sync == true) script.paused_scripts_start.push_back(GetCurrentScriptId());
  609. if (end_sync == true) script.paused_scripts_end.push_back(GetCurrentScriptId());
  610. // If the script is already queued or running, don't queue it again. Whoever waits for the
  611. // request must wait for the existing script, because nothing else would resume it.
  612. if (allow_duplicate == false){
  613. for (int i = 0; i < script_entity->queue.size(); i ++){
  614. if (script_entity->queue[i].function == function){
  615. script_entity->resort = true;
  616. QueueScript& existing = script_entity->queue[i];
  617. for (const ScriptId& waiting : script.paused_scripts_end)
  618. existing.paused_scripts_end.push_back(waiting);
  619. // A script that already started isn't waited for. See ScriptRequestStartSync.
  620. if (!existing.yield){
  621. for (const ScriptId& waiting : script.paused_scripts_start)
  622. existing.paused_scripts_start.push_back(waiting);
  623. }
  624. luaL_unref(lua_state_, LUA_REGISTRYINDEX, script.state_id);
  625. return true;
  626. }
  627. }
  628. }
  629. script_entity->queue.push_back(script);
  630. script_entity->resort = true;
  631. return true;
  632. }
  633. return false;
  634. }
  635. void ScriptManager::AddValueToStack(const float value){
  636. QueueScript* script = GetScriptByScriptId(current_script_id_);
  637. if (script != nullptr) lua_pushnumber(script->state, value);
  638. }
  639. void ScriptManager::UpdateField(){}
  640. void ScriptManager::UpdateBattle(){}
  641. void ScriptManager::UpdateWorld(){}