FieldModelInstruction.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. /*
  2. * V-Gears
  3. * Copyright (C) 2022 V-Gears Team
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program 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. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <iostream>
  19. #include <sstream>
  20. #include <stdexcept>
  21. #include <boost/format.hpp>
  22. #include "decompiler/field/instruction/FieldModelInstruction.h"
  23. #include "decompiler/field/FieldEngine.h"
  24. #include "decompiler/field/FieldCodeGenerator.h"
  25. #include "decompiler/field/FieldDisassembler.h"
  26. static bool TryParseFloat(const std::string& value, float& parsed_value){
  27. size_t parsed_length = 0;
  28. try{
  29. parsed_value = std::stof(value, &parsed_length);
  30. return parsed_length == value.size();
  31. }
  32. catch (const std::invalid_argument&){
  33. return false;
  34. }
  35. catch (const std::out_of_range&){
  36. return false;
  37. }
  38. }
  39. void FieldModelInstruction::ProcessInst(
  40. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  41. ){
  42. FieldEngine& eng = static_cast<FieldEngine&>(*engine);
  43. FunctionMetaData md(func.metadata);
  44. //std::cout << "Model opcode: " << opcode_ << "\n";
  45. switch (opcode_){
  46. case OPCODES::JOIN: ProcessJOIN(code_gen); break;
  47. case OPCODES::SPLIT: ProcessSPLIT(code_gen); break;
  48. case OPCODES::BLINK: code_gen->WriteTodo(md.GetEntityName(), "BLINK"); break;
  49. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::EYETX:
  50. code_gen->WriteTodo(md.GetEntityName(), "EYETX");
  51. break;
  52. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::TRNSP:
  53. code_gen->WriteTodo(md.GetEntityName(), "TRNSP");
  54. break;
  55. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::AMBNT:
  56. code_gen->WriteTodo(md.GetEntityName(), "AMBNT");
  57. break;
  58. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown03:
  59. code_gen->WriteTodo(md.GetEntityName(), "Unknown03");
  60. break;
  61. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown04:
  62. code_gen->WriteTodo(md.GetEntityName(), "Unknown04");
  63. break;
  64. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown05:
  65. code_gen->WriteTodo(md.GetEntityName(), "Unknown05");
  66. break;
  67. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::LIGHT:
  68. code_gen->WriteTodo(md.GetEntityName(), "LIGHT");
  69. break;
  70. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown07:
  71. code_gen->WriteTodo(md.GetEntityName(), "Unknown07");
  72. break;
  73. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown08:
  74. code_gen->WriteTodo(md.GetEntityName(), "Unknown08");
  75. break;
  76. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown09:
  77. code_gen->WriteTodo(md.GetEntityName(), "Unknown09");
  78. break;
  79. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::SBOBJ:
  80. code_gen->WriteTodo(md.GetEntityName(), "SBOBJ");
  81. break;
  82. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown0B:
  83. code_gen->WriteTodo(md.GetEntityName(), "Unknown0B");
  84. break;
  85. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::Unknown0C:
  86. code_gen->WriteTodo(md.GetEntityName(), "Unknown0C");
  87. break;
  88. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::SHINE:
  89. code_gen->WriteTodo(md.GetEntityName(), "SHINE");
  90. break;
  91. case (OPCODES::KAWAI << 8) | OPCODES_KAWAI::RESET:
  92. code_gen->WriteTodo(md.GetEntityName(), "RESET");
  93. break;
  94. case OPCODES::KAWIW: code_gen->WriteTodo(md.GetEntityName(), "KAWIW"); break;
  95. case OPCODES::PMOVA: code_gen->WriteTodo(md.GetEntityName(), "PMOVA"); break;
  96. case OPCODES::PDIRA: code_gen->WriteTodo(md.GetEntityName(), "PDIRA"); break;
  97. case OPCODES::PTURA: code_gen->WriteTodo(md.GetEntityName(), "PTURA"); break;
  98. case OPCODES::PGTDR: code_gen->WriteTodo(md.GetEntityName(), "PGTDR"); break;
  99. case OPCODES::PXYZI: code_gen->WriteTodo(md.GetEntityName(), "PXYZI"); break;
  100. case OPCODES::TLKON: ProcessTLKON(code_gen, md.GetEntityName()); break;
  101. case OPCODES::PC: ProcessPC(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  102. case OPCODES::opCodeCHAR: ProcessCHAR(code_gen, md.GetEntityName()); break;
  103. case OPCODES::DFANM: ProcessDFANM(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  104. case OPCODES::ANIME1:
  105. ProcessANIME1(code_gen, md.GetEntityName(), md.GetCharacterId());
  106. break;
  107. case OPCODES::VISI: ProcessVISI(code_gen, md.GetEntityName()); break;
  108. case OPCODES::XYZI: ProcessXYZI(code_gen, md.GetEntityName()); break;
  109. case OPCODES::XYI: code_gen->WriteTodo(md.GetEntityName(), "XYI"); break;
  110. case OPCODES::XYZ: code_gen->WriteTodo(md.GetEntityName(), "XYZ"); break;
  111. case OPCODES::MOVE: ProcessMOVE(code_gen, md.GetEntityName()); break;
  112. case OPCODES::CMOVE: code_gen->WriteTodo(md.GetEntityName(), "CMOVE"); break;
  113. case OPCODES::MOVA: ProcessMOVA(code_gen, md.GetEntityName(), eng); break;
  114. case OPCODES::TURA: ProcessTURA(code_gen, md.GetEntityName(), eng); break;
  115. case OPCODES::ANIMW: ProcessANIMW(code_gen, md.GetEntityName()); break;
  116. case OPCODES::FMOVE: ProcessFMOVE(code_gen, md.GetEntityName()); break;
  117. case OPCODES::ANIME2:
  118. ProcessANIME2(code_gen, md.GetEntityName(), md.GetCharacterId());
  119. break;
  120. case OPCODES::ANIM_1:
  121. ProcessANIM_1(code_gen, md.GetEntityName(), md.GetCharacterId());
  122. break;
  123. case OPCODES::CANIM1:
  124. ProcessCANIM1(code_gen, md.GetEntityName(), md.GetCharacterId());
  125. break;
  126. case OPCODES::CANM_1:
  127. ProcessCANM_1(code_gen, md.GetEntityName(), md.GetCharacterId());
  128. break;
  129. case OPCODES::MSPED: ProcessMSPED(code_gen, md.GetEntityName()); break;
  130. case OPCODES::DIR: ProcessDIR(code_gen, md.GetEntityName()); break;
  131. case OPCODES::TURNGEN: ProcessTURNGEN(code_gen, md.GetEntityName()); break;
  132. case OPCODES::TURN: code_gen->WriteTodo(md.GetEntityName(), "TURN"); break;
  133. case OPCODES::DIRA: ProcessDIRA(code_gen, md.GetEntityName(), eng); break;
  134. case OPCODES::GETDIR: code_gen->WriteTodo(md.GetEntityName(), "GETDIR"); break;
  135. case OPCODES::GETAXY: code_gen->WriteTodo(md.GetEntityName(), "GETAXY"); break;
  136. case OPCODES::GETAI: ProcessGETAI(code_gen, eng); break;
  137. case OPCODES::ANIM_2:
  138. ProcessANIM_2(code_gen, md.GetEntityName(), md.GetCharacterId());
  139. break;
  140. case OPCODES::CANIM2:
  141. ProcessCANIM2(code_gen, md.GetEntityName(), md.GetCharacterId());
  142. break;
  143. case OPCODES::CANM_2:
  144. ProcessCANM_2(code_gen, md.GetEntityName(), md.GetCharacterId());
  145. break;
  146. case OPCODES::ASPED: ProcessASPED(code_gen, md.GetEntityName()); break;
  147. case OPCODES::CC: ProcessCC(code_gen, eng); break;
  148. case OPCODES::JUMP: ProcessJUMP(code_gen, md.GetEntityName()); break;
  149. case OPCODES::AXYZI: ProcessAXYZI(code_gen); break;
  150. case OPCODES::LADER: ProcessLADER(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  151. case OPCODES::OFST: ProcessOFST(code_gen, md.GetEntityName()); break;
  152. case OPCODES::OFSTW:
  153. code_gen->AddOutputLine("self." + md.GetEntityName() + ":offset_sync()");
  154. break;
  155. case OPCODES::TALKR: ProcessTALKR(code_gen, md.GetEntityName()); break;
  156. case OPCODES::SLIDR: ProcessSLIDR(code_gen, md.GetEntityName()); break;
  157. case OPCODES::SOLID: ProcessSOLID(code_gen, md.GetEntityName()); break;
  158. case OPCODES::TLKR2: ProcessTALKR(code_gen, md.GetEntityName()); break;
  159. case OPCODES::SLDR2: code_gen->WriteTodo(md.GetEntityName(), "SLDR2"); break;
  160. case OPCODES::CCANM: code_gen->WriteTodo(md.GetEntityName(), "CCANM"); break;
  161. case OPCODES::FCFIX: code_gen->WriteTodo(md.GetEntityName(), "FCFIX"); break;
  162. case OPCODES::ANIMB: code_gen->WriteTodo(md.GetEntityName(), "ANIMB"); break;
  163. case OPCODES::TURNW: code_gen->WriteTodo(md.GetEntityName(), "TURNW"); break;
  164. default:
  165. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  166. md.GetEntityName(), address_, opcode_
  167. ));
  168. }
  169. }
  170. void FieldModelInstruction::ProcessJOIN(CodeGenerator* code_gen){
  171. code_gen->AddOutputLine("join_party(" + std::to_string(params_[0]->GetUnsigned()) + ")");
  172. }
  173. void FieldModelInstruction::ProcessSPLIT(CodeGenerator* code_gen){
  174. const std::vector<ValuePtr> parameters = GetParams();
  175. if (parameters.size() != 13){
  176. code_gen->WriteTodo("FieldModelInstruction", "SPLIT with malformed parameters");
  177. return;
  178. }
  179. for (const ValuePtr& parameter : parameters){
  180. if (parameter == nullptr || !parameter->IsInteger()){
  181. code_gen->WriteTodo("FieldModelInstruction", "SPLIT with invalid parameter");
  182. return;
  183. }
  184. }
  185. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  186. const float scale = 128.0f * cg->GetScaleFactor();
  187. const auto& ax = FieldCodeGenerator::FormatValueOrVariable(
  188. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[6]->GetSigned(),
  189. FieldCodeGenerator::ValueType::Float, scale
  190. );
  191. const auto& ay = FieldCodeGenerator::FormatValueOrVariable(
  192. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[7]->GetSigned(),
  193. FieldCodeGenerator::ValueType::Float, scale
  194. );
  195. const auto& ar = FieldCodeGenerator::FormatValueOrVariable(
  196. cg->GetFormatter(), params_[2]->GetUnsigned(), params_[8]->GetSigned(),
  197. FieldCodeGenerator::ValueType::Float, scale
  198. );
  199. const auto& bx = FieldCodeGenerator::FormatValueOrVariable(
  200. cg->GetFormatter(), params_[3]->GetUnsigned(), params_[9]->GetSigned(),
  201. FieldCodeGenerator::ValueType::Float, scale
  202. );
  203. const auto& by = FieldCodeGenerator::FormatValueOrVariable(
  204. cg->GetFormatter(), params_[4]->GetUnsigned(), params_[10]->GetSigned(),
  205. FieldCodeGenerator::ValueType::Float, scale
  206. );
  207. const auto& br = FieldCodeGenerator::FormatValueOrVariable(
  208. cg->GetFormatter(), params_[5]->GetUnsigned(), params_[11]->GetSigned(),
  209. FieldCodeGenerator::ValueType::Float, scale
  210. );
  211. const auto& speed = params_[12]->GetUnsigned();
  212. code_gen->AddOutputLine((
  213. boost::format("split_party(%1%, %2%, %3%, %4%, %5%, %6%, %7%)")
  214. % ax % ay % ar % bx % by % br % speed
  215. ).str());
  216. }
  217. void FieldModelInstruction::ProcessTLKON(CodeGenerator* code_gen, const std::string& entity){
  218. code_gen->AddOutputLine((
  219. boost::format("self.%1%:set_talkable(%2%)")
  220. % entity % FieldCodeGenerator::FormatInvertedBool(params_[0]->GetUnsigned())
  221. ).str());
  222. }
  223. void FieldModelInstruction::ProcessPC(
  224. CodeGenerator* code_gen, const std::string& entity, int char_id
  225. ){
  226. code_gen->AddOutputLine(
  227. (boost::format("set_entity_to_character(\"%1%\", %2%)") % entity % char_id).str()
  228. );
  229. }
  230. void FieldModelInstruction::ProcessCHAR(CodeGenerator* code_gen, const std::string& entity){
  231. code_gen->AddOutputLine(
  232. (boost::format("self.%1% = entity_manager:get_entity(\"%1%\")") % entity).str()
  233. );
  234. // A model that has no animation playing is not drawn. The ones with no "Idle" animation need
  235. // a default one, which a DFANM later in the script replaces.
  236. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  237. const std::string animation = cg->GetFormatter().GetDefaultAnimationName(params_[0]->GetSigned());
  238. // The walk and run animations are the second and the third of the model.
  239. const std::string walk = cg->GetFormatter().GetMoveAnimationName(params_[0]->GetSigned(), 1);
  240. const std::string run = cg->GetFormatter().GetMoveAnimationName(params_[0]->GetSigned(), 2);
  241. if (!walk.empty() && !run.empty() && (walk != "Walk" || run != "Run")){
  242. code_gen->AddOutputLine((
  243. boost::format("self.%1%:set_move_animations(\"%2%\", \"%3%\")") % entity % walk % run
  244. ).str());
  245. }
  246. if (!animation.empty()){
  247. code_gen->AddOutputLine((
  248. boost::format("self.%1%:set_default_animation(\"%2%\") -- No Idle animation")
  249. % entity % animation
  250. ).str());
  251. code_gen->AddOutputLine(
  252. (boost::format("self.%1%:play_animation(\"%2%\")") % entity % animation).str()
  253. );
  254. }
  255. }
  256. void FieldModelInstruction::ProcessDFANM(
  257. CodeGenerator* code_gen, const std::string& entity, int char_id
  258. ){
  259. // ID will be fixed-up downstream.
  260. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  261. auto animation_id = params_[0]->GetUnsigned();
  262. // TODO: check for zero.
  263. auto speed = 1.0f / params_[1]->GetUnsigned();
  264. code_gen->AddOutputLine((
  265. boost::format("self.%1%:set_default_animation(\"%2%\") -- speed %3%")
  266. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id) % speed
  267. ).str());
  268. code_gen->AddOutputLine((
  269. boost::format("self.%1%:play_animation(\"%2%\")")
  270. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id)
  271. ).str());
  272. }
  273. void FieldModelInstruction::ProcessANIME1(
  274. CodeGenerator* code_gen, const std::string& entity, int char_id
  275. ){
  276. // ID will be fixed-up downstream.
  277. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  278. auto animation_id = params_[0]->GetUnsigned();
  279. // TODO: check for zero.
  280. auto speed = 1.0f / params_[1]->GetUnsigned();
  281. code_gen->AddOutputLine((
  282. boost::format("self.%1%:play_animation(\"%2%\") -- speed %3%")
  283. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id) % speed
  284. ).str());
  285. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  286. }
  287. void FieldModelInstruction::ProcessMOVA(
  288. CodeGenerator* code_gen, const std::string& entity, const FieldEngine& engine
  289. ){
  290. // Walks to the other entity, and waits until it is there.
  291. const auto& target = engine.EntityByIndex(params_[0]->GetUnsigned());
  292. code_gen->AddOutputLine((
  293. boost::format("self.%1%:move_to_entity(entity_manager:get_entity(\"%2%\"))")
  294. % entity % target.GetName()
  295. ).str());
  296. code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str());
  297. }
  298. void FieldModelInstruction::ProcessTALKR(CodeGenerator* code_gen, const std::string& entity){
  299. // The range is given in the same units as the positions.
  300. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  301. const float scale = 128.0f * cg->GetScaleFactor();
  302. const std::string range_value = FieldCodeGenerator::FormatValueOrVariable(
  303. cg->GetFormatter(), params_[0]->GetUnsigned() + params_[1]->GetUnsigned(),
  304. params_[2]->GetUnsigned(), FieldCodeGenerator::ValueType::Float, scale
  305. );
  306. float range = 0;
  307. if (TryParseFloat(range_value, range)){
  308. code_gen->AddOutputLine(
  309. (boost::format("self.%1%:set_talk_radius(%2%)") % entity % range).str()
  310. );
  311. }
  312. else{
  313. // A variable: the scale is not applied yet.
  314. code_gen->AddOutputLine(
  315. "self." + entity + ":set_talk_radius((" + range_value + ") / " + std::to_string(scale) + ")"
  316. );
  317. }
  318. }
  319. void FieldModelInstruction::ProcessDIRA(
  320. CodeGenerator* code_gen, const std::string& entity, const FieldEngine& engine
  321. ){
  322. // Turns the model towards another entity at once.
  323. const auto& target = engine.EntityByIndex(params_[0]->GetUnsigned());
  324. code_gen->AddOutputLine((
  325. boost::format("self.%1%:face_entity(entity_manager:get_entity(\"%2%\"))")
  326. % entity % target.GetName()
  327. ).str());
  328. }
  329. void FieldModelInstruction::ProcessASPED(CodeGenerator* code_gen, const std::string& entity){
  330. // The value is the speed of the animations: 16 is the normal one, 32 double and 8 half.
  331. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  332. const std::string speed_value = FieldCodeGenerator::FormatValueOrVariable(
  333. cg->GetFormatter(), params_[0]->GetUnsigned() + params_[1]->GetUnsigned(),
  334. params_[2]->GetUnsigned()
  335. );
  336. float speed = 0;
  337. if (TryParseFloat(speed_value, speed)){
  338. code_gen->AddOutputLine((
  339. boost::format("self.%1%:set_animation_speed(%2%)") % entity % (speed / 16.0f)
  340. ).str());
  341. }
  342. else{
  343. code_gen->AddOutputLine(
  344. "self." + entity + ":set_animation_speed((" + speed_value + ") / 16.0)"
  345. );
  346. }
  347. }
  348. void FieldModelInstruction::ProcessANIME2(
  349. CodeGenerator* code_gen, const std::string& entity, int char_id
  350. ){
  351. // Like ANIME1, but the script doesn't wait for the animation to end.
  352. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  353. auto animation_id = params_[0]->GetUnsigned();
  354. auto speed = 1.0f / params_[1]->GetUnsigned();
  355. code_gen->AddOutputLine((
  356. boost::format("self.%1%:play_animation(\"%2%\") -- speed %3%")
  357. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id) % speed
  358. ).str());
  359. }
  360. void FieldModelInstruction::ProcessANIM_1(
  361. CodeGenerator* code_gen, const std::string& entity, int char_id
  362. ){
  363. // Like ANIM_2, but the script doesn't wait for the animation to end.
  364. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  365. auto animation_id = params_[0]->GetUnsigned();
  366. auto speed = 1.0f / params_[1]->GetUnsigned();
  367. code_gen->AddOutputLine((
  368. boost::format("self.%1%:play_animation_stop(\"%2%\") -- speed %3%")
  369. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id) % speed
  370. ).str());
  371. }
  372. void FieldModelInstruction::ProcessCANIM1(
  373. CodeGenerator* code_gen, const std::string& entity, int char_id
  374. ){
  375. // Like CANIM2, but the script doesn't wait for the animation to end.
  376. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  377. auto animation_id = params_[0]->GetUnsigned();
  378. auto start_frame = params_[1]->GetUnsigned() / 30.0f;
  379. auto end_frame = params_[2]->GetUnsigned() / 30.0f;
  380. auto speed = 1.0f / params_[3]->GetUnsigned();
  381. code_gen->AddOutputLine((
  382. boost::format("self.%1%:play_animation(\"%2%\", %3%, %4%) -- speed %5%")
  383. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id)
  384. % start_frame % end_frame % speed
  385. ).str());
  386. }
  387. void FieldModelInstruction::ProcessCANM_1(
  388. CodeGenerator* code_gen, const std::string& entity, int char_id
  389. ){
  390. // Like CANM_2, but the script doesn't wait for the animation to end.
  391. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  392. auto animation_id = params_[0]->GetUnsigned();
  393. auto start_frame = params_[1]->GetUnsigned() / 30.0f;
  394. auto end_frame = params_[2]->GetUnsigned() / 30.0f;
  395. auto speed = 1.0f / params_[3]->GetUnsigned();
  396. code_gen->AddOutputLine((
  397. boost::format("self.%1%:play_animation_stop(\"%2%\", %3%, %4%) -- speed %5%")
  398. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id)
  399. % start_frame % end_frame % speed
  400. ).str());
  401. }
  402. void FieldModelInstruction::ProcessANIMW(CodeGenerator* code_gen, const std::string& entity){
  403. // Waits for the animation that was started with one of the non-waiting opcodes.
  404. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  405. }
  406. void FieldModelInstruction::ProcessVISI(CodeGenerator* code_gen, const std::string& entity){
  407. code_gen->AddOutputLine((
  408. boost::format("self.%1%:set_visible(%2%)")
  409. % entity % FieldCodeGenerator::FormatBool(params_[0]->GetUnsigned())
  410. ).str());
  411. }
  412. void FieldModelInstruction::ProcessXYZI(CodeGenerator* code_gen, const std::string& entity){
  413. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  414. const float scale = 128.0f * cg->GetScaleFactor();
  415. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  416. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[4]->GetSigned(),
  417. FieldCodeGenerator::ValueType::Float, scale
  418. );
  419. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  420. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[5]->GetSigned(),
  421. FieldCodeGenerator::ValueType::Float, scale
  422. );
  423. const auto& z = FieldCodeGenerator::FormatValueOrVariable(
  424. cg->GetFormatter(), params_[2]->GetUnsigned(), params_[6]->GetSigned(),
  425. FieldCodeGenerator::ValueType::Float, scale
  426. );
  427. const auto& triangle_id = FieldCodeGenerator::FormatValueOrVariable(
  428. cg->GetFormatter(), params_[3]->GetUnsigned(), params_[7]->GetUnsigned()
  429. );
  430. code_gen->AddOutputLine((
  431. boost::format("self.%1%:set_position(%2%, %3%, %4%) -- triangle ID %5%")
  432. % entity % x % y % z % triangle_id
  433. ).str());
  434. }
  435. void FieldModelInstruction::ProcessMOVE(CodeGenerator* code_gen, const std::string& entity){
  436. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  437. const float scale = 128.0f * cg->GetScaleFactor();
  438. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  439. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[2]->GetSigned(),
  440. FieldCodeGenerator::ValueType::Float, scale
  441. );
  442. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  443. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[3]->GetSigned(),
  444. FieldCodeGenerator::ValueType::Float, scale
  445. );
  446. code_gen->AddOutputLine(
  447. (boost::format("self.%1%:move_to_position(%2%, %3%)") % entity % x % y).str()
  448. );
  449. code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str());
  450. }
  451. void FieldModelInstruction::ProcessFMOVE(CodeGenerator* code_gen, const std::string& entity){
  452. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  453. const float scale = 128.0f * cg->GetScaleFactor();
  454. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  455. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[2]->GetSigned(),
  456. FieldCodeGenerator::ValueType::Float, scale
  457. );
  458. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  459. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[3]->GetSigned(),
  460. FieldCodeGenerator::ValueType::Float, scale
  461. );
  462. // Like MOVE, but the animation is the one the script chose, not the walking one.
  463. code_gen->AddOutputLine((boost::format("self.%1%:move_auto_animation(false)") % entity).str());
  464. code_gen->AddOutputLine(
  465. (boost::format("self.%1%:move_to_position(%2%, %3%)") % entity % x % y).str()
  466. );
  467. code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str());
  468. code_gen->AddOutputLine((boost::format("self.%1%:move_auto_animation(true)") % entity).str());
  469. }
  470. void FieldModelInstruction::ProcessTURA(
  471. CodeGenerator* code_gen, const std::string& entity, const FieldEngine& engine
  472. ){
  473. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  474. const auto& dest_entity = engine.EntityByIndex(params_[0]->GetSigned());
  475. std::string direction;
  476. switch (params_[1]->GetUnsigned()){
  477. case 0: direction = "Entity.CLOCKWISE"; break;
  478. case 1: direction = "Entity.ANTICLOCKWISE"; break;
  479. case 2: direction = "Entity.CLOSEST"; break;
  480. // Default to closest:
  481. default: direction = "Entity.CLOSEST"; break;
  482. }
  483. auto steps = params_[2]->GetUnsigned();
  484. const float scaled_steps = static_cast<float>(steps) / 30.0f;
  485. code_gen->AddOutputLine((
  486. boost::format("self.%1%:turn_to_entity(entity_manager:get_entity(\"%2%\"), %3%, %4%)")
  487. % entity % dest_entity.GetName() % direction % scaled_steps
  488. ).str());
  489. code_gen->AddOutputLine((boost::format("self.%1%:turn_sync()") % entity).str());
  490. }
  491. void FieldModelInstruction::ProcessMSPED(CodeGenerator* code_gen, const std::string& entity){
  492. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  493. const float scale = 128.0f * cg->GetScaleFactor();
  494. const auto& speed = FieldCodeGenerator::FormatValueOrVariable(
  495. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[2]->GetUnsigned(),
  496. FieldCodeGenerator::ValueType::Float, 256.0f * scale / 30.0f
  497. );
  498. code_gen->AddOutputLine(
  499. (boost::format("self.%1%:set_move_auto_speed(%2%)") % entity % speed).str()
  500. );
  501. }
  502. void FieldModelInstruction::ProcessDIR(CodeGenerator* code_gen, const std::string& entity){
  503. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  504. const auto& degrees = FieldCodeGenerator::FormatValueOrVariable(
  505. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[1]->GetUnsigned(),
  506. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  507. );
  508. code_gen->AddOutputLine((boost::format("self.%1%:set_rotation(%2%)") % entity % degrees).str());
  509. }
  510. void FieldModelInstruction::ProcessTURNGEN(CodeGenerator* code_gen, const std::string& entity){
  511. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  512. const auto& degrees = FieldCodeGenerator::FormatValueOrVariable(
  513. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[2]->GetUnsigned(),
  514. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  515. );
  516. std::string direction;
  517. switch (params_[3]->GetUnsigned()){
  518. case 0: direction = "Entity.CLOCKWISE"; break;
  519. case 1: direction = "Entity.ANTICLOCKWISE"; break;
  520. case 2: direction = "Entity.CLOSEST"; break;
  521. // Default to closest:
  522. default: direction = "Entity.CLOSEST"; break;
  523. }
  524. auto steps = params_[4]->GetUnsigned();
  525. std::string step_type;
  526. switch (params_[5]->GetUnsigned()){
  527. case 1: step_type = "Entity.LINEAR"; break;
  528. case 2: step_type = "Entity.SMOOTH"; break;
  529. // Default to smooth
  530. default: step_type = "Entity.SMOOTH"; break;
  531. }
  532. const float scaled_steps = static_cast<float>(steps) / 30.0f;
  533. code_gen->AddOutputLine((
  534. boost::format("self.%1%:turn_to_direction(%2%, %3%, %4%, %5%)")
  535. % entity % degrees % direction % step_type % scaled_steps
  536. ).str());
  537. code_gen->AddOutputLine((boost::format("self.%1%:turn_sync()") % entity).str());
  538. }
  539. void FieldModelInstruction::ProcessGETAI(CodeGenerator* code_gen, const FieldEngine& engine){
  540. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  541. // TODO: check for assignment to literal.
  542. const auto& variable = FieldCodeGenerator::FormatValueOrVariable(
  543. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[3]->GetUnsigned()
  544. );
  545. const auto& entity = engine.EntityByIndex(params_[2]->GetUnsigned());
  546. code_gen->AddOutputLine(
  547. (boost::format("%1% = entity_manager:get_entity(\"%2%\"):get_move_triangle_id()")
  548. % variable % entity.GetName()
  549. ).str());
  550. }
  551. void FieldModelInstruction::ProcessANIM_2(
  552. CodeGenerator* code_gen, const std::string& entity, int char_id
  553. ){
  554. // ID will be fixed-up downstream.
  555. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  556. auto animation_id = params_[0]->GetUnsigned();
  557. // TODO: check for zero.
  558. auto speed = 1.0f / params_[1]->GetUnsigned();
  559. code_gen->AddOutputLine((
  560. boost::format("self.%1%:play_animation_stop(\"%2%\") -- speed %3%")
  561. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id) % speed
  562. ).str());
  563. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  564. }
  565. void FieldModelInstruction::ProcessCANIM2(
  566. CodeGenerator* code_gen, const std::string& entity, int char_id
  567. ){
  568. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  569. auto animation_id = params_[0]->GetUnsigned();
  570. auto start_frame = params_[1]->GetUnsigned() / 30.0f;
  571. auto end_frame = params_[2]->GetUnsigned() / 30.0f;
  572. // TODO: check for zero.
  573. auto speed = 1.0f / params_[3]->GetUnsigned();
  574. code_gen->AddOutputLine((
  575. boost::format("self.%1%:play_animation(\"%2%\", %3%, %4%) -- speed %5%")
  576. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id)
  577. % start_frame % end_frame % speed
  578. ).str());
  579. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  580. }
  581. void FieldModelInstruction::ProcessCANM_2(
  582. CodeGenerator* code_gen, const std::string& entity, int char_id
  583. ){
  584. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  585. // ID will be fixed-up downstream.
  586. auto animation_id = params_[0]->GetUnsigned();
  587. auto start_frame = params_[1]->GetUnsigned() / 30.0f;
  588. auto end_frame = params_[2]->GetUnsigned() / 30.0f;
  589. // TODO: check for zero.
  590. auto speed = 1.0f / params_[3]->GetUnsigned();
  591. code_gen->AddOutputLine((
  592. boost::format("self.%1%:play_animation_stop(\"%2%\", %3%, %4%) -- speed %5%")
  593. % entity % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation_id)
  594. % start_frame % end_frame % speed
  595. ).str());
  596. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  597. }
  598. void FieldModelInstruction::ProcessCC(CodeGenerator* code_gen, const FieldEngine& engine){
  599. const auto& entity = engine.EntityByIndex(params_[0]->GetUnsigned());
  600. code_gen->AddOutputLine(
  601. (boost::format("entity_manager:set_player_entity(\"%1%\")") % entity.GetName()).str()
  602. );
  603. }
  604. void FieldModelInstruction::ProcessJUMP(CodeGenerator* code_gen, const std::string& entity){
  605. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  606. const float scale = 128.0f * cg->GetScaleFactor();
  607. const std::string x_value = FieldCodeGenerator::FormatValueOrVariable(
  608. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[4]->GetSigned(),
  609. FieldCodeGenerator::ValueType::Float, scale
  610. );
  611. const std::string y_value = FieldCodeGenerator::FormatValueOrVariable(
  612. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[5]->GetSigned(),
  613. FieldCodeGenerator::ValueType::Float, scale
  614. );
  615. float x = 0;
  616. float y = 0;
  617. const bool literal_coordinates
  618. = TryParseFloat(x_value, x) && TryParseFloat(y_value, y);
  619. int i = atoi(FieldCodeGenerator::FormatValueOrVariable(
  620. cg->GetFormatter(), params_[2]->GetUnsigned(), params_[6]->GetSigned()
  621. ).c_str());
  622. int steps = atoi(FieldCodeGenerator::FormatValueOrVariable(
  623. cg->GetFormatter(), params_[3]->GetUnsigned(), params_[7]->GetSigned()
  624. ).c_str());
  625. //x *= 0.00781250273224;
  626. //y *= 0.00781250273224;
  627. // Always force reduce steps and bound it between 1 and 5.
  628. steps /= 6;
  629. if (steps < 1) steps = 1;
  630. if (steps > 5) steps = 5;
  631. // TODO: Z hardcoded as -1, handled in Entity::ScriptJumpToPosition.
  632. // TODO: Hardcoded 0.5 in seconds. Calculate using distance.
  633. if (literal_coordinates){
  634. code_gen->AddOutputLine((
  635. boost::format("self.%1%:jump_to_position(%2%, %3%, -1, 0.5, %4%) -- %5% steps.")
  636. % entity % x % y % i % steps
  637. ).str());
  638. }
  639. else{
  640. code_gen->AddOutputLine(
  641. "self." + entity + ":jump_to_position(" + x_value + ", " + y_value
  642. + ", -1, 0.5, " + std::to_string(i) + ")"
  643. );
  644. }
  645. code_gen->AddOutputLine((boost::format("self.%1%:jump_sync()") % entity).str());
  646. }
  647. void FieldModelInstruction::ProcessAXYZI(CodeGenerator* code_gen){
  648. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  649. const float scale = 128.0f * cg->GetScaleFactor();
  650. code_gen->AddOutputLine((
  651. boost::format("axyzi(%1%, %2%, %3%, %4%, %5%, %6%, %7%, %8%, %9%, %10%)")
  652. % params_[0]->GetSigned() % params_[1]->GetSigned() % params_[2]->GetSigned()
  653. % params_[3]->GetSigned() % params_[4]->GetSigned() % params_[5]->GetSigned()
  654. % params_[6]->GetSigned() % params_[7]->GetSigned() % params_[8]->GetSigned()
  655. % scale
  656. ).str());
  657. }
  658. void FieldModelInstruction::ProcessLADER(
  659. CodeGenerator* code_gen, const std::string& entity, int char_id
  660. ){
  661. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  662. const float scale = 128.0f * cg->GetScaleFactor();
  663. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  664. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[4]->GetSigned(),
  665. FieldCodeGenerator::ValueType::Float, scale
  666. );
  667. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  668. cg->GetFormatter(), params_[1]->GetUnsigned(), params_[5]->GetSigned(),
  669. FieldCodeGenerator::ValueType::Float, scale
  670. );
  671. const auto& z = FieldCodeGenerator::FormatValueOrVariable(
  672. cg->GetFormatter(), params_[2]->GetUnsigned(), params_[6]->GetSigned(),
  673. FieldCodeGenerator::ValueType::Float, scale
  674. );
  675. uint end_triangle = atoi(FieldCodeGenerator::FormatValueOrVariable(
  676. cg->GetFormatter(), params_[3]->GetUnsigned(), params_[7]->GetUnsigned()
  677. ).c_str());
  678. uint keys = params_[8]->GetUnsigned();
  679. uint animation = params_[9]->GetUnsigned();
  680. //float orientation = params_[10]->GetUnsigned() / (256.0f / 360.0f);
  681. const auto& orientation = FieldCodeGenerator::FormatValueOrVariable(
  682. cg->GetFormatter(), 0, params_[10]->GetSigned(),
  683. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  684. );
  685. uint speed = params_[11]->GetUnsigned();
  686. // TODO: Orientation and speed not set.
  687. code_gen->AddOutputLine((
  688. boost::format(
  689. "self.%1%:linear_to_position(%2%, %3%, %4%, %5%, \"%10%\", %6%, %7%) "
  690. "-- Animation %8% -- Speed %9%"
  691. ) % entity % x % y % z % keys % orientation % end_triangle % animation % speed
  692. % cg->GetFormatter().GetFriendlyAnimationName(char_id, animation)
  693. ).str());
  694. code_gen->AddOutputLine((boost::format("self.%1%:linear_sync()") % entity).str());
  695. }
  696. void FieldModelInstruction::ProcessSLIDR(CodeGenerator* code_gen, const std::string& entity){
  697. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  698. const float scale = 128.0f * cg->GetScaleFactor();
  699. // The documentation says the value should be in param_[1], but it's actually on param_[2]
  700. const std::string radius_value = FieldCodeGenerator::FormatValueOrVariable(
  701. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[2]->GetUnsigned(),
  702. FieldCodeGenerator::ValueType::Float, scale
  703. );
  704. float radius = 0;
  705. if (TryParseFloat(radius_value, radius)){
  706. code_gen->AddOutputLine((
  707. boost::format("self.%1%:set_solid_radius(%2%)")
  708. % entity % radius
  709. ).str());
  710. }
  711. else{
  712. code_gen->AddOutputLine(
  713. "self." + entity + ":set_solid_radius(" + radius_value + ")"
  714. );
  715. }
  716. }
  717. void FieldModelInstruction::ProcessSOLID(CodeGenerator* code_gen, const std::string& entity){
  718. code_gen->AddOutputLine((
  719. boost::format("self.%1%:set_solid(%2%)")
  720. % entity % FieldCodeGenerator::FormatInvertedBool(params_[0]->GetUnsigned())
  721. ).str());
  722. }
  723. void FieldModelInstruction::ProcessOFST(CodeGenerator* code_gen, const std::string& entity){
  724. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  725. const float scale = 128.0f * cg->GetScaleFactor();
  726. const std::string x_value = FieldCodeGenerator::FormatValueOrVariable(
  727. cg->GetFormatter(), params_[0]->GetUnsigned(), params_[5]->GetSigned(),
  728. FieldCodeGenerator::ValueType::Float, scale
  729. );
  730. const std::string y_value = FieldCodeGenerator::FormatValueOrVariable(
  731. cg->GetFormatter(), params_[1]->GetUnsigned(),params_[6]->GetSigned(),
  732. FieldCodeGenerator::ValueType::Float, scale
  733. );
  734. const std::string z_value = FieldCodeGenerator::FormatValueOrVariable(
  735. cg->GetFormatter(), params_[2]->GetUnsigned(), params_[7]->GetSigned(),
  736. FieldCodeGenerator::ValueType::Float, scale
  737. );
  738. const std::string speed_value = FieldCodeGenerator::FormatValueOrVariable(
  739. cg->GetFormatter(), params_[3]->GetUnsigned(), params_[8]->GetUnsigned()
  740. );
  741. float x = 0;
  742. float y = 0;
  743. float z = 0;
  744. float speed = 0;
  745. const bool literal_values
  746. = TryParseFloat(x_value, x) && TryParseFloat(y_value, y)
  747. && TryParseFloat(z_value, z) && TryParseFloat(speed_value, speed);
  748. std::string move_type;
  749. switch (params_[4]->GetUnsigned()){
  750. case 0: move_type = "Entity.NONE"; break;
  751. case 1: move_type = "Entity.LINEAR"; break;
  752. case 2: move_type = "Entity.SMOOTH"; break;
  753. default:
  754. code_gen->WriteTodo(entity, "OFST with unsupported movement type");
  755. return;
  756. }
  757. if (literal_values){
  758. speed /= 30.0f;
  759. code_gen->AddOutputLine((
  760. boost::format("self.%1%:offset_to_position(%2%, %3%, %4%, %5%, %6%)")
  761. % entity % x % y % z % move_type % speed
  762. ).str());
  763. }
  764. else{
  765. code_gen->AddOutputLine(
  766. "self." + entity + ":offset_to_position(" + x_value + ", " + y_value
  767. + ", " + z_value + ", " + move_type + ", (" + speed_value + ") / 30.0)"
  768. );
  769. }
  770. }