EntityManager.cpp 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
  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 <algorithm>
  16. #include <cstdlib>
  17. #include <iostream>
  18. #include <cmath>
  19. #include <limits>
  20. #include <OgreEntity.h>
  21. #include <OgreRoot.h>
  22. #include <OgreViewport.h>
  23. #include "core/CameraManager.h"
  24. #include "core/ConfigVar.h"
  25. #include "core/DebugDraw.h"
  26. #include "core/EntityManager.h"
  27. #include "core/EntityModel.h"
  28. #include "core/InputManager.h"
  29. #include "core/Logger.h"
  30. #include "core/ScriptManager.h"
  31. #include "core/Timer.h"
  32. #include "core/DialogsManager.h"
  33. /**
  34. * Entity manager singleton.
  35. */
  36. template<>EntityManager *Ogre::Singleton<EntityManager>::msSingleton = nullptr;
  37. ConfigVar cv_debug_grid("debug_grid", "Draw debug grid", "false");
  38. ConfigVar cv_debug_axis("debug_axis", "Draw debug axis", "false");
  39. ConfigVar cv_encounter_multiplier(
  40. "encounter_multiplier", "Multiplier of the random encounter rate (0 disables them)", "1"
  41. );
  42. ConfigVar cv_debug_encounters("debug_encounters", "Log random encounter checks", "false");
  43. /**
  44. * Frames of walking (at 30 frames per second) between two encounter checks. The original game
  45. * checks every 8 frames.
  46. */
  47. static const float ENCOUNTER_CHECK_FRAMES = 8.0f;
  48. /**
  49. * Value added to the encounter accumulator in each check, before dividing it by the rate of the
  50. * table. The original game uses the speed of the player, which is not known here, so this is tuned
  51. * to a battle every 15-30 seconds of walking in most fields. The encounter_multiplier cvar scales
  52. * it.
  53. */
  54. static const float ENCOUNTER_STEP_VALUE = 5.0f;
  55. const float EntityManager::SCENE_SCALE = 0.003f;
  56. const unsigned int EntityManager::BATTLE_BACKGROUND_ID = 1000;
  57. const unsigned int EntityManager::WORLD_MAP_BACKGROUND_ID = 1001;
  58. float EntityManager::PointElevation(
  59. const Ogre::Vector2& point, const Ogre::Vector3& a, const Ogre::Vector3& b, const Ogre::Vector3& c
  60. ){
  61. float _a = a.z * (b.y - c.y) + b.z * (c.y - a.y) + c.z * (a.y - b.y);
  62. float _b = a.y * (b.x - c.x) + b.y * (c.x - a.x) + c.y * (a.x - b.x);
  63. float _c = a.x * (b.z - c.z) + b.x * (c.z - a.z) + c.x * (a.z - b.z);
  64. float _d
  65. = a.x * (b.z * c.y - c.z * b.y) + b.x * (c.z * a.y - a.z * c.y)
  66. + c.x * (a.z * b.y - b.z * a.y);
  67. return (_d - _c * point.y - _a * point.x) / _b;
  68. }
  69. float EntityManager::SideOfVector(
  70. const Ogre::Vector2& point, const Ogre::Vector2& p1, const Ogre::Vector2& p2
  71. ){
  72. Ogre::Vector2 AB = p2 - p1;
  73. Ogre::Vector2 AP = point - p1;
  74. return AB.x * AP.y - AB.y * AP.x;
  75. }
  76. float EntityManager::SquareDistanceToLine(
  77. const Ogre::Vector3& point, const Ogre::Vector3& point_a, const Ogre::Vector3& point_b,
  78. Ogre::Vector3& proj
  79. ){
  80. float temp =
  81. -((point_a.x - point.x) * (point_b.x - point_a.x) + (point_a.y - point.y)
  82. * (point_b.y - point_a.y) + (point_a.z - point.z) * (point_b.z - point_a.z))
  83. / ((point_b.x - point_a.x) * (point_b.x - point_a.x) + (point_b.y - point_a.y)
  84. * (point_b.y - point_a.y) + (point_b.z - point_a.z) * (point_b.z - point_a.z));
  85. proj.x = temp * (point_b.x - point_a.x) + point_a.x;
  86. proj.y = temp * (point_b.y - point_a.y) + point_a.y;
  87. proj.z = temp * (point_b.z - point_a.z) + point_a.z;
  88. if (
  89. ((point_a.x >= proj.x && point_b.x <= proj.x) || (point_a.x < proj.x && point_b.x >= proj.x))
  90. && (
  91. (point_a.y >= proj.y && point_b.y <= proj.y) || (point_a.y < proj.y && point_b.y >= proj.y)
  92. )
  93. ){
  94. return (proj.x - point.x) * (proj.x - point.x) + (proj.y - point.y)
  95. * (proj.y - point.y) + (proj.z - point.z) * (proj.z - point.z);
  96. }
  97. return -1;
  98. }
  99. Ogre::Degree EntityManager::GetDirectionToPoint(
  100. const Ogre::Vector3& current_point, const Ogre::Vector3& direction_point
  101. ){
  102. // TODO: Declare function in header file.
  103. Ogre::Vector2 up(0.0f, -1.0f);
  104. Ogre::Vector2 dir(direction_point.x - current_point.x, direction_point.y - current_point.y);
  105. // Angle between vectors
  106. Ogre::Degree angle(Ogre::Radian(acosf(dir.dotProduct(up) / (dir.length() * up.length()))));
  107. return (dir.x < 0) ? Ogre::Degree(360) - angle : angle;
  108. }
  109. EntityManager::EntityManager():
  110. background_3d_(nullptr),
  111. player_entity_(nullptr),
  112. player_move_(Ogre::Vector3::ZERO),
  113. player_move_rotation_(0),
  114. player_lock_(false),
  115. player_run_(false),
  116. random_encounters_(false),
  117. encounter_rate_(0),
  118. encounter_table_(1),
  119. encounter_distance_(0.0f),
  120. encounter_last_position_(Ogre::Vector3::ZERO),
  121. encounter_last_position_valid_(false),
  122. encounter_safe_steps_(0)
  123. {
  124. LOG_TRIVIAL("EntityManager created.");
  125. scene_node_ = Ogre::Root::getSingleton().getSceneManager("Scene")
  126. ->getRootSceneNode()->createChildSceneNode("EntityManager");
  127. //grid_ = Ogre::Root::getSingleton().getSceneManager("Scene")
  128. // ->createEntity("Grid", "system/grid.mesh");
  129. grid_ = Ogre::Root::getSingleton().getSceneManager("Scene")->createEntity("Grid", "grid.mesh");
  130. scene_node_->attachObject(grid_);
  131. //axis_ = Ogre::Root::getSingleton().getSceneManager("Scene")
  132. // ->createEntity("Axis", "system/axis.mesh");
  133. axis_ = Ogre::Root::getSingleton().getSceneManager("Scene")->createEntity("Axis", "axis.mesh");
  134. scene_node_->attachObject(axis_);
  135. }
  136. EntityManager::~EntityManager(){
  137. if (grid_ != nullptr) Ogre::Root::getSingleton().getSceneManager("Scene")->destroyEntity(grid_);
  138. if (axis_ != nullptr) Ogre::Root::getSingleton().getSceneManager("Scene")->destroyEntity(axis_);
  139. Clear();
  140. Ogre::Root::getSingleton().getSceneManager("Scene")->getRootSceneNode()->removeAndDestroyChild(
  141. "EntityManager"
  142. );
  143. LOG_TRIVIAL("EntityManager destroyed.");
  144. }
  145. void EntityManager::Input(const VGears::Event& event){
  146. background_2d_.InputDebug(event);
  147. if (paused_ == true) return;
  148. if (player_entity_ != NULL && player_lock_ == false){
  149. if (event.type == VGears::ET_KEY_REPEAT && event.event == "walk_left") player_move_.x = -1;
  150. else if (event.type == VGears::ET_KEY_REPEAT && event.event == "walk_right")
  151. player_move_.x = 1;
  152. if (event.type == VGears::ET_KEY_REPEAT && event.event == "walk_down") player_move_.y = -1;
  153. else if (event.type == VGears::ET_KEY_REPEAT && event.event == "walk_up")
  154. player_move_.y = 1;
  155. if (event.type == VGears::ET_KEY_REPEAT && event.event == "run") player_run_ = true;
  156. if (event.type == VGears::ET_KEY_PRESS && event.event == "CONFIRM"){
  157. CheckEntityInteract();
  158. for (unsigned int i = 0; i < entity_triggers_.size(); ++ i){
  159. if (
  160. entity_triggers_[i]->IsEnabled() == true
  161. && entity_triggers_[i]->CheckApproached() == true
  162. ){
  163. ScriptEntity* scr_entity = ScriptManager::getSingleton().GetScriptEntityByName(
  164. ScriptManager::ENTITY, entity_triggers_[i]->GetName()
  165. );
  166. bool added = ScriptManager::getSingleton().ScriptRequest(
  167. scr_entity, "on_interact", 1, "", "", false, false
  168. );
  169. if (added == false){
  170. LOG_WARNING(
  171. "Script 'on_interact' for entity '" + entity_triggers_[i]->GetName()
  172. + "' doesn't exist."
  173. );
  174. }
  175. }
  176. }
  177. }
  178. // Rotate move vector to field move rotation
  179. Ogre::Quaternion q1;
  180. q1.FromAngleAxis(player_move_rotation_, Ogre::Vector3::UNIT_Z);
  181. player_move_ = q1 * player_move_;
  182. }
  183. }
  184. void EntityManager::UpdateField(){
  185. // Update all entity scripts
  186. ScriptManager::getSingleton().Update(ScriptManager::ENTITY);
  187. // The player is walking if there is a move set by the input.
  188. const bool walked = player_entity_ != nullptr && player_move_ != Ogre::Vector3::ZERO;
  189. // Set move point for player entity
  190. if (player_entity_ != nullptr && player_move_ != Ogre::Vector3::ZERO){
  191. Entity::State state = player_entity_->GetState();
  192. if (state == Entity::WALKMESH || state == Entity::NONE){
  193. player_move_
  194. *= player_entity_->GetMoveWalkSpeed() * Timer::getSingleton().GetGameTimeDelta();
  195. player_entity_->SetMovePosition(player_entity_->GetPosition() + player_move_);
  196. player_entity_->SetState(Entity::WALKMESH);
  197. }
  198. // HACK: If manually moving, make playable entity solid.
  199. // Some scripts 'forget' to make the playable entity solid at the end
  200. // of them, and if it remains non-solid, it can't interact with lines
  201. // (i.e. it cant' use gateways).
  202. player_entity_->SetSolid(true);
  203. }
  204. for (unsigned int i = 0; i < entity_.size(); ++ i){
  205. entity_[i]->Update();
  206. // It's not needed to update movements it delta time == 0 (for example
  207. // if time is stopped)
  208. if (Timer::getSingleton().GetGameTimeDelta() == 0) continue;
  209. // Update screen scroll.
  210. Entity* scroll_entity = background_2d_.GetAutoScrollEntity();
  211. if (scroll_entity == entity_[i])
  212. background_2d_.SetScroll(background_2d_.GetEntityScroll(scroll_entity));
  213. // Update offset.
  214. if (entity_[i]->GetOffsetType() != AT_NONE) SetNextOffsetStep(entity_[i]);
  215. // Update turn.
  216. if (entity_[i]->GetTurnType() != AT_NONE) SetNextTurnStep(entity_[i]);
  217. // Update movement.
  218. switch(entity_[i]->GetState()){
  219. case Entity::WALKMESH:
  220. {
  221. // Try set entity on walkmesh if it's still don't has triangle.
  222. if (entity_[i]->GetMoveTriangleId() == -1){
  223. if (SetEntityOnWalkmesh(entity_[i]) == false){
  224. LOG_TRIVIAL(
  225. "Can't set entity '" + entity_[i]->GetName()
  226. + "' on walkmesh. Finish move."
  227. );
  228. entity_[i]->UnsetMove();
  229. }
  230. }
  231. // Perform move
  232. if (entity_[i]->GetState() == Entity::WALKMESH){
  233. float speed = 0;
  234. if (player_lock_ == false && player_entity_ == entity_[i]){
  235. speed = entity_[i]->GetMoveWalkSpeed();
  236. // TODO: Set as a constant.
  237. // Or even better, as a configurable variable.
  238. // The run key inverts the default movement.
  239. if (player_run_ != player_default_run_) speed *= 4;
  240. }
  241. else speed = entity_[i]->GetMoveAutoSpeed();
  242. float remaining_time = Timer::getSingleton().GetGameTimeDelta();
  243. bool is_move = false;
  244. if (entity_[i]->IsScriptedMove() && speed > 0.0f){
  245. const float max_step_time = 0.2f / speed;
  246. while (remaining_time > 0.0f && entity_[i]->GetState() == Entity::WALKMESH){
  247. const float step_time = std::min(remaining_time, max_step_time);
  248. const bool moved = PerformWalkmeshMove(entity_[i], speed, step_time);
  249. is_move = is_move || moved;
  250. if (!moved) break;
  251. remaining_time -= step_time;
  252. }
  253. }
  254. else is_move = PerformWalkmeshMove(entity_[i], speed, remaining_time);
  255. if (entity_[i]->GetMoveAutoAnimation() == true){
  256. if (is_move == true){
  257. if (speed >= entity_[i]->GetMoveRunSpeed()){
  258. entity_[i]->PlayAnimationContinue(
  259. entity_[i]->GetMoveAnimationRunName()
  260. );
  261. }
  262. else{
  263. entity_[i]->PlayAnimationContinue(
  264. entity_[i]->GetMoveAnimationWalkName()
  265. );
  266. }
  267. }
  268. else if (
  269. entity_[i]->GetAnimationState() != Entity::REQUESTED_ANIMATION
  270. ){
  271. entity_[i]->PlayAnimationContinue(
  272. entity_[i]->GetDefaultAnimationName()
  273. );
  274. }
  275. }
  276. }
  277. }
  278. break;
  279. case Entity::LINEAR:
  280. CheckTriggers(entity_[i], entity_[i]->GetPosition());
  281. SetNextLinearStep(entity_[i]);
  282. break;
  283. case Entity::JUMP:
  284. SetNextJumpStep(entity_[i]);
  285. break;
  286. case Entity::NEEDS_TO_REATTACH:
  287. if (SetEntityOnWalkmesh(entity_[i])) entity_[i]->SetState(Entity::WALKMESH);
  288. else entity_[i]->SetState(Entity::NONE);
  289. case Entity::NONE:
  290. CheckTriggers(entity_[i], entity_[i]->GetPosition());
  291. if (entity_[i]->GetAnimationState() != Entity::REQUESTED_ANIMATION)
  292. entity_[i]->PlayAnimationContinue(entity_[i]->GetDefaultAnimationName());
  293. break;
  294. }
  295. }
  296. // Reset player move. It already must be handled in update
  297. player_move_ = Ogre::Vector3::ZERO;
  298. player_run_ = false;
  299. if (background_2d_.GetScrollType() != Background2D::NONE) SetNextScrollStep();
  300. background_2d_.Update();
  301. // This can start a battle and change the module, so it's the last thing to do.
  302. UpdateEncounters(walked);
  303. // The movie follows the speed of the game, so the scenes that are timed with it stay in sync.
  304. // Its sound doesn't, so at any other speed they drift apart.
  305. movie_player_.Update(Timer::getSingleton().GetGameTimeDelta());
  306. screen_fade_.Update(Timer::getSingleton().GetGameTimeDelta());
  307. }
  308. void EntityManager::UpdateBattle(){
  309. ScriptManager::getSingleton().Update(ScriptManager::BATTLE);
  310. if (background_3d_ != nullptr){
  311. background_3d_->Update();
  312. background_3d_->PlayAnimationContinue(background_3d_->GetDefaultAnimationName());
  313. }
  314. for (unsigned int i = 0; i < battle_entity_.size(); ++ i){
  315. battle_entity_[i]->Update();
  316. // Go back to the default animation, unless an animation has been requested. Requested
  317. // animations that are not looped go back to the default one by themselves when they end.
  318. if (battle_entity_[i]->GetAnimationState() != Entity::REQUESTED_ANIMATION){
  319. battle_entity_[i]->PlayAnimationContinue(battle_entity_[i]->GetDefaultAnimationName());
  320. }
  321. }
  322. //CameraManager::getSingleton().GetCurrentCamera()->roll(Ogre::Radian(0.01));
  323. }
  324. void EntityManager::UpdateWorld(){
  325. ScriptManager::getSingleton().Update(ScriptManager::ENTITY);
  326. for (Entity* entity : entity_){
  327. entity->Update();
  328. }
  329. player_move_ = Ogre::Vector3::ZERO;
  330. player_run_ = false;
  331. }
  332. void EntityManager::AddFieldOrWorldMapEntity(
  333. const Ogre::String& name, const Ogre::String& file_name, const Ogre::Vector3& position,
  334. const Ogre::Degree& rotation, const Ogre::Vector3& scale,
  335. const Ogre::Quaternion& root_orientation, const int index
  336. ){
  337. if (module_ != Module::FIELD){
  338. LOG_ERROR("Tried to add field or world Entity but the EntityManager is in battle mode.");
  339. return;
  340. }
  341. Ogre::SceneNode* node = scene_node_->createChildSceneNode("Model_" + name);
  342. EntityModel* entity = new EntityModel(name, file_name, node);
  343. entity->SetPosition(position);
  344. entity->SetRotation(rotation);
  345. entity->setScale(scale);
  346. entity->SetIndex(index);
  347. entity->setRootOrientation(root_orientation);
  348. entity_.push_back(entity);
  349. ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, entity->GetName(), entity);
  350. }
  351. EntityModel* EntityManager::AddWorldMapEntity(
  352. const Ogre::String& name, const Ogre::String& file_name,
  353. const Ogre::Vector3& position, const Ogre::Degree& rotation,
  354. const Ogre::Vector3& scale, const Ogre::String& resource_group
  355. ){
  356. if (!IsWorldModule()){
  357. LOG_ERROR("Tried to add a world-map entity outside world-map mode.");
  358. return nullptr;
  359. }
  360. if (GetEntity(name) != nullptr){
  361. LOG_ERROR("Tried to add duplicate world-map entity '" + name + "'.");
  362. return nullptr;
  363. }
  364. Ogre::SceneNode* node = scene_node_->createChildSceneNode("Model_" + name);
  365. EntityModel* entity = new EntityModel(name, file_name, node, resource_group);
  366. entity->SetPosition(position);
  367. entity->SetRotation(rotation);
  368. entity->setScale(scale);
  369. entity->SetVisible(true);
  370. entity_.push_back(entity);
  371. ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, entity->GetName(), entity);
  372. return entity;
  373. }
  374. void EntityManager::AddBattleEntity(
  375. const Ogre::String& name, const Ogre::String& file_name, const Ogre::Vector3& position,
  376. const Ogre::Degree& rotation, const Ogre::Vector3& scale,
  377. const Ogre::Quaternion& root_orientation, const int index
  378. ){
  379. if (module_ != Module::BATTLE){
  380. LOG_ERROR("Tried to add battle Entity but the EntityManager is not in battle mode.");
  381. return;
  382. }
  383. Ogre::SceneNode* node = scene_node_->createChildSceneNode("Battle_" + name);
  384. // Battle models must be reoriented.
  385. node->setOrientation(root_orientation);
  386. EntityModel* entity = new EntityModel(name, file_name, node);
  387. entity->SetPosition(position);
  388. entity->SetRotation(rotation);
  389. entity->setScale(scale);
  390. entity->SetIndex(index);
  391. entity->SetVisible(true);
  392. battle_entity_.push_back(entity);
  393. ScriptManager::getSingleton().AddEntity(ScriptManager::BATTLE, entity->GetName(), entity);
  394. /*std::cout << "BATTLE ANIMATIONS FOR " << name << std::endl;
  395. const std::map<Ogre::String, Ogre::AnimationState*>& anims
  396. = entity->GetModel()->getAllAnimationStates()->getAnimationStates();
  397. int size = anims.size();
  398. std::cout << " " << std::to_string(size) << " TOTAL " << std::endl;
  399. for (auto anim = anims.begin(); anim != anims.end(); ++ anim){
  400. std::cout << " - " << anim->first << ", " << anim->second << std::endl;
  401. }*/
  402. /* Animations have the following names:
  403. * XX_00,
  404. * XX_01,
  405. * XX_02,
  406. * ...
  407. * where XX is the first two characters of file_name, without the path.
  408. */
  409. Ogre::String anim = file_name.substr(file_name.find_last_of("/\\") + 1).substr(0, 2) + "_00";
  410. //std::cout << " Playing " << anim << std::endl;
  411. entity->ScriptSetDefaultAnimation(anim.c_str());
  412. }
  413. Entity* EntityManager::GetBackground3D() const{
  414. if (IsFieldModule()){
  415. LOG_WARNING("Tried to retrieve a 3D background while the EntityManager is in field mode.");
  416. return nullptr;
  417. }
  418. return background_3d_;
  419. }
  420. void EntityManager::SetBackground3D(const Ogre::String& name, const Ogre::String& file_name){
  421. if (IsFieldModule()){
  422. LOG_ERROR("Tried to add a 3D background while the EntityManager is in field mode.");
  423. return;
  424. }
  425. Ogre::SceneNode* node = scene_node_->createChildSceneNode("bg_" + name);
  426. background_3d_ = new EntityModel(name, file_name, node);
  427. background_3d_->SetPosition(Ogre::Vector3(0, 0, 0));
  428. background_3d_->SetRotation(Ogre::Degree(0));
  429. background_3d_->setScale(Ogre::Vector3(SCENE_SCALE, SCENE_SCALE, SCENE_SCALE));
  430. background_3d_->SetIndex(IsBattleModule() ? BATTLE_BACKGROUND_ID : WORLD_MAP_BACKGROUND_ID);
  431. background_3d_->SetVisible(true);
  432. // Backgrounds must be reoriented.
  433. node->setOrientation(1, 1, 0, 0);
  434. // Probably don't need a script for the 3d background.
  435. /*ScriptManager::getSingleton().AddEntity(
  436. ScriptManager::BATTLE, background_3d_->GetName(), background_3d_
  437. );*/
  438. }
  439. void EntityManager::UpdateDebug(){
  440. grid_->setVisible(cv_debug_grid.GetB());
  441. axis_->setVisible(cv_debug_axis.GetB());
  442. if (module_ == Module::BATTLE){
  443. if (background_3d_ != nullptr) background_3d_->UpdateDebug();
  444. for (unsigned int i = 0; i < battle_entity_.size(); ++ i) battle_entity_[i]->UpdateDebug();
  445. }
  446. else{
  447. for (unsigned int i = 0; i < entity_.size(); ++ i) entity_[i]->UpdateDebug();
  448. for (unsigned int i = 0; i < entity_triggers_.size(); ++ i)
  449. entity_triggers_[i]->UpdateDebug();
  450. for (unsigned int i = 0; i < entity_points_.size(); ++ i) entity_points_[i]->UpdateDebug();
  451. walkmesh_.UpdateDebug();
  452. background_2d_.UpdateDebug();
  453. }
  454. }
  455. void EntityManager::OnResize(){background_2d_.OnResize();}
  456. void EntityManager::ClearField(){
  457. ClearEncounterTables();
  458. entities_hidden_by_battle_.clear();
  459. walkmesh_.Clear();
  460. background_2d_.Clear();
  461. screen_fade_.Clear();
  462. DialogsManager::getSingleton().Clear();
  463. for (unsigned int i = 0; i < entity_.size(); ++ i){
  464. ScriptManager::getSingleton().RemoveEntity(ScriptManager::ENTITY, entity_[i]->GetName());
  465. delete entity_[i];
  466. }
  467. entity_.clear();
  468. player_entity_ = nullptr;
  469. player_move_ = Ogre::Vector3::ZERO;
  470. player_move_rotation_ = 0;
  471. player_lock_ = false;
  472. for (unsigned int i = 0; i < entity_triggers_.size(); ++ i){
  473. ScriptManager::getSingleton().RemoveEntity(
  474. ScriptManager::ENTITY, entity_triggers_[i]->GetName()
  475. );
  476. delete entity_triggers_[i];
  477. }
  478. entity_triggers_.clear();
  479. for (unsigned int i = 0; i < entity_points_.size(); ++ i) delete entity_points_[i];
  480. entity_points_.clear();
  481. for (unsigned int i = 0; i < entity_scripts_.size(); ++ i)
  482. ScriptManager::getSingleton().RemoveEntity(ScriptManager::ENTITY, entity_scripts_[i]);
  483. scene_node_->removeAndDestroyAllChildren();
  484. }
  485. void EntityManager::ClearBattle(){
  486. for (unsigned int i = 0; i < battle_entity_.size(); ++ i){
  487. ScriptManager::getSingleton().RemoveEntity(
  488. ScriptManager::ENTITY, battle_entity_[i]->GetName()
  489. );
  490. Ogre::Root::getSingleton().getSceneManager("Scene")->destroyEntity(
  491. battle_entity_[i]->GetName()
  492. );
  493. scene_node_->removeAndDestroyChild("Battle_" + battle_entity_[i]->GetName());
  494. }
  495. battle_entity_.clear();
  496. if (background_3d_ != nullptr){
  497. Ogre::Root::getSingleton().getSceneManager("Scene")->destroyEntity(
  498. "bg_" + background_3d_->GetName()
  499. );
  500. /*ScriptManager::getSingleton().RemoveEntity(
  501. ScriptManager::ENTITY, background_3d_->GetName()
  502. );*/
  503. scene_node_->getChild("bg_" + background_3d_->GetName())->removeAllChildren();
  504. scene_node_->removeAndDestroyChild("bg_" + background_3d_->GetName());
  505. Ogre::Root::getSingleton().getSceneManager("Scene")->destroyEntity(
  506. background_3d_->GetName()
  507. );
  508. background_3d_ = nullptr;
  509. }
  510. }
  511. void EntityManager::ClearWorld(){
  512. walkmesh_.Clear();
  513. for (Entity* entity : entity_){
  514. ScriptManager::getSingleton().RemoveEntity(ScriptManager::ENTITY, entity->GetName());
  515. delete entity;
  516. }
  517. entity_.clear();
  518. player_entity_ = nullptr;
  519. player_move_ = Ogre::Vector3::ZERO;
  520. player_move_rotation_ = 0;
  521. player_lock_ = false;
  522. player_run_ = false;
  523. for (EntityTrigger* trigger : entity_triggers_){
  524. ScriptManager::getSingleton().RemoveEntity(ScriptManager::ENTITY, trigger->GetName());
  525. delete trigger;
  526. }
  527. entity_triggers_.clear();
  528. for (EntityPoint* point : entity_points_) delete point;
  529. entity_points_.clear();
  530. for (const Ogre::String& script : entity_scripts_){
  531. ScriptManager::getSingleton().RemoveEntity(ScriptManager::ENTITY, script);
  532. }
  533. entity_scripts_.clear();
  534. scene_node_->removeAndDestroyAllChildren();
  535. }
  536. Walkmesh* EntityManager::GetWalkmesh(){return &walkmesh_;}
  537. bool EntityManager::IsWorldPositionBlocked(
  538. const Entity* moving_entity, const Ogre::Vector3& position
  539. ) const{
  540. if (!IsWorldModule() || moving_entity == nullptr) return false;
  541. for (const Entity* other : entity_){
  542. if (other == moving_entity || !other->IsSolid()) continue;
  543. const Ogre::Vector3 other_position = other->GetPosition();
  544. const Ogre::Real dx = position.x - other_position.x;
  545. const Ogre::Real dz = position.z - other_position.z;
  546. const Ogre::Real radius = moving_entity->GetSolidRadius() + other->GetSolidRadius();
  547. if (dx * dx + dz * dz >= radius * radius) continue;
  548. const Ogre::Real moving_top = position.y + moving_entity->GetHeight();
  549. const Ogre::Real other_top = other_position.y + other->GetHeight();
  550. if (position.y < other_top && other_position.y < moving_top) return true;
  551. }
  552. return false;
  553. }
  554. Background2D* EntityManager::GetBackground2D(){return &background_2d_;}
  555. ScreenFade* EntityManager::GetScreenFade(){return &screen_fade_;}
  556. MoviePlayer* EntityManager::GetMoviePlayer(){return &movie_player_;}
  557. void EntityManager::AddEntity(
  558. const Ogre::String& name, const Ogre::String& file_name,
  559. const Ogre::Vector3& position, const Ogre::Degree& rotation, int index
  560. ){
  561. AddEntity(
  562. name, file_name, position, rotation,
  563. Ogre::Vector3::UNIT_SCALE, Ogre::Quaternion::IDENTITY, index
  564. );
  565. }
  566. void EntityManager::AddEntity(
  567. const Ogre::String& name, const Ogre::String& file_name, const Ogre::Vector3& position,
  568. const Ogre::Degree& rotation, const Ogre::Vector3& scale,
  569. const Ogre::Quaternion& root_orientation, const int index
  570. ){
  571. if (IsBattleModule())
  572. AddBattleEntity(name, file_name, position, rotation, scale, root_orientation, index);
  573. else
  574. AddFieldOrWorldMapEntity(
  575. name, file_name, position, rotation, scale, root_orientation, index
  576. );
  577. }
  578. void EntityManager::ScriptAddEntity(
  579. const char* name, const char* file_name,
  580. const float x, const float y, const float z, const float rotation, int index
  581. ){AddEntity(name, file_name, Ogre::Vector3(x, y, z), Ogre::Degree(rotation), index);}
  582. void EntityManager::AddEntityTrigger(
  583. const Ogre::String& name,
  584. const Ogre::Vector3& point1, const Ogre::Vector3& point2, const bool enabled
  585. ){
  586. if (IsBattleModule()){
  587. LOG_WARNING("Tried to add an entity trigger but the EntityManager is in battle mode.");
  588. return;
  589. }
  590. EntityTrigger* trigger = new EntityTrigger(name);
  591. trigger->SetPoints(point1, point2);
  592. trigger->SetEnabled(enabled);
  593. entity_triggers_.push_back(trigger);
  594. ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, trigger->GetName(), nullptr);
  595. }
  596. void EntityManager::AddEntityPoint(
  597. const Ogre::String& name, const Ogre::Vector3& position, const float rotation
  598. ){
  599. if (IsBattleModule()){
  600. LOG_WARNING("Tried to add an entity point but the EntityManager is in battle mode.");
  601. return;
  602. }
  603. EntityPoint* entity_point = new EntityPoint(name);
  604. entity_point->SetPosition(position);
  605. entity_point->SetRotation(rotation);
  606. entity_points_.push_back(entity_point);
  607. }
  608. void EntityManager::AddEntityScript(const Ogre::String& name){
  609. // TODO: Should call to this be prevented while in battle mode?
  610. entity_scripts_.push_back(name);
  611. ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, name, nullptr);
  612. }
  613. void EntityManager::ScriptAddEntityScript(const char* name){AddEntityScript(name);}
  614. Entity* EntityManager::GetEntity(const Ogre::String& name) const{
  615. if (IsBattleModule()){
  616. for (unsigned int i = 0; i < battle_entity_.size(); ++ i)
  617. if (battle_entity_[i]->GetName() == name) return battle_entity_[i];
  618. }
  619. else{
  620. for (unsigned int i = 0; i < entity_.size(); ++ i)
  621. if (entity_[i]->GetName() == name) return entity_[i];
  622. }
  623. return nullptr;
  624. }
  625. Entity* EntityManager::GetEntityFromIndex(const int index) const{
  626. if (IsBattleModule()){
  627. if (background_3d_ != nullptr && background_3d_->GetIndex() == index)
  628. return background_3d_;
  629. for (unsigned int i = 0; i < battle_entity_.size(); ++ i)
  630. if (battle_entity_[i]->GetIndex() == index) return battle_entity_[i];
  631. }
  632. else{
  633. for (unsigned int i = 0; i < entity_.size(); ++ i)
  634. if (entity_[i]->GetIndex() == index) return entity_[i];
  635. }
  636. return nullptr;
  637. }
  638. Entity* EntityManager::GetEntityFromCharacterId(const int id) const{
  639. if (IsBattleModule()){
  640. for (unsigned int i = 0; i < battle_entity_.size(); ++ i)
  641. if (battle_entity_[i]->IsCharacter() && battle_entity_[i]->GetCharacterId() == id)
  642. return battle_entity_[i];
  643. }
  644. else{
  645. for (unsigned int i = 0; i < entity_.size(); ++ i)
  646. if (entity_[i]->IsCharacter() && entity_[i]->GetCharacterId() == id) return entity_[i];
  647. }
  648. return nullptr;
  649. }
  650. Entity* EntityManager::ScriptGetEntity(const char* name) const{
  651. return GetEntity(Ogre::String(name));
  652. }
  653. EntityPoint* EntityManager::ScriptGetEntityPoint(const char* name) const{
  654. for (unsigned int i = 0; i < entity_points_.size(); ++ i)
  655. if (entity_points_[i]->GetName() == name) return entity_points_[i];
  656. return nullptr;
  657. }
  658. void EntityManager::ScriptSetPlayerEntity(const char* name){
  659. if (IsBattleModule()){
  660. LOG_WARNING("Tried to set a player entity but the EntityManager is in battle mode.");
  661. return;
  662. }
  663. for (unsigned int i = 0; i < entity_.size(); ++ i){
  664. if (entity_[i]->GetName() == name){
  665. player_entity_ = entity_[i];
  666. // Also, make is solid, visible, and non-talkable.
  667. entity_[i]->SetSolid(true);
  668. entity_[i]->SetVisible(true);
  669. entity_[i]->SetTalkable(false);
  670. }
  671. }
  672. }
  673. void EntityManager::ScriptPlacePlayerAtFieldEntry(
  674. const float x, const float y, const int triangle_id, const float direction
  675. ){
  676. if (player_entity_ == nullptr || walkmesh_.GetNumberOfTriangles() <= 0) return;
  677. int safe_triangle_id = triangle_id;
  678. if (safe_triangle_id < 0 || safe_triangle_id >= walkmesh_.GetNumberOfTriangles()){
  679. LOG_WARNING("World-map field entry has an invalid walkmesh triangle; using triangle 0.");
  680. safe_triangle_id = 0;
  681. }
  682. player_entity_->SetPosition(Ogre::Vector3(
  683. x, y, walkmesh_.GetA(static_cast<unsigned int>(safe_triangle_id)).z
  684. ));
  685. player_entity_->SetRotation(Ogre::Degree(direction));
  686. player_entity_->SetMoveTriangleId(safe_triangle_id);
  687. player_entity_->SetState(Entity::WALKMESH);
  688. }
  689. Entity* EntityManager::ScriptGetPlayerEntity() const{
  690. if (IsBattleModule()){
  691. LOG_WARNING("Tried to set a player entity but the EntityManager is in battle mode.");
  692. return nullptr;
  693. }
  694. return player_entity_;
  695. }
  696. void EntityManager::ScriptUnsetPlayerEntity(){
  697. if (IsBattleModule()){
  698. LOG_WARNING("Tried to unset a player entity but the EntityManager is in battle mode.");
  699. return;
  700. }
  701. player_entity_ = nullptr;
  702. }
  703. void EntityManager::ScriptPlayerLock(const bool lock){
  704. if (IsBattleModule()){
  705. LOG_WARNING("Tried to set player lock but the EntityManager is in battle mode.");
  706. return;
  707. }
  708. player_lock_ = lock;
  709. if (lock == true) player_move_ = Ogre::Vector3::ZERO;
  710. }
  711. bool EntityManager::IsPlayerLocked() const{return player_lock_;}
  712. void EntityManager::SetPlayerDefaultRun(const bool run){player_default_run_ = run;}
  713. void EntityManager::SetPlayerMoveRotation(const Ogre::Radian rotation){
  714. if (IsBattleModule()){
  715. LOG_WARNING("Tried to set player move rotation the EntityManager is in battle mode.");
  716. return;
  717. }
  718. player_move_rotation_ = rotation;
  719. }
  720. bool EntityManager::GetRandomEncounters(){return random_encounters_;}
  721. float EntityManager::GetEncounterMultiplier() const{return cv_encounter_multiplier.GetF();}
  722. bool EntityManager::IsDebugEncounters() const{return cv_debug_encounters.GetB();}
  723. void EntityManager::SetRandomEncounters(bool active){random_encounters_ = active;}
  724. float EntityManager::GetEncounterRate(){return encounter_rate_;}
  725. void EntityManager::HideFieldEntities(){
  726. entities_hidden_by_battle_.clear();
  727. for (Entity* entity : entity_){
  728. if (entity->IsVisible()){
  729. entities_hidden_by_battle_.push_back(entity);
  730. entity->SetVisible(false);
  731. }
  732. }
  733. }
  734. void EntityManager::ShowFieldEntities(){
  735. // Only the ones that still exist: the field could have been changed during the battle.
  736. for (Entity* hidden : entities_hidden_by_battle_){
  737. if (std::find(entity_.begin(), entity_.end(), hidden) != entity_.end()) hidden->SetVisible(true);
  738. }
  739. entities_hidden_by_battle_.clear();
  740. }
  741. void EntityManager::ClearEncounterTables(){
  742. encounter_tables_.clear();
  743. encounter_table_ = 1;
  744. encounter_distance_ = 0.0f;
  745. encounter_last_position_valid_ = false;
  746. encounter_accumulator_ = 0;
  747. encounter_last_battle_ = -1;
  748. }
  749. void EntityManager::AddEncounterTable(
  750. const int id, const int rate, const std::vector<std::pair<int, int>>& battles,
  751. const std::vector<std::pair<int, int>>& specials
  752. ){
  753. EncounterTable table;
  754. table.rate = rate;
  755. table.battles = battles;
  756. table.specials = specials;
  757. encounter_tables_[id] = table;
  758. }
  759. void EntityManager::ScriptSetEncounterTable(const int id){encounter_table_ = id;}
  760. int EntityManager::ScriptRollEncounterBattle() const{
  761. auto found = encounter_tables_.find(encounter_table_);
  762. if (found == encounter_tables_.end()) return -1;
  763. const EncounterTable& table = found->second;
  764. // Like the original game: a roll out of 64 is checked against the special battles one after
  765. // the other (each one adds its probability). If none is hit, a new roll chooses a standard
  766. // battle, the last one being the default.
  767. int roll = std::rand() % 64;
  768. int sum = 0;
  769. for (const auto& special : table.specials){
  770. sum += special.second;
  771. if (roll < sum) return special.first;
  772. }
  773. if (table.battles.empty()) return -1;
  774. roll = std::rand() % 64;
  775. sum = 0;
  776. for (const auto& battle : table.battles){
  777. sum += battle.second;
  778. if (roll < sum) return battle.first;
  779. }
  780. return table.battles.back().first;
  781. }
  782. void EntityManager::UpdateEncounters(const bool walked){
  783. if (player_entity_ == nullptr){
  784. encounter_last_position_valid_ = false;
  785. return;
  786. }
  787. const Ogre::Vector3 position = player_entity_->GetPosition();
  788. const float distance
  789. = encounter_last_position_valid_ ? (position - encounter_last_position_).length() : 0.0f;
  790. encounter_last_position_ = position;
  791. encounter_last_position_valid_ = true;
  792. // Only walking counts: not scripted movements, and not jumps to another place.
  793. if (!walked || player_lock_ || distance > 2.0f) return;
  794. if (!random_encounters_ || encounter_tables_.empty()) return;
  795. auto found = encounter_tables_.find(encounter_table_);
  796. if (found == encounter_tables_.end()) return;
  797. const float multiplier = cv_encounter_multiplier.GetF();
  798. if (multiplier <= 0.0f) return;
  799. // Same as the original game. Every few frames of walking the accumulator grows by a value
  800. // divided by the rate (the lower the rate, the more often the encounters), and an encounter
  801. // starts if a random number is lower than the accumulator.
  802. const int rate = found->second.rate;
  803. if (rate <= 0) return;
  804. encounter_distance_ += Timer::getSingleton().GetGameTimeDelta() * 30.0f;
  805. while (encounter_distance_ >= ENCOUNTER_CHECK_FRAMES){
  806. encounter_distance_ -= ENCOUNTER_CHECK_FRAMES;
  807. encounter_accumulator_ += static_cast<int>(ENCOUNTER_STEP_VALUE * multiplier * 256.0f) / rate;
  808. const int roll = std::rand() % 256 * 256;
  809. if (cv_debug_encounters.GetB()){
  810. LOG_TRIVIAL(
  811. "Encounter check: rate " + std::to_string(rate) + ", accumulator "
  812. + std::to_string(encounter_accumulator_ / 256.0f) + "/256, roll "
  813. + std::to_string(roll / 256)
  814. );
  815. }
  816. if (roll >= encounter_accumulator_) continue;
  817. int battle = ScriptRollEncounterBattle();
  818. // The same battle is not repeated: it's rolled again once.
  819. if (battle == encounter_last_battle_) battle = ScriptRollEncounterBattle();
  820. if (battle < 0) return;
  821. encounter_distance_ = 0.0f;
  822. encounter_accumulator_ = 0;
  823. encounter_last_battle_ = battle;
  824. LOG_TRIVIAL("Random encounter: battle " + std::to_string(battle));
  825. // The battle is started by a script, as it has to change the module of every manager.
  826. ScriptManager::getSingleton().RunString("Encounter.start(" + std::to_string(battle) + ")");
  827. return;
  828. }
  829. }
  830. void EntityManager::SetEncounterRate(float rate){
  831. if (rate >= 1.0f) encounter_rate_ = 1.0f;
  832. else if (rate <= 0.0f) encounter_rate_ = 0.0f;
  833. else encounter_rate_ = rate;
  834. }
  835. bool EntityManager::IsKeyOn(unsigned int key_code){
  836. // If the player is locked, always false.
  837. // TODO: Maybe this will interfere with something down the road?
  838. // Some scene where the player can't move but must press a button?
  839. if (player_lock_) return false;
  840. // The key code of the scripts is a mask of the pad buttons, and the check is true if any of
  841. // them is pressed. Translate each to its key. For example 32 is "Circle/Action", the space bar.
  842. // TODO: Get values from config.lua
  843. static const struct{unsigned int mask; int key;} KEYS[] = {
  844. {4, OIS::KC_PGUP}, {8, OIS::KC_PGDOWN}, {32, OIS::KC_SPACE}, {64, OIS::KC_ESCAPE},
  845. {4096, OIS::KC_UP}, {8192, OIS::KC_RIGHT}, {16384, OIS::KC_DOWN}, {32768, OIS::KC_LEFT}
  846. };
  847. for (const auto& entry : KEYS)
  848. if ((key_code & entry.mask) && InputManager::getSingleton().IsButtonPressed(entry.key))
  849. return true;
  850. return false;
  851. }
  852. bool EntityManager::IsKeyOff(unsigned int key_code){return !IsKeyOn(key_code);}
  853. void EntityManager::SetEntityToCharacter(const char* entity_name, unsigned int char_id){
  854. if (IsBattleModule()){
  855. for (unsigned int i = 0; i < battle_entity_.size(); ++ i){
  856. if (battle_entity_[i]->GetName() == entity_name){
  857. battle_entity_[i]->SetCharacter(char_id);
  858. return;
  859. }
  860. }
  861. }
  862. else{
  863. for (unsigned int i = 0; i < entity_.size(); ++ i){
  864. if (entity_[i]->GetName() == entity_name){
  865. entity_[i]->SetCharacter(char_id);
  866. return;
  867. }
  868. }
  869. }
  870. LOG_WARNING(
  871. "Unabe to assigne character ID " + std::to_string(char_id) + " to entity '" + entity_name
  872. + "'. No entity by that name exists."
  873. );
  874. }
  875. bool EntityManager::SetEntityOnWalkmesh(Entity* entity){
  876. if (IsBattleModule()){
  877. LOG_WARNING("Tried to set entity on the walkmesh, but the EntityManager is in battle mode");
  878. return false;
  879. }
  880. Ogre::Vector3 position3 = entity->GetPosition();
  881. Ogre::Vector2 position2;
  882. position2.x = position3.x;
  883. position2.y = position3.y;
  884. std::vector<std::pair<int, float>> triangles;
  885. // Search for possible triangles.
  886. for (int i = 0; i < walkmesh_.GetNumberOfTriangles(); ++ i){
  887. Ogre::Vector3 A3 = walkmesh_.GetA(i);
  888. Ogre::Vector3 B3 = walkmesh_.GetB(i);
  889. Ogre::Vector3 C3 = walkmesh_.GetC(i);
  890. Ogre::Vector2 A2(A3.x, A3.y);
  891. Ogre::Vector2 B2(B3.x, B3.y);
  892. Ogre::Vector2 C2(C3.x, C3.y);
  893. if (Ogre::Math::pointInTri2D(position2, A2, B2, C2) == true){
  894. triangles.push_back(std::make_pair(i, PointElevation(
  895. position2, walkmesh_.GetA(i), walkmesh_.GetB(i), walkmesh_.GetC(i)
  896. )));
  897. }
  898. }
  899. // If the coordinates doesn't match any triangle, exit.
  900. if (triangles.size() == 0 && entity == player_entity_){
  901. LOG_ERROR(
  902. "Can't find any triangle to place entity '" + entity->GetName() + "' on walkmesh."
  903. );
  904. return false;
  905. }
  906. // From the possible triangles, select the closest one (on the Z edge).
  907. int closest_z_triangle = -1;
  908. float closest_z_distance = 9999.0f;
  909. float pos_z = position3.z;
  910. for (size_t i = 0; i < triangles.size(); ++ i){
  911. if (std::abs(pos_z - triangles[i].second) < closest_z_distance){
  912. closest_z_triangle = triangles[i].first;
  913. closest_z_distance = std::abs(pos_z - triangles[i].second);
  914. }
  915. }
  916. if (closest_z_triangle == -1 && entity == player_entity_){
  917. LOG_ERROR(
  918. "Can't find nearby triangle to place entity '" + entity->GetName() + "' on walkmesh."
  919. );
  920. return false;
  921. }
  922. entity->SetPosition(Ogre::Vector3(position2.x, position2.y, pos_z));
  923. entity->SetMoveTriangleId(closest_z_triangle);
  924. return true;
  925. }
  926. float EntityManager::TriggerReach(float solid_radius){
  927. // The radius an entity has when a script doesn't change it.
  928. const float default_radius = 0.21f;
  929. return solid_radius < 0.01f ? default_radius : solid_radius;
  930. }
  931. bool EntityManager::WalkmeshEdgeBlocks(bool is_player, bool scripted_move){
  932. return is_player || !scripted_move;
  933. }
  934. bool EntityManager::IsBetterInteractionTarget(
  935. float angle, float distance, float best_angle, float best_distance
  936. ){
  937. const float same_angle = 0.001f;
  938. if (angle < best_angle - same_angle) return true;
  939. return std::fabs(angle - best_angle) <= same_angle && distance < best_distance;
  940. }
  941. float EntityManager::ScriptedMoveStep(float distance, float stop_distance, float max_step){
  942. return std::min(max_step, std::max(0.0f, distance - stop_distance));
  943. }
  944. bool EntityManager::PerformWalkmeshMove(Entity* entity, const float speed, const float delta_time){
  945. if (IsBattleModule()){
  946. LOG_WARNING("Tried to perform a walkmesh move, but the EntityManager is in battle mode");
  947. return false;
  948. }
  949. Ogre::Vector3 start_point = entity->GetPosition();
  950. Ogre::Vector3 move_vector = entity->GetMovePosition() - start_point;
  951. Ogre::Vector2 direction(move_vector.x, move_vector.y);
  952. const float distance_to_target = direction.length();
  953. const bool scripted_move = entity->IsScriptedMove();
  954. const float stop_distance = entity->GetMoveStopDistance();
  955. if (scripted_move && distance_to_target <= stop_distance + 0.0001f){
  956. entity->UnsetMove();
  957. return false;
  958. }
  959. direction.normalise();
  960. direction *= delta_time;
  961. direction *= speed;
  962. if (scripted_move && !direction.isZeroLength()){
  963. direction.normalise();
  964. direction *= ScriptedMoveStep(
  965. distance_to_target, stop_distance, speed * delta_time
  966. );
  967. }
  968. // If movement is still needed but speed or time don't allow it, just return for now.
  969. if (direction.isZeroLength() == true){
  970. if (player_entity_ == entity) CheckTriggers(entity, entity->GetPosition());
  971. return false;
  972. }
  973. int current_triangle = entity->GetMoveTriangleId();
  974. if (current_triangle == -1){
  975. LOG_ERROR("Entity '" + entity->GetName() + "' not placed on walkmesh and can't move.");
  976. return false;
  977. }
  978. Ogre::Vector3 rotation(0.0f, 0.0f, 0.0f);
  979. Ogre::Quaternion q1(0.0f, 0.0f, 0.0f, 1.0f);
  980. Ogre::Vector3 end_point(0.0f, 0.0f, 0.0f);
  981. Ogre::Vector3 end_point2(0.0f, 0.0f, 0.0f);
  982. bool first_triangle_check = false;
  983. bool second_triangle_check = false;
  984. bool third_triangle_check = false;
  985. bool last_triangle_check = false;
  986. bool first_entity_check = false;
  987. bool second_entity_check = false;
  988. bool third_entity_check = false;
  989. // Shorten move vector by triangle angle
  990. end_point.x = start_point.x + direction.x;
  991. end_point.y = start_point.y + direction.y;
  992. Ogre::Vector3 A3 = walkmesh_.GetA(current_triangle);
  993. Ogre::Vector3 B3 = walkmesh_.GetB(current_triangle);
  994. Ogre::Vector3 C3 = walkmesh_.GetC(current_triangle);
  995. end_point.z = PointElevation(Ogre::Vector2(end_point.x, end_point.y), A3, B3, C3);
  996. Ogre::Vector3 temp = end_point - start_point;
  997. temp.normalise();
  998. temp = temp * direction.length();
  999. direction.x = temp.x;
  1000. direction.y = temp.y;
  1001. // Set the direction for the entity if it has to move.
  1002. if (entity->GetMoveAutoRotation() == true){
  1003. Ogre::Vector2 up(0.0f, -1.0f);
  1004. // Angle between vectors
  1005. Ogre::Degree angle(Ogre::Radian(acosf(
  1006. direction.dotProduct(up) / (direction.length() * up.length())
  1007. )));
  1008. angle = (direction.x < 0) ? Ogre::Degree(360) - angle : angle;
  1009. entity->SetRotation(Ogre::Degree(angle));
  1010. }
  1011. float solid = (entity->IsSolid() == true) ? entity->GetSolidRadius() : 0.01f;
  1012. // For not playable entities, be a bit more lenient with solid collisions.
  1013. // Don't use the solid radius.
  1014. if (entity != player_entity_) solid /= 10;
  1015. // Get ending point.
  1016. end_point.z = start_point.z;
  1017. for (
  1018. int i = 0;
  1019. (entity == player_entity_ && i < 16)
  1020. || (entity == player_entity_ && i < 3)
  1021. || (entity != player_entity_ && i <= 16);
  1022. ++ i
  1023. ){
  1024. end_point = Ogre::Vector3(
  1025. start_point.x + direction.x, start_point.y + direction.y, start_point.z
  1026. );
  1027. // 1st check.
  1028. // Rotate move_vector +45.
  1029. q1.FromAngleAxis(Ogre::Radian(Ogre::Degree(45)), Ogre::Vector3::UNIT_Z);
  1030. rotation.x = direction.x;
  1031. rotation.y = direction.y;
  1032. rotation.z = 0;
  1033. rotation.normalise();
  1034. rotation = q1 * rotation;
  1035. // Multiply move_vector by solid range
  1036. rotation = rotation * solid;
  1037. end_point2.x = end_point.x + rotation.x;
  1038. end_point2.y = end_point.y + rotation.y;
  1039. // Check_triangle.
  1040. first_triangle_check = WalkmeshBorderCross(entity, end_point2, direction);
  1041. entity->SetMoveTriangleId(current_triangle);
  1042. // Model check.
  1043. first_entity_check = CheckSolidCollisions(entity, end_point2);
  1044. // 2nd check.
  1045. // Rotate move_vector +45.
  1046. q1.FromAngleAxis(Ogre::Radian(Ogre::Degree(-45)), Ogre::Vector3::UNIT_Z);
  1047. rotation.x = direction.x;
  1048. rotation.y = direction.y;
  1049. rotation.z = 0;
  1050. rotation.normalise();
  1051. rotation = q1 * rotation;
  1052. // Multiply move_vector by solid range.
  1053. rotation = rotation * solid;
  1054. end_point2.x = end_point.x + rotation.x;
  1055. end_point2.y = end_point.y + rotation.y;
  1056. // Check triangle.
  1057. second_triangle_check = WalkmeshBorderCross(entity, end_point2, direction);
  1058. entity->SetMoveTriangleId(current_triangle);
  1059. // Model check.
  1060. second_entity_check = CheckSolidCollisions(entity, end_point2);
  1061. // 3rd check.
  1062. rotation.x = direction.x;
  1063. rotation.y = direction.y;
  1064. rotation.z = 0;
  1065. rotation.normalise();
  1066. rotation = rotation * solid;
  1067. end_point2.x = end_point.x + rotation.x;
  1068. end_point2.y = end_point.y + rotation.y;
  1069. // Check triangle.
  1070. third_triangle_check = WalkmeshBorderCross(entity, end_point2, direction);
  1071. entity->SetMoveTriangleId(current_triangle);
  1072. // Model check.
  1073. third_entity_check = CheckSolidCollisions(entity, end_point2);
  1074. // Check condition and modify move_vector.
  1075. if (
  1076. first_triangle_check != false || second_triangle_check != false
  1077. || third_triangle_check != false || first_entity_check != false
  1078. || second_entity_check != false || third_entity_check != false
  1079. ){
  1080. // Only for NPC.
  1081. if (entity != player_entity_){
  1082. // If the entity collides only directly into a triangle border.
  1083. if (
  1084. first_triangle_check == false
  1085. && second_triangle_check == false
  1086. && third_triangle_check != false
  1087. ){
  1088. q1.FromAngleAxis(Ogre::Radian(Ogre::Degree(-7)), Ogre::Vector3::UNIT_Z);
  1089. rotation.x = direction.x;
  1090. rotation.y = direction.y;
  1091. rotation = q1 * rotation;
  1092. direction.x = rotation.x;
  1093. direction.y = rotation.y;
  1094. }
  1095. // Or if the entity only collides into other entity directly.
  1096. else if (
  1097. first_entity_check == false && second_entity_check == false
  1098. && third_entity_check != false
  1099. ){
  1100. q1.FromAngleAxis(Ogre::Radian(Ogre::Degree(-11.25f)), Ogre::Vector3::UNIT_Z);
  1101. rotation.x = direction.x;
  1102. rotation.y = direction.y;
  1103. rotation = q1 * rotation;
  1104. direction.x = rotation.x;
  1105. direction.y = rotation.y;
  1106. }
  1107. // If not both left and right check failed.
  1108. if (first_triangle_check == false || second_triangle_check == false){
  1109. if (
  1110. (first_triangle_check == false && first_entity_check != false)
  1111. || (first_triangle_check != false && second_triangle_check == false)
  1112. ){
  1113. q1.FromAngleAxis(
  1114. Ogre::Radian(Ogre::Degree(-11.25f)), Ogre::Vector3::UNIT_Z
  1115. );
  1116. rotation.x = direction.x;
  1117. rotation.y = direction.y;
  1118. rotation = q1 * rotation;
  1119. direction.x = rotation.x;
  1120. direction.y = rotation.y;
  1121. }
  1122. if (
  1123. first_triangle_check == false
  1124. && first_entity_check == false
  1125. && (second_triangle_check != false || second_entity_check != false
  1126. )
  1127. ){
  1128. q1.FromAngleAxis(Ogre::Radian(Ogre::Degree(11.25f)), Ogre::Vector3::UNIT_Z);
  1129. rotation.x = direction.x;
  1130. rotation.y = direction.y;
  1131. rotation = q1 * rotation;
  1132. direction.x = rotation.x;
  1133. direction.y = rotation.y;
  1134. }
  1135. continue;
  1136. }
  1137. break;
  1138. }
  1139. // Only for playable character.
  1140. else{
  1141. if (
  1142. first_entity_check == false
  1143. && second_entity_check == false
  1144. && third_entity_check == false
  1145. ){
  1146. // If not both left and right check failed
  1147. if (first_triangle_check == false || second_triangle_check == false){
  1148. if (first_triangle_check != false && second_triangle_check == false){
  1149. q1.FromAngleAxis(
  1150. Ogre::Radian(Ogre::Degree(-11.25f)), Ogre::Vector3::UNIT_Z
  1151. );
  1152. rotation.x = direction.x;
  1153. rotation.y = direction.y;
  1154. rotation = q1 * rotation;
  1155. direction.x = rotation.x;
  1156. direction.y = rotation.y;
  1157. }
  1158. if (first_triangle_check == false && second_triangle_check != false){
  1159. q1.FromAngleAxis(
  1160. Ogre::Radian(Ogre::Degree(11.25f)), Ogre::Vector3::UNIT_Z
  1161. );
  1162. rotation.x = direction.x;
  1163. rotation.y = direction.y;
  1164. rotation = q1 * rotation;
  1165. direction.x = rotation.x;
  1166. direction.y = rotation.y;
  1167. }
  1168. continue;
  1169. }
  1170. break;
  1171. }
  1172. }
  1173. }
  1174. }
  1175. // Set new X, Y and Z
  1176. last_triangle_check = WalkmeshBorderCross(entity, end_point, direction);
  1177. if (
  1178. first_triangle_check != false || second_triangle_check != false
  1179. || third_triangle_check != false || last_triangle_check != false
  1180. || first_entity_check != false || second_entity_check != false
  1181. || third_entity_check != false
  1182. ){
  1183. // Event if the entity can't move, check for triggers, but use current position.
  1184. if (player_entity_ == entity) CheckTriggers(entity, entity->GetPosition());
  1185. return false;
  1186. }
  1187. // Check triggers before set entity position because move line is checked.
  1188. if (player_entity_ == entity) CheckTriggers(entity, end_point);
  1189. entity->SetPosition(end_point);
  1190. // If the entity has come to destination point, finish movement.
  1191. Ogre::Vector3 final = entity->GetMovePosition() - end_point;
  1192. const float tolerance = scripted_move ? 0.0001f : speed / 10;
  1193. if (Ogre::Vector2(final.x, final.y).length() <= stop_distance + tolerance)
  1194. entity->UnsetMove();
  1195. return true;
  1196. }
  1197. bool EntityManager::WalkmeshBorderCross(
  1198. Entity* entity, Ogre::Vector3& position, const Ogre::Vector2& move_vector
  1199. ){
  1200. if (IsBattleModule()){
  1201. LOG_WARNING("Tried to check for walkmesh cross, but the EntityManager is in battle mode");
  1202. return false;
  1203. }
  1204. int current_triangle = entity->GetMoveTriangleId();
  1205. if (current_triangle == -1) return true;
  1206. const bool free_walk = !WalkmeshEdgeBlocks(entity == player_entity_, entity->IsScriptedMove());
  1207. Ogre::Vector2 pos = Ogre::Vector2(position.x, position.y);
  1208. for (;;){
  1209. Ogre::Vector3 A3 = walkmesh_.GetA(current_triangle);
  1210. Ogre::Vector3 B3 = walkmesh_.GetB(current_triangle);
  1211. Ogre::Vector3 C3 = walkmesh_.GetC(current_triangle);
  1212. Ogre::Vector2 A(A3.x, A3.y);
  1213. Ogre::Vector2 B(B3.x, B3.y);
  1214. Ogre::Vector2 C(C3.x, C3.y);
  1215. float sign1 = SideOfVector(pos, B, A);
  1216. float sign2 = SideOfVector(pos, C, B);
  1217. float sign3 = SideOfVector(pos, A, C);
  1218. int next_triangle = -1;
  1219. if (sign1 < 0) next_triangle = walkmesh_.GetAccessSide(current_triangle, 0);
  1220. else if (sign2 < 0) next_triangle = walkmesh_.GetAccessSide(current_triangle, 1);
  1221. else if (sign3 < 0) next_triangle = walkmesh_.GetAccessSide(current_triangle, 2);
  1222. else{
  1223. position.z = PointElevation(pos, A3, B3, C3);
  1224. entity->SetMoveTriangleId(current_triangle);
  1225. return false;
  1226. }
  1227. if (next_triangle >= 0){
  1228. bool lock = walkmesh_.IsLocked(next_triangle);
  1229. if (lock == false || free_walk){
  1230. current_triangle = next_triangle;
  1231. continue;
  1232. }
  1233. }
  1234. position.z = PointElevation(pos, A3, B3, C3);
  1235. entity->SetMoveTriangleId(current_triangle);
  1236. // A walk of a script goes on over the edge, keeping the last triangle.
  1237. return !free_walk;
  1238. }
  1239. }
  1240. bool EntityManager::CheckSolidCollisions(Entity* entity, Ogre::Vector3& position){
  1241. if (IsBattleModule()){
  1242. LOG_WARNING("Tried to check for solid collisions, but the EntityManager is in battle mode");
  1243. return false;
  1244. }
  1245. if (entity->IsSolid() == false) return false;
  1246. for (size_t i = 0; i < entity_.size(); ++ i){
  1247. if (entity_[i]->IsSolid() == false) continue;
  1248. if (entity_[i] == entity) continue;
  1249. Ogre::Vector3 pos1 = entity_[i]->GetPosition();
  1250. float solid_range = entity_[i]->GetSolidRadius();
  1251. solid_range *= solid_range;
  1252. float height = (pos1.z < position.z) ? entity_[i]->GetHeight() : entity->GetHeight();
  1253. if (
  1254. ((pos1.z - position.z + height) < (height * 2)) && ((pos1.z - position.z + height) >= 0)
  1255. ){
  1256. float distance
  1257. = (pos1.x - position.x) * (pos1.x - position.x)
  1258. + (pos1.y - position.y) * (pos1.y - position.y);
  1259. if (distance < solid_range) return true;
  1260. }
  1261. }
  1262. return false;
  1263. }
  1264. void EntityManager::CheckTriggers(Entity* entity, const Ogre::Vector3& position){
  1265. if (IsBattleModule()){
  1266. LOG_WARNING("Tried to check for triggers, but the EntityManager is in battle mode");
  1267. return;
  1268. }
  1269. if (entity->IsSolid() == false) return;
  1270. if (player_entity_ != entity) return;
  1271. if (player_entity_->IsSolid() == false) return;
  1272. if (player_lock_) return;
  1273. bool is_moving = true;
  1274. if (
  1275. std::fabs(entity->GetPosition().x - position.x) < 0.005f
  1276. && std::fabs(entity->GetPosition().y - position.y) < 0.005f
  1277. && std::fabs(entity->GetPosition().z - position.z) < 0.005f
  1278. ){
  1279. is_moving = false;
  1280. }
  1281. for (unsigned int i = 0; i < entity_triggers_.size(); ++ i){
  1282. ScriptEntity* trigger = ScriptManager::getSingleton().GetScriptEntityByName(
  1283. ScriptManager::ENTITY, entity_triggers_[i]->GetName()
  1284. );
  1285. if (trigger != nullptr && entity_triggers_[i]->IsEnabled() == true){
  1286. Ogre::Vector3 point_a = entity_triggers_[i]->GetPoint1();
  1287. Ogre::Vector3 point_b = entity_triggers_[i]->GetPoint2();
  1288. Ogre::Vector3 entity_position = entity->GetPosition();
  1289. Ogre::Vector3 proj;
  1290. // Calculate distance
  1291. float square_dist = SquareDistanceToLine(entity_position, point_a, point_b, proj);
  1292. float solid_radius = TriggerReach(entity->GetSolidRadius());
  1293. // Calculate direction.
  1294. const Ogre::Degree direction_to_line = GetDirectionToPoint(entity_position, proj);
  1295. const Ogre::Degree destination_direction_to_line = GetDirectionToPoint(position, proj);
  1296. const Ogre::Degree movement_direction = entity->GetRotation();
  1297. Ogre::Degree angle = direction_to_line - movement_direction + Ogre::Degree(90);
  1298. angle = (angle > Ogre::Degree(360)) ? angle - Ogre::Degree(360) : angle;
  1299. // Check on enter line.
  1300. // Must happen only once when the solid radius touches the line, while facing the line,
  1301. // while the entity is manually moving, and only if not already triggered.
  1302. if (
  1303. square_dist != -1 && square_dist <= solid_radius * solid_radius
  1304. && angle < Ogre::Degree(180) && angle > Ogre::Degree(0)
  1305. && entity_triggers_[i]->CheckApproached() == false
  1306. && is_moving
  1307. ){
  1308. entity_triggers_[i]->SetApproached(true);
  1309. //std::cout << " [TRIGGER] " << entity->GetName() << " on_approach "
  1310. // << entity_triggers_[i]->GetName() << std::endl;
  1311. ScriptManager::getSingleton().ScriptRequest(
  1312. trigger, "on_approach", 1, entity->GetName(), "", false, false
  1313. );
  1314. }
  1315. // Check on move to line.
  1316. // Must happen only once when the entity center touches the line, if the entity has
  1317. // previously entered the line.
  1318. if (
  1319. square_dist != -1 && square_dist < 1
  1320. && entity_triggers_[i]->CheckCrossed() == false
  1321. && entity_triggers_[i]->CheckApproached() == true
  1322. ){
  1323. entity_triggers_[i]->SetCrossed(true);
  1324. //std::cout << " [TRIGGER] " << entity->GetName() << " on_cross "
  1325. // << entity_triggers_[i]->GetName() << std::endl;
  1326. ScriptManager::getSingleton().ScriptRequest(
  1327. trigger, "on_cross", 1, entity->GetName(), "", false, false
  1328. );
  1329. }
  1330. // Check on cross line.
  1331. // Must happen on every frame while the entity solid radius collides with the line, if
  1332. // the entity has previously entered the line.
  1333. if (
  1334. square_dist != -1 && square_dist <= solid_radius * solid_radius
  1335. && entity_triggers_[i]->CheckApproached() == true
  1336. ){
  1337. // TODO: Fix this hack.
  1338. // After the on_near script is run, set a cooltime of 5 frames before running
  1339. // it again. This is because most of the time this trigger checks for a key, and
  1340. // if its pressed, is run multiple times.
  1341. if (entity_triggers_[i]->GetNearEventCooldown() == 0){
  1342. //std::cout << " [TRIGGER] " << entity->GetName()
  1343. // << " on_near " << entity_triggers_[i]->GetName() << std::endl;
  1344. ScriptManager::getSingleton().ScriptRequest(
  1345. trigger, "on_near", 1, entity->GetName(), "", true, true
  1346. );
  1347. entity_triggers_[i]->ResetNearEventCooldown();
  1348. }
  1349. else entity_triggers_[i]->DecreaseNearEventCooldown();
  1350. }
  1351. // Check on cross line once.
  1352. // The same as on cross line, but only once.
  1353. if (
  1354. square_dist != -1 && square_dist < solid_radius * solid_radius
  1355. && entity_triggers_[i]->CheckNearSingleEventTriggered() == false
  1356. && entity_triggers_[i]->CheckApproached() == true
  1357. ){
  1358. entity_triggers_[i]->SetNearSingleEventTriggered(true);
  1359. //std::cout << " [TRIGGER] " << entity->GetName()
  1360. // << " on_near_once " << entity_triggers_[i]->GetName() << std::endl;
  1361. ScriptManager::getSingleton().ScriptRequest(
  1362. trigger, "on_near_once", 1, entity->GetName(), "", false, false
  1363. );
  1364. }
  1365. // Check on leave line.
  1366. // Must be triggered once when the line is not in the entity solid radius, but only if
  1367. // on enter script has been triggered previously.
  1368. if (
  1369. square_dist != -1 && square_dist > solid_radius * solid_radius
  1370. && entity_triggers_[i]->CheckApproached()
  1371. ){
  1372. //std::cout << " [TRIGGER] " << entity->GetName() << " on_leave "
  1373. // << entity_triggers_[i]->GetName() << std::endl;
  1374. ScriptManager::getSingleton().ScriptRequest(
  1375. trigger, "on_leave", 1, entity->GetName(), "", false, false
  1376. );
  1377. }
  1378. // If the entity is far enough from the line, mark every trigger as inactive
  1379. if (square_dist == -1 || square_dist > solid_radius * solid_radius){
  1380. entity_triggers_[i]->Clear();
  1381. //std::cout << " [TRIGGER] " << entity->GetName() << " clear of "
  1382. // << entity_triggers_[i]->GetName() << std::endl;
  1383. }
  1384. }
  1385. }
  1386. }
  1387. void EntityManager::CheckEntityInteract(){
  1388. if (IsBattleModule()){
  1389. LOG_WARNING(
  1390. "Tried to check for entity interaction, but the EntityManager is in battle mode"
  1391. );
  1392. return;
  1393. }
  1394. if (player_entity_ == NULL || player_lock_ == true || player_entity_->IsSolid() == false)
  1395. return;
  1396. Ogre::Degree angle_pc = player_entity_->GetRotation();
  1397. Entity* entity_to_interact = NULL;
  1398. Ogre::Degree less_angle(90);
  1399. float best_distance = std::numeric_limits<float>::max();
  1400. Ogre::Real nearest_worldmap_distance = -1.0f;
  1401. for (unsigned int i = 0; i < entity_.size(); ++ i){
  1402. if (entity_[i]->IsTalkable() == false) continue;
  1403. if (entity_[i] == player_entity_) continue;
  1404. Ogre::Vector3 pos1 = entity_[i]->GetPosition();
  1405. float interact_range = entity_[i]->GetTalkRadius();
  1406. Ogre::Vector3 pos2 = player_entity_->GetPosition();
  1407. float solid_range = player_entity_->GetSolidRadius();
  1408. if (IsWorldModule()){
  1409. const Ogre::Real target_bottom = pos1.y;
  1410. const Ogre::Real player_bottom = pos2.y;
  1411. const Ogre::Real talk_range
  1412. = interact_range + solid_range + 0.05f;
  1413. const Ogre::Real distance
  1414. = (pos1.x - pos2.x) * (pos1.x - pos2.x)
  1415. + (pos1.z - pos2.z) * (pos1.z - pos2.z);
  1416. const bool height_overlap
  1417. = player_bottom <= target_bottom + entity_[i]->GetHeight()
  1418. && player_bottom + player_entity_->GetHeight() >= target_bottom;
  1419. if (
  1420. height_overlap && distance <= talk_range * talk_range
  1421. && (nearest_worldmap_distance < 0 || distance < nearest_worldmap_distance)
  1422. ){
  1423. entity_to_interact = entity_[i];
  1424. nearest_worldmap_distance = distance;
  1425. }
  1426. continue;
  1427. }
  1428. int height = (pos1.z < pos2.z) ? entity_[i]->GetHeight() : player_entity_->GetHeight();
  1429. if (((pos1.z - pos2.z + height) < (height * 2)) && ((pos1.z - pos2.z + height) >= 0)){
  1430. interact_range = (interact_range + solid_range) * (interact_range + solid_range);
  1431. float distance
  1432. = (pos1.x - pos2.x) * (pos1.x - pos2.x) + (pos1.y - pos2.y) * (pos1.y - pos2.y);
  1433. if (distance < interact_range + solid_range){
  1434. Ogre::Degree angle_to_model = GetDirectionToPoint(pos2, pos1);
  1435. Ogre::Degree angle = angle_pc - angle_to_model;
  1436. angle = (angle < Ogre::Degree(0)) ? -angle : angle;
  1437. angle = (angle >= Ogre::Degree(180)) ? Ogre::Degree(360) - angle : angle;
  1438. // With several entities in range, interact with the one the player faces most.
  1439. // Nobody 90 degrees or more away from where the player faces is considered.
  1440. if (
  1441. angle < Ogre::Degree(90) && IsBetterInteractionTarget(
  1442. angle.valueDegrees(), distance, less_angle.valueDegrees(), best_distance
  1443. )
  1444. ){
  1445. less_angle = angle;
  1446. best_distance = distance;
  1447. entity_to_interact = entity_[i];
  1448. }
  1449. }
  1450. }
  1451. }
  1452. if (entity_to_interact != NULL && entity_to_interact->IsLine() == false){
  1453. Ogre::String name = entity_to_interact->GetName();
  1454. ScriptEntity* scr_entity = ScriptManager::getSingleton().GetScriptEntityByName(
  1455. ScriptManager::ENTITY, name
  1456. );
  1457. bool added = ScriptManager::getSingleton().ScriptRequest(
  1458. scr_entity, "on_interact", 1, "", "", false, false
  1459. );
  1460. if (added == false){
  1461. LOG_WARNING("Script 'on_interact' for entity '" + name + "' doesn't exist.");
  1462. }
  1463. }
  1464. }
  1465. void EntityManager::SetNextOffsetStep(Entity* entity){
  1466. // TODO: Allow this while on battle mode?
  1467. ActionType type = entity->GetOffsetType();
  1468. float total = entity->GetOffsetSeconds();
  1469. float current = entity->GetOffsetCurrentSeconds();
  1470. current += Timer::getSingleton().GetGameTimeDelta();
  1471. current = (current > total) ? total : current;
  1472. Ogre::Vector3 start = entity->GetOffsetPositionStart();
  1473. Ogre::Vector3 end = entity->GetOffsetPositionEnd();
  1474. // TODO: Prevent division by 0:
  1475. float x = current / total;
  1476. if (std::isnan(x)) x = 0.0f;
  1477. float smooth_modifier = (type == AT_SMOOTH) ? -2 * x * x * x + 3 * x * x : x;
  1478. Ogre::Vector3 offset = start + (end - start) * smooth_modifier;
  1479. entity->SetOffset(offset);
  1480. if (current == total) entity->UnsetOffset();
  1481. else entity->SetOffsetCurrentSeconds(current);
  1482. }
  1483. void EntityManager::SetNextTurnStep(Entity* entity){
  1484. // TODO: Allow this while on battle mode?
  1485. ActionType type = entity->GetTurnType();
  1486. float total = entity->GetTurnSeconds();
  1487. float current = entity->GetTurnCurrentSeconds();
  1488. current += Timer::getSingleton().GetGameTimeDelta();
  1489. current = (current > total) ? total : current;
  1490. Ogre::Degree start = entity->GetTurnDirectionStart();
  1491. Ogre::Degree end = entity->GetTurnDirectionEnd();
  1492. // TODO: Prevent division by 0:
  1493. float x = current / total;
  1494. float smooth_modifier = (type == AT_SMOOTH) ? -2 * x * x * x + 3 * x * x : x;
  1495. Ogre::Degree rotation = start + (end - start) * smooth_modifier;
  1496. entity->SetRotation(rotation);
  1497. if (current == total) entity->UnsetTurn();
  1498. else entity->SetTurnCurrentSeconds(current);
  1499. }
  1500. void EntityManager::SetNextLinearStep(Entity* entity){
  1501. if (IsBattleModule()){
  1502. LOG_WARNING("Tried to set next linear step, but the EntityManager is in battle mode");
  1503. return;
  1504. }
  1505. bool to_end = true;
  1506. bool is_move = false;
  1507. Ogre::Vector3 start = entity->GetLinearStart();
  1508. Ogre::Vector3 end = entity->GetLinearEnd();
  1509. Ogre::Vector3 current = entity->GetPosition();
  1510. float delta = Timer::getSingleton().GetGameTimeDelta();
  1511. // The player climbs a ladder with the directional keys. While a scene has the player locked
  1512. // the keys have no effect, and the scene makes the player move on its own, as with any other
  1513. // entity.
  1514. if (entity == player_entity_ && !player_lock_){
  1515. LinearMovement move = entity->GetLinearMovement();
  1516. if (move == LM_DOWN_TO_UP || move == LM_UP_TO_DOWN){
  1517. if (player_move_.y > 0){ // move up
  1518. to_end = (move == LM_DOWN_TO_UP) ? true : false;
  1519. is_move = true;
  1520. }
  1521. else if (player_move_.y < 0){ // move down
  1522. to_end = (move == LM_DOWN_TO_UP) ? false : true;
  1523. is_move = true;
  1524. }
  1525. }
  1526. else if (move == LM_LEFT_TO_RIGHT || move == LM_RIGHT_TO_LEFT){
  1527. if (player_move_.x > 0){ // move right
  1528. to_end = (move == LM_LEFT_TO_RIGHT) ? true : false;
  1529. is_move = true;
  1530. }
  1531. else if (player_move_.x < 0){ // move left
  1532. to_end = (move == LM_LEFT_TO_RIGHT) ? false : true;
  1533. is_move = true;
  1534. }
  1535. }
  1536. }
  1537. else is_move = true;
  1538. if (is_move == true){
  1539. Ogre::Vector3 position;
  1540. if (to_end == true){
  1541. Ogre::Vector3 end_start = end - start;
  1542. // Move across the whole line by this number of seconds.
  1543. float time = end_start.length() / 1.2f;
  1544. position.x = current.x + (end_start.x / time) * delta;
  1545. position.y = current.y + (end_start.y / time) * delta;
  1546. position.z = current.z + (end_start.z / time) * delta;
  1547. position.x = (end_start.x < 0)
  1548. ? std::max(position.x, end.x) : std::min(position.x, end.x);
  1549. position.y = (end_start.y < 0)
  1550. ? std::max(position.y, end.y) : std::min(position.y, end.y);
  1551. position.z = (end_start.z < 0)
  1552. ? std::max(position.z, end.z) : std::min(position.z, end.z);
  1553. if (position == end){
  1554. entity->UnsetLinear();
  1555. entity->SetMoveTriangleId(entity->GetLinearDestTriangle());
  1556. SetEntityOnWalkmesh(entity);
  1557. }
  1558. }
  1559. else{
  1560. Ogre::Vector3 end_start = start - end;
  1561. // Move across the whole line by this number of seconds.
  1562. float time = end_start.length() / 1.2f;
  1563. position.x = current.x + (end_start.x / time) * delta;
  1564. position.y = current.y + (end_start.y / time) * delta;
  1565. position.z = current.z + (end_start.z / time) * delta;
  1566. position.x = (end_start.x < 0)
  1567. ? std::max(position.x, start.x) : std::min(position.x, start.x);
  1568. position.y = (end_start.y < 0)
  1569. ? std::max(position.y, start.y) : std::min(position.y, start.y);
  1570. position.z = (end_start.z < 0)
  1571. ? std::max(position.z, start.z) : std::min(position.z, start.z);
  1572. if (position == start){
  1573. entity->UnsetLinear();
  1574. SetEntityOnWalkmesh(entity);
  1575. }
  1576. }
  1577. entity->SetPosition(position);
  1578. entity->UpdateAnimation((to_end == true) ? delta : -delta);
  1579. }
  1580. }
  1581. void EntityManager::SetNextJumpStep(Entity* entity){
  1582. if (IsBattleModule()){
  1583. LOG_WARNING("Tried to set next jump step, but the EntityManager is in battle mode");
  1584. return;
  1585. }
  1586. float total = entity->GetJumpSeconds();
  1587. float current = entity->GetJumpCurrentSeconds();
  1588. current += Timer::getSingleton().GetGameTimeDelta();
  1589. current = (current > total) ? total : current;
  1590. Ogre::Vector3 start_position = entity->GetJumpStart();
  1591. Ogre::Vector3 end_position = entity->GetJumpEnd();
  1592. Ogre::Vector3 position;
  1593. position.x = start_position.x + ((end_position.x - start_position.x) / total) * current;
  1594. position.y = start_position.y + ((end_position.y - start_position.y) / total) * current;
  1595. position.z = current * current * -1.0f + current
  1596. * ((end_position.z - start_position.z) / total + total * 1.0f) + start_position.z;
  1597. entity->SetPosition(position);
  1598. if (total == current){
  1599. entity->UnsetJump();
  1600. entity->SetMoveTriangleId(entity->GetJumpDestTriangle());
  1601. SetEntityOnWalkmesh(entity);
  1602. }
  1603. else entity->SetJumpCurrentSeconds(current);
  1604. }
  1605. void EntityManager::SetNextScrollStep(){
  1606. // TODO: Allow this on battle mode?
  1607. background_2d_.UpdateScrollTarget();
  1608. Background2D::SCROLL_TYPE type = background_2d_.GetScrollType();
  1609. float total = background_2d_.GetScrollSeconds();
  1610. float current = background_2d_.GetScrollCurrentSeconds();
  1611. current += Timer::getSingleton().GetGameTimeDelta();
  1612. current = (current > total) ? total : current;
  1613. Ogre::Vector2 start = background_2d_.GetScrollPositionStart();
  1614. Ogre::Vector2 end = background_2d_.GetScrollPositionEnd();
  1615. float x = current / total;
  1616. float smooth_modifier = (type == Background2D::SMOOTH) ? -2 * x * x * x + 3 * x * x : x;
  1617. Ogre::Vector2 scroll = start + (end - start) * smooth_modifier;
  1618. background_2d_.SetScroll(scroll);
  1619. if (current == total) background_2d_.UnsetScroll();
  1620. else background_2d_.SetScrollCurrentSeconds(current);
  1621. }