content_cao.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "content_cao.h"
  17. #include <IBillboardSceneNode.h>
  18. #include <ICameraSceneNode.h>
  19. #include <ITextSceneNode.h>
  20. #include <IMeshManipulator.h>
  21. #include <IAnimatedMeshSceneNode.h>
  22. #include "client/client.h"
  23. #include "client/renderingengine.h"
  24. #include "client/sound.h"
  25. #include "client/tile.h"
  26. #include "util/basic_macros.h"
  27. #include "util/numeric.h" // For IntervalLimiter & setPitchYawRoll
  28. #include "util/serialize.h"
  29. #include "camera.h" // CameraModes
  30. #include "collision.h"
  31. #include "content_cso.h"
  32. #include "environment.h"
  33. #include "itemdef.h"
  34. #include "localplayer.h"
  35. #include "map.h"
  36. #include "mesh.h"
  37. #include "nodedef.h"
  38. #include "serialization.h" // For decompressZlib
  39. #include "settings.h"
  40. #include "sound.h"
  41. #include "tool.h"
  42. #include "wieldmesh.h"
  43. #include <algorithm>
  44. #include <cmath>
  45. #include "client/shader.h"
  46. class Settings;
  47. struct ToolCapabilities;
  48. std::unordered_map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
  49. template<typename T>
  50. void SmoothTranslator<T>::init(T current)
  51. {
  52. val_old = current;
  53. val_current = current;
  54. val_target = current;
  55. anim_time = 0;
  56. anim_time_counter = 0;
  57. aim_is_end = true;
  58. }
  59. template<typename T>
  60. void SmoothTranslator<T>::update(T new_target, bool is_end_position, float update_interval)
  61. {
  62. aim_is_end = is_end_position;
  63. val_old = val_current;
  64. val_target = new_target;
  65. if (update_interval > 0) {
  66. anim_time = update_interval;
  67. } else {
  68. if (anim_time < 0.001 || anim_time > 1.0)
  69. anim_time = anim_time_counter;
  70. else
  71. anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
  72. }
  73. anim_time_counter = 0;
  74. }
  75. template<typename T>
  76. void SmoothTranslator<T>::translate(f32 dtime)
  77. {
  78. anim_time_counter = anim_time_counter + dtime;
  79. T val_diff = val_target - val_old;
  80. f32 moveratio = 1.0;
  81. if (anim_time > 0.001)
  82. moveratio = anim_time_counter / anim_time;
  83. f32 move_end = aim_is_end ? 1.0 : 1.5;
  84. // Move a bit less than should, to avoid oscillation
  85. moveratio = std::min(moveratio * 0.8f, move_end);
  86. val_current = val_old + val_diff * moveratio;
  87. }
  88. void SmoothTranslatorWrapped::translate(f32 dtime)
  89. {
  90. anim_time_counter = anim_time_counter + dtime;
  91. f32 val_diff = std::abs(val_target - val_old);
  92. if (val_diff > 180.f)
  93. val_diff = 360.f - val_diff;
  94. f32 moveratio = 1.0;
  95. if (anim_time > 0.001)
  96. moveratio = anim_time_counter / anim_time;
  97. f32 move_end = aim_is_end ? 1.0 : 1.5;
  98. // Move a bit less than should, to avoid oscillation
  99. moveratio = std::min(moveratio * 0.8f, move_end);
  100. wrappedApproachShortest(val_current, val_target,
  101. val_diff * moveratio, 360.f);
  102. }
  103. void SmoothTranslatorWrappedv3f::translate(f32 dtime)
  104. {
  105. anim_time_counter = anim_time_counter + dtime;
  106. v3f val_diff_v3f;
  107. val_diff_v3f.X = std::abs(val_target.X - val_old.X);
  108. val_diff_v3f.Y = std::abs(val_target.Y - val_old.Y);
  109. val_diff_v3f.Z = std::abs(val_target.Z - val_old.Z);
  110. if (val_diff_v3f.X > 180.f)
  111. val_diff_v3f.X = 360.f - val_diff_v3f.X;
  112. if (val_diff_v3f.Y > 180.f)
  113. val_diff_v3f.Y = 360.f - val_diff_v3f.Y;
  114. if (val_diff_v3f.Z > 180.f)
  115. val_diff_v3f.Z = 360.f - val_diff_v3f.Z;
  116. f32 moveratio = 1.0;
  117. if (anim_time > 0.001)
  118. moveratio = anim_time_counter / anim_time;
  119. f32 move_end = aim_is_end ? 1.0 : 1.5;
  120. // Move a bit less than should, to avoid oscillation
  121. moveratio = std::min(moveratio * 0.8f, move_end);
  122. wrappedApproachShortest(val_current.X, val_target.X,
  123. val_diff_v3f.X * moveratio, 360.f);
  124. wrappedApproachShortest(val_current.Y, val_target.Y,
  125. val_diff_v3f.Y * moveratio, 360.f);
  126. wrappedApproachShortest(val_current.Z, val_target.Z,
  127. val_diff_v3f.Z * moveratio, 360.f);
  128. }
  129. /*
  130. Other stuff
  131. */
  132. static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
  133. float txs, float tys, int col, int row)
  134. {
  135. video::SMaterial& material = bill->getMaterial(0);
  136. core::matrix4& matrix = material.getTextureMatrix(0);
  137. matrix.setTextureTranslate(txs*col, tys*row);
  138. matrix.setTextureScale(txs, tys);
  139. }
  140. // Evaluate transform chain recursively; irrlicht does not do this for us
  141. static void updatePositionRecursive(scene::ISceneNode *node)
  142. {
  143. scene::ISceneNode *parent = node->getParent();
  144. if (parent)
  145. updatePositionRecursive(parent);
  146. node->updateAbsolutePosition();
  147. }
  148. /*
  149. TestCAO
  150. */
  151. class TestCAO : public ClientActiveObject
  152. {
  153. public:
  154. TestCAO(Client *client, ClientEnvironment *env);
  155. virtual ~TestCAO() = default;
  156. ActiveObjectType getType() const
  157. {
  158. return ACTIVEOBJECT_TYPE_TEST;
  159. }
  160. static ClientActiveObject* create(Client *client, ClientEnvironment *env);
  161. void addToScene(ITextureSource *tsrc);
  162. void removeFromScene(bool permanent);
  163. void updateLight(u32 day_night_ratio);
  164. void updateNodePos();
  165. void step(float dtime, ClientEnvironment *env);
  166. void processMessage(const std::string &data);
  167. bool getCollisionBox(aabb3f *toset) const { return false; }
  168. private:
  169. scene::IMeshSceneNode *m_node;
  170. v3f m_position;
  171. };
  172. // Prototype
  173. TestCAO proto_TestCAO(NULL, NULL);
  174. TestCAO::TestCAO(Client *client, ClientEnvironment *env):
  175. ClientActiveObject(0, client, env),
  176. m_node(NULL),
  177. m_position(v3f(0,10*BS,0))
  178. {
  179. ClientActiveObject::registerType(getType(), create);
  180. }
  181. ClientActiveObject* TestCAO::create(Client *client, ClientEnvironment *env)
  182. {
  183. return new TestCAO(client, env);
  184. }
  185. void TestCAO::addToScene(ITextureSource *tsrc)
  186. {
  187. if(m_node != NULL)
  188. return;
  189. //video::IVideoDriver* driver = smgr->getVideoDriver();
  190. scene::SMesh *mesh = new scene::SMesh();
  191. scene::IMeshBuffer *buf = new scene::SMeshBuffer();
  192. video::SColor c(255,255,255,255);
  193. video::S3DVertex vertices[4] =
  194. {
  195. video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
  196. video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
  197. video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
  198. video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
  199. };
  200. u16 indices[] = {0,1,2,2,3,0};
  201. buf->append(vertices, 4, indices, 6);
  202. // Set material
  203. buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
  204. buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
  205. buf->getMaterial().setTexture(0, tsrc->getTextureForMesh("rat.png"));
  206. buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
  207. buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
  208. buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  209. // Add to mesh
  210. mesh->addMeshBuffer(buf);
  211. buf->drop();
  212. m_node = RenderingEngine::get_scene_manager()->addMeshSceneNode(mesh, NULL);
  213. mesh->drop();
  214. updateNodePos();
  215. }
  216. void TestCAO::removeFromScene(bool permanent)
  217. {
  218. if (!m_node)
  219. return;
  220. m_node->remove();
  221. m_node = NULL;
  222. }
  223. void TestCAO::updateLight(u32 day_night_ratio)
  224. {
  225. }
  226. void TestCAO::updateNodePos()
  227. {
  228. if (!m_node)
  229. return;
  230. m_node->setPosition(m_position);
  231. //m_node->setRotation(v3f(0, 45, 0));
  232. }
  233. void TestCAO::step(float dtime, ClientEnvironment *env)
  234. {
  235. if(m_node)
  236. {
  237. v3f rot = m_node->getRotation();
  238. //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
  239. rot.Y += dtime * 180;
  240. m_node->setRotation(rot);
  241. }
  242. }
  243. void TestCAO::processMessage(const std::string &data)
  244. {
  245. infostream<<"TestCAO: Got data: "<<data<<std::endl;
  246. std::istringstream is(data, std::ios::binary);
  247. u16 cmd;
  248. is>>cmd;
  249. if(cmd == 0)
  250. {
  251. v3f newpos;
  252. is>>newpos.X;
  253. is>>newpos.Y;
  254. is>>newpos.Z;
  255. m_position = newpos;
  256. updateNodePos();
  257. }
  258. }
  259. /*
  260. GenericCAO
  261. */
  262. #include "clientobject.h"
  263. GenericCAO::GenericCAO(Client *client, ClientEnvironment *env):
  264. ClientActiveObject(0, client, env)
  265. {
  266. if (client == NULL) {
  267. ClientActiveObject::registerType(getType(), create);
  268. } else {
  269. m_client = client;
  270. }
  271. }
  272. bool GenericCAO::getCollisionBox(aabb3f *toset) const
  273. {
  274. if (m_prop.physical)
  275. {
  276. //update collision box
  277. toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
  278. toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
  279. toset->MinEdge += m_position;
  280. toset->MaxEdge += m_position;
  281. return true;
  282. }
  283. return false;
  284. }
  285. bool GenericCAO::collideWithObjects() const
  286. {
  287. return m_prop.collideWithObjects;
  288. }
  289. void GenericCAO::initialize(const std::string &data)
  290. {
  291. infostream<<"GenericCAO: Got init data"<<std::endl;
  292. processInitData(data);
  293. if (m_is_player) {
  294. // Check if it's the current player
  295. LocalPlayer *player = m_env->getLocalPlayer();
  296. if (player && strcmp(player->getName(), m_name.c_str()) == 0) {
  297. m_is_local_player = true;
  298. m_is_visible = false;
  299. player->setCAO(this);
  300. }
  301. }
  302. m_enable_shaders = g_settings->getBool("enable_shaders");
  303. }
  304. void GenericCAO::processInitData(const std::string &data)
  305. {
  306. std::istringstream is(data, std::ios::binary);
  307. const u8 version = readU8(is);
  308. if (version < 1) {
  309. errorstream << "GenericCAO: Unsupported init data version"
  310. << std::endl;
  311. return;
  312. }
  313. // PROTOCOL_VERSION >= 37
  314. m_name = deSerializeString(is);
  315. m_is_player = readU8(is);
  316. m_id = readU16(is);
  317. m_position = readV3F32(is);
  318. m_rotation = readV3F32(is);
  319. m_hp = readU16(is);
  320. const u8 num_messages = readU8(is);
  321. for (int i = 0; i < num_messages; i++) {
  322. std::string message = deSerializeLongString(is);
  323. processMessage(message);
  324. }
  325. m_rotation = wrapDegrees_0_360_v3f(m_rotation);
  326. pos_translator.init(m_position);
  327. rot_translator.init(m_rotation);
  328. updateNodePos();
  329. }
  330. GenericCAO::~GenericCAO()
  331. {
  332. removeFromScene(true);
  333. }
  334. bool GenericCAO::getSelectionBox(aabb3f *toset) const
  335. {
  336. if (!m_prop.is_visible || !m_is_visible || m_is_local_player
  337. || !m_prop.pointable) {
  338. return false;
  339. }
  340. *toset = m_selection_box;
  341. return true;
  342. }
  343. const v3f GenericCAO::getPosition() const
  344. {
  345. if (!getParent())
  346. return pos_translator.val_current;
  347. // Calculate real position in world based on MatrixNode
  348. if (m_matrixnode) {
  349. v3s16 camera_offset = m_env->getCameraOffset();
  350. return m_matrixnode->getAbsolutePosition() +
  351. intToFloat(camera_offset, BS);
  352. }
  353. return m_position;
  354. }
  355. const bool GenericCAO::isImmortal()
  356. {
  357. return itemgroup_get(getGroups(), "immortal");
  358. }
  359. scene::ISceneNode *GenericCAO::getSceneNode() const
  360. {
  361. if (m_meshnode) {
  362. return m_meshnode;
  363. }
  364. if (m_animated_meshnode) {
  365. return m_animated_meshnode;
  366. }
  367. if (m_wield_meshnode) {
  368. return m_wield_meshnode;
  369. }
  370. if (m_spritenode) {
  371. return m_spritenode;
  372. }
  373. return NULL;
  374. }
  375. scene::IAnimatedMeshSceneNode *GenericCAO::getAnimatedMeshSceneNode() const
  376. {
  377. return m_animated_meshnode;
  378. }
  379. void GenericCAO::setChildrenVisible(bool toset)
  380. {
  381. for (u16 cao_id : m_attachment_child_ids) {
  382. GenericCAO *obj = m_env->getGenericCAO(cao_id);
  383. if (obj) {
  384. obj->setVisible(toset);
  385. }
  386. }
  387. }
  388. void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
  389. {
  390. int old_parent = m_attachment_parent_id;
  391. m_attachment_parent_id = parent_id;
  392. m_attachment_bone = bone;
  393. m_attachment_position = position;
  394. m_attachment_rotation = rotation;
  395. ClientActiveObject *parent = m_env->getActiveObject(parent_id);
  396. if (parent_id != old_parent) {
  397. if (auto *o = m_env->getActiveObject(old_parent))
  398. o->removeAttachmentChild(m_id);
  399. if (parent)
  400. parent->addAttachmentChild(m_id);
  401. }
  402. updateAttachments();
  403. }
  404. void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
  405. v3f *rotation) const
  406. {
  407. *parent_id = m_attachment_parent_id;
  408. *bone = m_attachment_bone;
  409. *position = m_attachment_position;
  410. *rotation = m_attachment_rotation;
  411. }
  412. void GenericCAO::clearChildAttachments()
  413. {
  414. // Cannot use for-loop here: setAttachment() modifies 'm_attachment_child_ids'!
  415. while (!m_attachment_child_ids.empty()) {
  416. int child_id = *m_attachment_child_ids.begin();
  417. if (ClientActiveObject *child = m_env->getActiveObject(child_id))
  418. child->setAttachment(0, "", v3f(), v3f());
  419. removeAttachmentChild(child_id);
  420. }
  421. }
  422. void GenericCAO::clearParentAttachment()
  423. {
  424. if (m_attachment_parent_id)
  425. setAttachment(0, "", m_attachment_position, m_attachment_rotation);
  426. else
  427. setAttachment(0, "", v3f(), v3f());
  428. }
  429. void GenericCAO::addAttachmentChild(int child_id)
  430. {
  431. m_attachment_child_ids.insert(child_id);
  432. }
  433. void GenericCAO::removeAttachmentChild(int child_id)
  434. {
  435. m_attachment_child_ids.erase(child_id);
  436. }
  437. ClientActiveObject* GenericCAO::getParent() const
  438. {
  439. return m_attachment_parent_id ? m_env->getActiveObject(m_attachment_parent_id) :
  440. nullptr;
  441. }
  442. void GenericCAO::removeFromScene(bool permanent)
  443. {
  444. // Should be true when removing the object permanently
  445. // and false when refreshing (eg: updating visuals)
  446. if (m_env && permanent) {
  447. // The client does not know whether this object does re-appear to
  448. // a later time, thus do not clear child attachments.
  449. clearParentAttachment();
  450. }
  451. if (m_meshnode) {
  452. m_meshnode->remove();
  453. m_meshnode->drop();
  454. m_meshnode = nullptr;
  455. } else if (m_animated_meshnode) {
  456. m_animated_meshnode->remove();
  457. m_animated_meshnode->drop();
  458. m_animated_meshnode = nullptr;
  459. } else if (m_wield_meshnode) {
  460. m_wield_meshnode->remove();
  461. m_wield_meshnode->drop();
  462. m_wield_meshnode = nullptr;
  463. } else if (m_spritenode) {
  464. m_spritenode->remove();
  465. m_spritenode->drop();
  466. m_spritenode = nullptr;
  467. }
  468. if (m_matrixnode) {
  469. m_matrixnode->remove();
  470. m_matrixnode->drop();
  471. m_matrixnode = nullptr;
  472. }
  473. if (m_nametag) {
  474. m_client->getCamera()->removeNametag(m_nametag);
  475. m_nametag = nullptr;
  476. }
  477. }
  478. void GenericCAO::addToScene(ITextureSource *tsrc)
  479. {
  480. m_smgr = RenderingEngine::get_scene_manager();
  481. if (getSceneNode() != NULL) {
  482. return;
  483. }
  484. m_visuals_expired = false;
  485. if (!m_prop.is_visible)
  486. return;
  487. infostream << "GenericCAO::addToScene(): " << m_prop.visual << std::endl;
  488. if (m_enable_shaders) {
  489. IShaderSource *shader_source = m_client->getShaderSource();
  490. MaterialType material_type;
  491. if (m_prop.shaded && m_prop.glow == 0)
  492. material_type = (m_prop.use_texture_alpha) ?
  493. TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC;
  494. else
  495. material_type = (m_prop.use_texture_alpha) ?
  496. TILE_MATERIAL_PLAIN_ALPHA : TILE_MATERIAL_PLAIN;
  497. u32 shader_id = shader_source->getShader("object_shader", material_type, NDT_NORMAL);
  498. m_material_type = shader_source->getShaderInfo(shader_id).material;
  499. } else {
  500. m_material_type = (m_prop.use_texture_alpha) ?
  501. video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
  502. }
  503. auto grabMatrixNode = [this] {
  504. m_matrixnode = RenderingEngine::get_scene_manager()->
  505. addDummyTransformationSceneNode();
  506. m_matrixnode->grab();
  507. };
  508. auto setSceneNodeMaterial = [this] (scene::ISceneNode *node) {
  509. node->setMaterialFlag(video::EMF_LIGHTING, false);
  510. node->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
  511. node->setMaterialFlag(video::EMF_FOG_ENABLE, true);
  512. node->setMaterialType(m_material_type);
  513. if (m_enable_shaders) {
  514. node->setMaterialFlag(video::EMF_GOURAUD_SHADING, false);
  515. node->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
  516. }
  517. };
  518. if (m_prop.visual == "sprite") {
  519. grabMatrixNode();
  520. m_spritenode = RenderingEngine::get_scene_manager()->addBillboardSceneNode(
  521. m_matrixnode, v2f(1, 1), v3f(0,0,0), -1);
  522. m_spritenode->grab();
  523. m_spritenode->setMaterialTexture(0,
  524. tsrc->getTextureForMesh("unknown_node.png"));
  525. setSceneNodeMaterial(m_spritenode);
  526. m_spritenode->setSize(v2f(m_prop.visual_size.X,
  527. m_prop.visual_size.Y) * BS);
  528. {
  529. const float txs = 1.0 / 1;
  530. const float tys = 1.0 / 1;
  531. setBillboardTextureMatrix(m_spritenode,
  532. txs, tys, 0, 0);
  533. }
  534. } else if (m_prop.visual == "upright_sprite") {
  535. grabMatrixNode();
  536. scene::SMesh *mesh = new scene::SMesh();
  537. double dx = BS * m_prop.visual_size.X / 2;
  538. double dy = BS * m_prop.visual_size.Y / 2;
  539. video::SColor c(0xFFFFFFFF);
  540. { // Front
  541. scene::IMeshBuffer *buf = new scene::SMeshBuffer();
  542. video::S3DVertex vertices[4] = {
  543. video::S3DVertex(-dx, -dy, 0, 0,0,1, c, 1,1),
  544. video::S3DVertex( dx, -dy, 0, 0,0,1, c, 0,1),
  545. video::S3DVertex( dx, dy, 0, 0,0,1, c, 0,0),
  546. video::S3DVertex(-dx, dy, 0, 0,0,1, c, 1,0),
  547. };
  548. if (m_is_player) {
  549. // Move minimal Y position to 0 (feet position)
  550. for (video::S3DVertex &vertex : vertices)
  551. vertex.Pos.Y += dy;
  552. }
  553. u16 indices[] = {0,1,2,2,3,0};
  554. buf->append(vertices, 4, indices, 6);
  555. // Set material
  556. buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
  557. buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
  558. buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
  559. buf->getMaterial().MaterialType = m_material_type;
  560. if (m_enable_shaders) {
  561. buf->getMaterial().EmissiveColor = c;
  562. buf->getMaterial().setFlag(video::EMF_GOURAUD_SHADING, false);
  563. buf->getMaterial().setFlag(video::EMF_NORMALIZE_NORMALS, true);
  564. }
  565. // Add to mesh
  566. mesh->addMeshBuffer(buf);
  567. buf->drop();
  568. }
  569. { // Back
  570. scene::IMeshBuffer *buf = new scene::SMeshBuffer();
  571. video::S3DVertex vertices[4] = {
  572. video::S3DVertex( dx,-dy, 0, 0,0,-1, c, 1,1),
  573. video::S3DVertex(-dx,-dy, 0, 0,0,-1, c, 0,1),
  574. video::S3DVertex(-dx, dy, 0, 0,0,-1, c, 0,0),
  575. video::S3DVertex( dx, dy, 0, 0,0,-1, c, 1,0),
  576. };
  577. if (m_is_player) {
  578. // Move minimal Y position to 0 (feet position)
  579. for (video::S3DVertex &vertex : vertices)
  580. vertex.Pos.Y += dy;
  581. }
  582. u16 indices[] = {0,1,2,2,3,0};
  583. buf->append(vertices, 4, indices, 6);
  584. // Set material
  585. buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
  586. buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
  587. buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
  588. buf->getMaterial().MaterialType = m_material_type;
  589. if (m_enable_shaders) {
  590. buf->getMaterial().EmissiveColor = c;
  591. buf->getMaterial().setFlag(video::EMF_GOURAUD_SHADING, false);
  592. buf->getMaterial().setFlag(video::EMF_NORMALIZE_NORMALS, true);
  593. }
  594. // Add to mesh
  595. mesh->addMeshBuffer(buf);
  596. buf->drop();
  597. }
  598. m_meshnode = RenderingEngine::get_scene_manager()->
  599. addMeshSceneNode(mesh, m_matrixnode);
  600. m_meshnode->grab();
  601. mesh->drop();
  602. // Set it to use the materials of the meshbuffers directly.
  603. // This is needed for changing the texture in the future
  604. m_meshnode->setReadOnlyMaterials(true);
  605. } else if (m_prop.visual == "cube") {
  606. grabMatrixNode();
  607. scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
  608. m_meshnode = RenderingEngine::get_scene_manager()->
  609. addMeshSceneNode(mesh, m_matrixnode);
  610. m_meshnode->grab();
  611. mesh->drop();
  612. m_meshnode->setScale(m_prop.visual_size);
  613. m_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
  614. m_prop.backface_culling);
  615. setSceneNodeMaterial(m_meshnode);
  616. } else if (m_prop.visual == "mesh") {
  617. grabMatrixNode();
  618. scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh, true);
  619. if (mesh) {
  620. m_animated_meshnode = RenderingEngine::get_scene_manager()->
  621. addAnimatedMeshSceneNode(mesh, m_matrixnode);
  622. m_animated_meshnode->grab();
  623. mesh->drop(); // The scene node took hold of it
  624. if (!checkMeshNormals(mesh)) {
  625. infostream << "GenericCAO: recalculating normals for mesh "
  626. << m_prop.mesh << std::endl;
  627. m_smgr->getMeshManipulator()->
  628. recalculateNormals(mesh, true, false);
  629. }
  630. m_animated_meshnode->animateJoints(); // Needed for some animations
  631. m_animated_meshnode->setScale(m_prop.visual_size);
  632. // set vertex colors to ensure alpha is set
  633. setMeshColor(m_animated_meshnode->getMesh(), video::SColor(0xFFFFFFFF));
  634. setAnimatedMeshColor(m_animated_meshnode, video::SColor(0xFFFFFFFF));
  635. setSceneNodeMaterial(m_animated_meshnode);
  636. m_animated_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
  637. m_prop.backface_culling);
  638. } else
  639. errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
  640. } else if (m_prop.visual == "wielditem" || m_prop.visual == "item") {
  641. grabMatrixNode();
  642. ItemStack item;
  643. if (m_prop.wield_item.empty()) {
  644. // Old format, only textures are specified.
  645. infostream << "textures: " << m_prop.textures.size() << std::endl;
  646. if (!m_prop.textures.empty()) {
  647. infostream << "textures[0]: " << m_prop.textures[0]
  648. << std::endl;
  649. IItemDefManager *idef = m_client->idef();
  650. item = ItemStack(m_prop.textures[0], 1, 0, idef);
  651. }
  652. } else {
  653. infostream << "serialized form: " << m_prop.wield_item << std::endl;
  654. item.deSerialize(m_prop.wield_item, m_client->idef());
  655. }
  656. m_wield_meshnode = new WieldMeshSceneNode(
  657. RenderingEngine::get_scene_manager(), -1);
  658. m_wield_meshnode->setItem(item, m_client,
  659. (m_prop.visual == "wielditem"));
  660. m_wield_meshnode->setScale(m_prop.visual_size / 2.0f);
  661. m_wield_meshnode->setColor(video::SColor(0xFFFFFFFF));
  662. } else {
  663. infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
  664. <<"\" not supported"<<std::endl;
  665. }
  666. /* don't update while punch texture modifier is active */
  667. if (m_reset_textures_timer < 0)
  668. updateTextures(m_current_texture_modifier);
  669. scene::ISceneNode *node = getSceneNode();
  670. if (node && m_matrixnode)
  671. node->setParent(m_matrixnode);
  672. updateNametag();
  673. updateNodePos();
  674. updateAnimation();
  675. updateBonePosition();
  676. updateAttachments();
  677. setNodeLight(m_last_light);
  678. }
  679. void GenericCAO::updateLight(u32 day_night_ratio)
  680. {
  681. if (m_glow < 0)
  682. return;
  683. u8 light_at_pos = 0;
  684. bool pos_ok = false;
  685. v3s16 pos[3];
  686. u16 npos = getLightPosition(pos);
  687. for (u16 i = 0; i < npos; i++) {
  688. bool this_ok;
  689. MapNode n = m_env->getMap().getNode(pos[i], &this_ok);
  690. if (this_ok) {
  691. u8 this_light = n.getLightBlend(day_night_ratio, m_client->ndef());
  692. light_at_pos = MYMAX(light_at_pos, this_light);
  693. pos_ok = true;
  694. }
  695. }
  696. if (!pos_ok)
  697. light_at_pos = blend_light(day_night_ratio, LIGHT_SUN, 0);
  698. u8 light = decode_light(light_at_pos + m_glow);
  699. if (light != m_last_light) {
  700. m_last_light = light;
  701. setNodeLight(light);
  702. }
  703. }
  704. void GenericCAO::setNodeLight(u8 light)
  705. {
  706. video::SColor color(255, light, light, light);
  707. if (m_prop.visual == "wielditem" || m_prop.visual == "item") {
  708. if (m_wield_meshnode)
  709. m_wield_meshnode->setNodeLightColor(color);
  710. return;
  711. }
  712. if (m_enable_shaders) {
  713. if (m_prop.visual == "upright_sprite") {
  714. if (!m_meshnode)
  715. return;
  716. scene::IMesh *mesh = m_meshnode->getMesh();
  717. for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
  718. scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
  719. buf->getMaterial().EmissiveColor = color;
  720. }
  721. } else {
  722. scene::ISceneNode *node = getSceneNode();
  723. if (!node)
  724. return;
  725. for (u32 i = 0; i < node->getMaterialCount(); ++i) {
  726. video::SMaterial &material = node->getMaterial(i);
  727. material.EmissiveColor = color;
  728. }
  729. }
  730. } else {
  731. if (m_meshnode) {
  732. setMeshColor(m_meshnode->getMesh(), color);
  733. } else if (m_animated_meshnode) {
  734. setAnimatedMeshColor(m_animated_meshnode, color);
  735. } else if (m_spritenode) {
  736. m_spritenode->setColor(color);
  737. }
  738. }
  739. }
  740. u16 GenericCAO::getLightPosition(v3s16 *pos)
  741. {
  742. const auto &box = m_prop.collisionbox;
  743. pos[0] = floatToInt(m_position + box.MinEdge * BS, BS);
  744. pos[1] = floatToInt(m_position + box.MaxEdge * BS, BS);
  745. // Skip center pos if it falls into the same node as Min or MaxEdge
  746. if ((box.MaxEdge - box.MinEdge).getLengthSQ() < 3.0f)
  747. return 2;
  748. pos[2] = floatToInt(m_position + box.getCenter() * BS, BS);
  749. return 3;
  750. }
  751. void GenericCAO::updateNametag()
  752. {
  753. if (m_is_local_player) // No nametag for local player
  754. return;
  755. if (m_prop.nametag.empty()) {
  756. // Delete nametag
  757. if (m_nametag) {
  758. m_client->getCamera()->removeNametag(m_nametag);
  759. m_nametag = nullptr;
  760. }
  761. return;
  762. }
  763. scene::ISceneNode *node = getSceneNode();
  764. if (!node)
  765. return;
  766. v3f pos;
  767. pos.Y = m_prop.selectionbox.MaxEdge.Y + 0.3f;
  768. if (!m_nametag) {
  769. // Add nametag
  770. m_nametag = m_client->getCamera()->addNametag(node,
  771. m_prop.nametag, m_prop.nametag_color, pos);
  772. } else {
  773. // Update nametag
  774. m_nametag->nametag_text = m_prop.nametag;
  775. m_nametag->nametag_color = m_prop.nametag_color;
  776. m_nametag->nametag_pos = pos;
  777. }
  778. }
  779. void GenericCAO::updateNodePos()
  780. {
  781. if (getParent() != NULL)
  782. return;
  783. scene::ISceneNode *node = getSceneNode();
  784. if (node) {
  785. v3s16 camera_offset = m_env->getCameraOffset();
  786. v3f pos = pos_translator.val_current -
  787. intToFloat(camera_offset, BS);
  788. getPosRotMatrix().setTranslation(pos);
  789. if (node != m_spritenode) { // rotate if not a sprite
  790. v3f rot = m_is_local_player ? -m_rotation : -rot_translator.val_current;
  791. setPitchYawRoll(getPosRotMatrix(), rot);
  792. }
  793. }
  794. }
  795. void GenericCAO::step(float dtime, ClientEnvironment *env)
  796. {
  797. // Handle model animations and update positions instantly to prevent lags
  798. if (m_is_local_player) {
  799. LocalPlayer *player = m_env->getLocalPlayer();
  800. m_position = player->getPosition();
  801. pos_translator.val_current = m_position;
  802. m_rotation.Y = wrapDegrees_0_360(player->getYaw());
  803. rot_translator.val_current = m_rotation;
  804. if (m_is_visible) {
  805. int old_anim = player->last_animation;
  806. float old_anim_speed = player->last_animation_speed;
  807. m_velocity = v3f(0,0,0);
  808. m_acceleration = v3f(0,0,0);
  809. const PlayerControl &controls = player->getPlayerControl();
  810. bool walking = false;
  811. if (controls.up || controls.down || controls.left || controls.right ||
  812. controls.forw_move_joystick_axis != 0.f ||
  813. controls.sidew_move_joystick_axis != 0.f)
  814. walking = true;
  815. f32 new_speed = player->local_animation_speed;
  816. v2s32 new_anim = v2s32(0,0);
  817. bool allow_update = false;
  818. // increase speed if using fast or flying fast
  819. if((g_settings->getBool("fast_move") &&
  820. m_client->checkLocalPrivilege("fast")) &&
  821. (controls.aux1 ||
  822. (!player->touching_ground &&
  823. g_settings->getBool("free_move") &&
  824. m_client->checkLocalPrivilege("fly"))))
  825. new_speed *= 1.5;
  826. // slowdown speed if sneeking
  827. if (controls.sneak && walking)
  828. new_speed /= 2;
  829. if (walking && (controls.LMB || controls.RMB)) {
  830. new_anim = player->local_animations[3];
  831. player->last_animation = WD_ANIM;
  832. } else if(walking) {
  833. new_anim = player->local_animations[1];
  834. player->last_animation = WALK_ANIM;
  835. } else if(controls.LMB || controls.RMB) {
  836. new_anim = player->local_animations[2];
  837. player->last_animation = DIG_ANIM;
  838. }
  839. // Apply animations if input detected and not attached
  840. // or set idle animation
  841. if ((new_anim.X + new_anim.Y) > 0 && !getParent()) {
  842. allow_update = true;
  843. m_animation_range = new_anim;
  844. m_animation_speed = new_speed;
  845. player->last_animation_speed = m_animation_speed;
  846. } else {
  847. player->last_animation = NO_ANIM;
  848. if (old_anim != NO_ANIM) {
  849. m_animation_range = player->local_animations[0];
  850. updateAnimation();
  851. }
  852. }
  853. // Update local player animations
  854. if ((player->last_animation != old_anim ||
  855. m_animation_speed != old_anim_speed) &&
  856. player->last_animation != NO_ANIM && allow_update)
  857. updateAnimation();
  858. }
  859. }
  860. if (m_visuals_expired && m_smgr) {
  861. m_visuals_expired = false;
  862. // Attachments, part 1: All attached objects must be unparented first,
  863. // or Irrlicht causes a segmentation fault
  864. for (u16 cao_id : m_attachment_child_ids) {
  865. ClientActiveObject *obj = m_env->getActiveObject(cao_id);
  866. if (obj) {
  867. scene::ISceneNode *child_node = obj->getSceneNode();
  868. // The node's parent is always an IDummyTraformationSceneNode,
  869. // so we need to reparent that one instead.
  870. if (child_node)
  871. child_node->getParent()->setParent(m_smgr->getRootSceneNode());
  872. }
  873. }
  874. removeFromScene(false);
  875. addToScene(m_client->tsrc());
  876. // Attachments, part 2: Now that the parent has been refreshed, put its attachments back
  877. for (u16 cao_id : m_attachment_child_ids) {
  878. ClientActiveObject *obj = m_env->getActiveObject(cao_id);
  879. if (obj)
  880. obj->updateAttachments();
  881. }
  882. }
  883. // Make sure m_is_visible is always applied
  884. scene::ISceneNode *node = getSceneNode();
  885. if (node)
  886. node->setVisible(m_is_visible);
  887. if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
  888. {
  889. // Set these for later
  890. m_position = getPosition();
  891. m_velocity = v3f(0,0,0);
  892. m_acceleration = v3f(0,0,0);
  893. pos_translator.val_current = m_position;
  894. pos_translator.val_target = m_position;
  895. } else {
  896. rot_translator.translate(dtime);
  897. v3f lastpos = pos_translator.val_current;
  898. if(m_prop.physical)
  899. {
  900. aabb3f box = m_prop.collisionbox;
  901. box.MinEdge *= BS;
  902. box.MaxEdge *= BS;
  903. collisionMoveResult moveresult;
  904. f32 pos_max_d = BS*0.125; // Distance per iteration
  905. v3f p_pos = m_position;
  906. v3f p_velocity = m_velocity;
  907. moveresult = collisionMoveSimple(env,env->getGameDef(),
  908. pos_max_d, box, m_prop.stepheight, dtime,
  909. &p_pos, &p_velocity, m_acceleration,
  910. this, m_prop.collideWithObjects);
  911. // Apply results
  912. m_position = p_pos;
  913. m_velocity = p_velocity;
  914. bool is_end_position = moveresult.collides;
  915. pos_translator.update(m_position, is_end_position, dtime);
  916. } else {
  917. m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
  918. m_velocity += dtime * m_acceleration;
  919. pos_translator.update(m_position, pos_translator.aim_is_end,
  920. pos_translator.anim_time);
  921. }
  922. pos_translator.translate(dtime);
  923. updateNodePos();
  924. float moved = lastpos.getDistanceFrom(pos_translator.val_current);
  925. m_step_distance_counter += moved;
  926. if (m_step_distance_counter > 1.5f * BS) {
  927. m_step_distance_counter = 0.0f;
  928. if (!m_is_local_player && m_prop.makes_footstep_sound) {
  929. const NodeDefManager *ndef = m_client->ndef();
  930. v3s16 p = floatToInt(getPosition() +
  931. v3f(0.0f, (m_prop.collisionbox.MinEdge.Y - 0.5f) * BS, 0.0f), BS);
  932. MapNode n = m_env->getMap().getNode(p);
  933. SimpleSoundSpec spec = ndef->get(n).sound_footstep;
  934. // Reduce footstep gain, as non-local-player footsteps are
  935. // somehow louder.
  936. spec.gain *= 0.6f;
  937. m_client->sound()->playSoundAt(spec, false, getPosition());
  938. }
  939. }
  940. }
  941. m_anim_timer += dtime;
  942. if(m_anim_timer >= m_anim_framelength)
  943. {
  944. m_anim_timer -= m_anim_framelength;
  945. m_anim_frame++;
  946. if(m_anim_frame >= m_anim_num_frames)
  947. m_anim_frame = 0;
  948. }
  949. updateTexturePos();
  950. if(m_reset_textures_timer >= 0)
  951. {
  952. m_reset_textures_timer -= dtime;
  953. if(m_reset_textures_timer <= 0) {
  954. m_reset_textures_timer = -1;
  955. updateTextures(m_previous_texture_modifier);
  956. }
  957. }
  958. if (!getParent() && std::fabs(m_prop.automatic_rotate) > 0.001) {
  959. // This is the child node's rotation. It is only used for automatic_rotate.
  960. v3f local_rot = node->getRotation();
  961. local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG *
  962. m_prop.automatic_rotate);
  963. node->setRotation(local_rot);
  964. }
  965. if (!getParent() && m_prop.automatic_face_movement_dir &&
  966. (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
  967. float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
  968. + m_prop.automatic_face_movement_dir_offset;
  969. float max_rotation_per_sec =
  970. m_prop.automatic_face_movement_max_rotation_per_sec;
  971. if (max_rotation_per_sec > 0) {
  972. wrappedApproachShortest(m_rotation.Y, target_yaw,
  973. dtime * max_rotation_per_sec, 360.f);
  974. } else {
  975. // Negative values of max_rotation_per_sec mean disabled.
  976. m_rotation.Y = target_yaw;
  977. }
  978. rot_translator.val_current = m_rotation;
  979. updateNodePos();
  980. }
  981. if (m_animated_meshnode) {
  982. // Everything must be updated; the whole transform
  983. // chain as well as the animated mesh node.
  984. // Otherwise, bone attachments would be relative to
  985. // a position that's one frame old.
  986. if (m_matrixnode)
  987. updatePositionRecursive(m_matrixnode);
  988. m_animated_meshnode->updateAbsolutePosition();
  989. m_animated_meshnode->animateJoints();
  990. updateBonePosition();
  991. }
  992. }
  993. void GenericCAO::updateTexturePos()
  994. {
  995. if(m_spritenode)
  996. {
  997. scene::ICameraSceneNode* camera =
  998. m_spritenode->getSceneManager()->getActiveCamera();
  999. if(!camera)
  1000. return;
  1001. v3f cam_to_entity = m_spritenode->getAbsolutePosition()
  1002. - camera->getAbsolutePosition();
  1003. cam_to_entity.normalize();
  1004. int row = m_tx_basepos.Y;
  1005. int col = m_tx_basepos.X;
  1006. if (m_tx_select_horiz_by_yawpitch) {
  1007. if (cam_to_entity.Y > 0.75)
  1008. col += 5;
  1009. else if (cam_to_entity.Y < -0.75)
  1010. col += 4;
  1011. else {
  1012. float mob_dir =
  1013. atan2(cam_to_entity.Z, cam_to_entity.X) / M_PI * 180.;
  1014. float dir = mob_dir - m_rotation.Y;
  1015. dir = wrapDegrees_180(dir);
  1016. if (std::fabs(wrapDegrees_180(dir - 0)) <= 45.1f)
  1017. col += 2;
  1018. else if(std::fabs(wrapDegrees_180(dir - 90)) <= 45.1f)
  1019. col += 3;
  1020. else if(std::fabs(wrapDegrees_180(dir - 180)) <= 45.1f)
  1021. col += 0;
  1022. else if(std::fabs(wrapDegrees_180(dir + 90)) <= 45.1f)
  1023. col += 1;
  1024. else
  1025. col += 4;
  1026. }
  1027. }
  1028. // Animation goes downwards
  1029. row += m_anim_frame;
  1030. float txs = m_tx_size.X;
  1031. float tys = m_tx_size.Y;
  1032. setBillboardTextureMatrix(m_spritenode, txs, tys, col, row);
  1033. }
  1034. }
  1035. // Do not pass by reference, see header.
  1036. void GenericCAO::updateTextures(std::string mod)
  1037. {
  1038. ITextureSource *tsrc = m_client->tsrc();
  1039. bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
  1040. bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
  1041. bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
  1042. m_previous_texture_modifier = m_current_texture_modifier;
  1043. m_current_texture_modifier = mod;
  1044. m_glow = m_prop.glow;
  1045. if (m_spritenode) {
  1046. if (m_prop.visual == "sprite") {
  1047. std::string texturestring = "unknown_node.png";
  1048. if (!m_prop.textures.empty())
  1049. texturestring = m_prop.textures[0];
  1050. texturestring += mod;
  1051. m_spritenode->getMaterial(0).MaterialType = m_material_type;
  1052. m_spritenode->getMaterial(0).MaterialTypeParam = 0.5f;
  1053. m_spritenode->setMaterialTexture(0,
  1054. tsrc->getTextureForMesh(texturestring));
  1055. // This allows setting per-material colors. However, until a real lighting
  1056. // system is added, the code below will have no effect. Once MineTest
  1057. // has directional lighting, it should work automatically.
  1058. if (!m_prop.colors.empty()) {
  1059. m_spritenode->getMaterial(0).AmbientColor = m_prop.colors[0];
  1060. m_spritenode->getMaterial(0).DiffuseColor = m_prop.colors[0];
  1061. m_spritenode->getMaterial(0).SpecularColor = m_prop.colors[0];
  1062. }
  1063. m_spritenode->getMaterial(0).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
  1064. m_spritenode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
  1065. m_spritenode->getMaterial(0).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
  1066. }
  1067. }
  1068. if (m_animated_meshnode) {
  1069. if (m_prop.visual == "mesh") {
  1070. for (u32 i = 0; i < m_prop.textures.size() &&
  1071. i < m_animated_meshnode->getMaterialCount(); ++i) {
  1072. std::string texturestring = m_prop.textures[i];
  1073. if (texturestring.empty())
  1074. continue; // Empty texture string means don't modify that material
  1075. texturestring += mod;
  1076. video::ITexture* texture = tsrc->getTextureForMesh(texturestring);
  1077. if (!texture) {
  1078. errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
  1079. continue;
  1080. }
  1081. // Set material flags and texture
  1082. video::SMaterial& material = m_animated_meshnode->getMaterial(i);
  1083. material.MaterialType = m_material_type;
  1084. material.MaterialTypeParam = 0.5f;
  1085. material.TextureLayer[0].Texture = texture;
  1086. material.setFlag(video::EMF_LIGHTING, true);
  1087. material.setFlag(video::EMF_BILINEAR_FILTER, false);
  1088. material.setFlag(video::EMF_BACK_FACE_CULLING, m_prop.backface_culling);
  1089. // don't filter low-res textures, makes them look blurry
  1090. // player models have a res of 64
  1091. const core::dimension2d<u32> &size = texture->getOriginalSize();
  1092. const u32 res = std::min(size.Height, size.Width);
  1093. use_trilinear_filter &= res > 64;
  1094. use_bilinear_filter &= res > 64;
  1095. m_animated_meshnode->getMaterial(i)
  1096. .setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
  1097. m_animated_meshnode->getMaterial(i)
  1098. .setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
  1099. m_animated_meshnode->getMaterial(i)
  1100. .setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
  1101. }
  1102. for (u32 i = 0; i < m_prop.colors.size() &&
  1103. i < m_animated_meshnode->getMaterialCount(); ++i)
  1104. {
  1105. // This allows setting per-material colors. However, until a real lighting
  1106. // system is added, the code below will have no effect. Once MineTest
  1107. // has directional lighting, it should work automatically.
  1108. m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
  1109. m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
  1110. m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
  1111. }
  1112. }
  1113. }
  1114. if(m_meshnode)
  1115. {
  1116. if(m_prop.visual == "cube")
  1117. {
  1118. for (u32 i = 0; i < 6; ++i)
  1119. {
  1120. std::string texturestring = "unknown_node.png";
  1121. if(m_prop.textures.size() > i)
  1122. texturestring = m_prop.textures[i];
  1123. texturestring += mod;
  1124. // Set material flags and texture
  1125. video::SMaterial& material = m_meshnode->getMaterial(i);
  1126. material.MaterialType = m_material_type;
  1127. material.MaterialTypeParam = 0.5f;
  1128. material.setFlag(video::EMF_LIGHTING, false);
  1129. material.setFlag(video::EMF_BILINEAR_FILTER, false);
  1130. material.setTexture(0,
  1131. tsrc->getTextureForMesh(texturestring));
  1132. material.getTextureMatrix(0).makeIdentity();
  1133. // This allows setting per-material colors. However, until a real lighting
  1134. // system is added, the code below will have no effect. Once MineTest
  1135. // has directional lighting, it should work automatically.
  1136. if(m_prop.colors.size() > i)
  1137. {
  1138. m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
  1139. m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
  1140. m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
  1141. }
  1142. m_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
  1143. m_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
  1144. m_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
  1145. }
  1146. } else if (m_prop.visual == "upright_sprite") {
  1147. scene::IMesh *mesh = m_meshnode->getMesh();
  1148. {
  1149. std::string tname = "unknown_object.png";
  1150. if (!m_prop.textures.empty())
  1151. tname = m_prop.textures[0];
  1152. tname += mod;
  1153. scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
  1154. buf->getMaterial().setTexture(0,
  1155. tsrc->getTextureForMesh(tname));
  1156. // This allows setting per-material colors. However, until a real lighting
  1157. // system is added, the code below will have no effect. Once MineTest
  1158. // has directional lighting, it should work automatically.
  1159. if(!m_prop.colors.empty()) {
  1160. buf->getMaterial().AmbientColor = m_prop.colors[0];
  1161. buf->getMaterial().DiffuseColor = m_prop.colors[0];
  1162. buf->getMaterial().SpecularColor = m_prop.colors[0];
  1163. }
  1164. buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
  1165. buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
  1166. buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
  1167. }
  1168. {
  1169. std::string tname = "unknown_object.png";
  1170. if (m_prop.textures.size() >= 2)
  1171. tname = m_prop.textures[1];
  1172. else if (!m_prop.textures.empty())
  1173. tname = m_prop.textures[0];
  1174. tname += mod;
  1175. scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
  1176. buf->getMaterial().setTexture(0,
  1177. tsrc->getTextureForMesh(tname));
  1178. // This allows setting per-material colors. However, until a real lighting
  1179. // system is added, the code below will have no effect. Once MineTest
  1180. // has directional lighting, it should work automatically.
  1181. if (m_prop.colors.size() >= 2) {
  1182. buf->getMaterial().AmbientColor = m_prop.colors[1];
  1183. buf->getMaterial().DiffuseColor = m_prop.colors[1];
  1184. buf->getMaterial().SpecularColor = m_prop.colors[1];
  1185. } else if (!m_prop.colors.empty()) {
  1186. buf->getMaterial().AmbientColor = m_prop.colors[0];
  1187. buf->getMaterial().DiffuseColor = m_prop.colors[0];
  1188. buf->getMaterial().SpecularColor = m_prop.colors[0];
  1189. }
  1190. buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
  1191. buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
  1192. buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
  1193. }
  1194. // Set mesh color (only if lighting is disabled)
  1195. if (!m_prop.colors.empty() && m_glow < 0)
  1196. setMeshColor(mesh, m_prop.colors[0]);
  1197. }
  1198. }
  1199. }
  1200. void GenericCAO::updateAnimation()
  1201. {
  1202. if (!m_animated_meshnode)
  1203. return;
  1204. if (m_animated_meshnode->getStartFrame() != m_animation_range.X ||
  1205. m_animated_meshnode->getEndFrame() != m_animation_range.Y)
  1206. m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y);
  1207. if (m_animated_meshnode->getAnimationSpeed() != m_animation_speed)
  1208. m_animated_meshnode->setAnimationSpeed(m_animation_speed);
  1209. m_animated_meshnode->setTransitionTime(m_animation_blend);
  1210. // Requires Irrlicht 1.8 or greater
  1211. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR > 1
  1212. if (m_animated_meshnode->getLoopMode() != m_animation_loop)
  1213. m_animated_meshnode->setLoopMode(m_animation_loop);
  1214. #endif
  1215. }
  1216. void GenericCAO::updateAnimationSpeed()
  1217. {
  1218. if (!m_animated_meshnode)
  1219. return;
  1220. m_animated_meshnode->setAnimationSpeed(m_animation_speed);
  1221. }
  1222. void GenericCAO::updateBonePosition()
  1223. {
  1224. if (m_bone_position.empty() || !m_animated_meshnode)
  1225. return;
  1226. m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
  1227. for (auto &it : m_bone_position) {
  1228. std::string bone_name = it.first;
  1229. irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
  1230. if (bone) {
  1231. bone->setPosition(it.second.X);
  1232. bone->setRotation(it.second.Y);
  1233. }
  1234. }
  1235. // search through bones to find mistakenly rotated bones due to bug in Irrlicht
  1236. for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) {
  1237. irr::scene::IBoneSceneNode *bone = m_animated_meshnode->getJointNode(i);
  1238. if (!bone)
  1239. continue;
  1240. //If bone is manually positioned there is no need to perform the bug check
  1241. bool skip = false;
  1242. for (auto &it : m_bone_position) {
  1243. if (it.first == bone->getName()) {
  1244. skip = true;
  1245. break;
  1246. }
  1247. }
  1248. if (skip)
  1249. continue;
  1250. // Workaround for Irrlicht bug
  1251. // We check each bone to see if it has been rotated ~180deg from its expected position due to a bug in Irricht
  1252. // when using EJUOR_CONTROL joint control. If the bug is detected we update the bone to the proper position
  1253. // and update the bones transformation.
  1254. v3f bone_rot = bone->getRelativeTransformation().getRotationDegrees();
  1255. float offset = fabsf(bone_rot.X - bone->getRotation().X);
  1256. if (offset > 179.9f && offset < 180.1f) {
  1257. bone->setRotation(bone_rot);
  1258. bone->updateAbsolutePosition();
  1259. }
  1260. }
  1261. // The following is needed for set_bone_pos to propagate to
  1262. // attached objects correctly.
  1263. // Irrlicht ought to do this, but doesn't when using EJUOR_CONTROL.
  1264. for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) {
  1265. auto bone = m_animated_meshnode->getJointNode(i);
  1266. // Look for the root bone.
  1267. if (bone && bone->getParent() == m_animated_meshnode) {
  1268. // Update entire skeleton.
  1269. bone->updateAbsolutePositionOfAllChildren();
  1270. break;
  1271. }
  1272. }
  1273. }
  1274. void GenericCAO::updateAttachments()
  1275. {
  1276. ClientActiveObject *parent = getParent();
  1277. m_attached_to_local = parent && parent->isLocalPlayer();
  1278. /*
  1279. Following cases exist:
  1280. m_attachment_parent_id == 0 && !parent
  1281. This object is not attached
  1282. m_attachment_parent_id != 0 && parent
  1283. This object is attached
  1284. m_attachment_parent_id != 0 && !parent
  1285. This object will be attached as soon the parent is known
  1286. m_attachment_parent_id == 0 && parent
  1287. Impossible case
  1288. */
  1289. if (!parent) { // Detach or don't attach
  1290. if (m_matrixnode) {
  1291. v3s16 camera_offset = m_env->getCameraOffset();
  1292. v3f old_pos = getPosition();
  1293. m_matrixnode->setParent(m_smgr->getRootSceneNode());
  1294. getPosRotMatrix().setTranslation(old_pos - intToFloat(camera_offset, BS));
  1295. m_matrixnode->updateAbsolutePosition();
  1296. }
  1297. }
  1298. else // Attach
  1299. {
  1300. parent->updateAttachments();
  1301. scene::ISceneNode *parent_node = parent->getSceneNode();
  1302. scene::IAnimatedMeshSceneNode *parent_animated_mesh_node =
  1303. parent->getAnimatedMeshSceneNode();
  1304. if (parent_animated_mesh_node && !m_attachment_bone.empty()) {
  1305. parent_node = parent_animated_mesh_node->getJointNode(m_attachment_bone.c_str());
  1306. }
  1307. if (m_matrixnode && parent_node) {
  1308. m_matrixnode->setParent(parent_node);
  1309. parent_node->updateAbsolutePosition();
  1310. getPosRotMatrix().setTranslation(m_attachment_position);
  1311. //setPitchYawRoll(getPosRotMatrix(), m_attachment_rotation);
  1312. // use Irrlicht eulers instead
  1313. getPosRotMatrix().setRotationDegrees(m_attachment_rotation);
  1314. m_matrixnode->updateAbsolutePosition();
  1315. }
  1316. }
  1317. }
  1318. bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const
  1319. {
  1320. const ObjectProperties &old = m_prop;
  1321. /* Visuals do not need to be expired for:
  1322. * - nametag props: handled by updateNametag()
  1323. * - textures: handled by updateTextures()
  1324. * - sprite props: handled by updateTexturePos()
  1325. * - glow: handled by updateLight()
  1326. * - any other properties that do not change appearance
  1327. */
  1328. bool uses_legacy_texture = new_.wield_item.empty() &&
  1329. (new_.visual == "wielditem" || new_.visual == "item");
  1330. // Ordered to compare primitive types before std::vectors
  1331. return old.backface_culling != new_.backface_culling ||
  1332. old.is_visible != new_.is_visible ||
  1333. old.mesh != new_.mesh ||
  1334. old.shaded != new_.shaded ||
  1335. old.use_texture_alpha != new_.use_texture_alpha ||
  1336. old.visual != new_.visual ||
  1337. old.visual_size != new_.visual_size ||
  1338. old.wield_item != new_.wield_item ||
  1339. old.colors != new_.colors ||
  1340. (uses_legacy_texture && old.textures != new_.textures);
  1341. }
  1342. void GenericCAO::processMessage(const std::string &data)
  1343. {
  1344. //infostream<<"GenericCAO: Got message"<<std::endl;
  1345. std::istringstream is(data, std::ios::binary);
  1346. // command
  1347. u8 cmd = readU8(is);
  1348. if (cmd == AO_CMD_SET_PROPERTIES) {
  1349. ObjectProperties newprops;
  1350. newprops.deSerialize(is);
  1351. // Check what exactly changed
  1352. bool expire_visuals = visualExpiryRequired(newprops);
  1353. bool textures_changed = m_prop.textures != newprops.textures;
  1354. // Apply changes
  1355. m_prop = std::move(newprops);
  1356. m_selection_box = m_prop.selectionbox;
  1357. m_selection_box.MinEdge *= BS;
  1358. m_selection_box.MaxEdge *= BS;
  1359. m_tx_size.X = 1.0f / m_prop.spritediv.X;
  1360. m_tx_size.Y = 1.0f / m_prop.spritediv.Y;
  1361. if(!m_initial_tx_basepos_set){
  1362. m_initial_tx_basepos_set = true;
  1363. m_tx_basepos = m_prop.initial_sprite_basepos;
  1364. }
  1365. if (m_is_local_player) {
  1366. LocalPlayer *player = m_env->getLocalPlayer();
  1367. player->makes_footstep_sound = m_prop.makes_footstep_sound;
  1368. aabb3f collision_box = m_prop.collisionbox;
  1369. collision_box.MinEdge *= BS;
  1370. collision_box.MaxEdge *= BS;
  1371. player->setCollisionbox(collision_box);
  1372. player->setEyeHeight(m_prop.eye_height);
  1373. player->setZoomFOV(m_prop.zoom_fov);
  1374. }
  1375. if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty())
  1376. m_prop.nametag = m_name;
  1377. if (expire_visuals) {
  1378. expireVisuals();
  1379. } else {
  1380. infostream << "GenericCAO: properties updated but expiring visuals"
  1381. << " not necessary" << std::endl;
  1382. if (textures_changed) {
  1383. // don't update while punch texture modifier is active
  1384. if (m_reset_textures_timer < 0)
  1385. updateTextures(m_current_texture_modifier);
  1386. }
  1387. updateNametag();
  1388. }
  1389. } else if (cmd == AO_CMD_UPDATE_POSITION) {
  1390. // Not sent by the server if this object is an attachment.
  1391. // We might however get here if the server notices the object being detached before the client.
  1392. m_position = readV3F32(is);
  1393. m_velocity = readV3F32(is);
  1394. m_acceleration = readV3F32(is);
  1395. m_rotation = readV3F32(is);
  1396. m_rotation = wrapDegrees_0_360_v3f(m_rotation);
  1397. bool do_interpolate = readU8(is);
  1398. bool is_end_position = readU8(is);
  1399. float update_interval = readF32(is);
  1400. // Place us a bit higher if we're physical, to not sink into
  1401. // the ground due to sucky collision detection...
  1402. if(m_prop.physical)
  1403. m_position += v3f(0,0.002,0);
  1404. if(getParent() != NULL) // Just in case
  1405. return;
  1406. if(do_interpolate)
  1407. {
  1408. if(!m_prop.physical)
  1409. pos_translator.update(m_position, is_end_position, update_interval);
  1410. } else {
  1411. pos_translator.init(m_position);
  1412. }
  1413. rot_translator.update(m_rotation, false, update_interval);
  1414. updateNodePos();
  1415. } else if (cmd == AO_CMD_SET_TEXTURE_MOD) {
  1416. std::string mod = deSerializeString(is);
  1417. // immediately reset a engine issued texture modifier if a mod sends a different one
  1418. if (m_reset_textures_timer > 0) {
  1419. m_reset_textures_timer = -1;
  1420. updateTextures(m_previous_texture_modifier);
  1421. }
  1422. updateTextures(mod);
  1423. } else if (cmd == AO_CMD_SET_SPRITE) {
  1424. v2s16 p = readV2S16(is);
  1425. int num_frames = readU16(is);
  1426. float framelength = readF32(is);
  1427. bool select_horiz_by_yawpitch = readU8(is);
  1428. m_tx_basepos = p;
  1429. m_anim_num_frames = num_frames;
  1430. m_anim_framelength = framelength;
  1431. m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
  1432. updateTexturePos();
  1433. } else if (cmd == AO_CMD_SET_PHYSICS_OVERRIDE) {
  1434. float override_speed = readF32(is);
  1435. float override_jump = readF32(is);
  1436. float override_gravity = readF32(is);
  1437. // these are sent inverted so we get true when the server sends nothing
  1438. bool sneak = !readU8(is);
  1439. bool sneak_glitch = !readU8(is);
  1440. bool new_move = !readU8(is);
  1441. if(m_is_local_player)
  1442. {
  1443. LocalPlayer *player = m_env->getLocalPlayer();
  1444. player->physics_override_speed = override_speed;
  1445. player->physics_override_jump = override_jump;
  1446. player->physics_override_gravity = override_gravity;
  1447. player->physics_override_sneak = sneak;
  1448. player->physics_override_sneak_glitch = sneak_glitch;
  1449. player->physics_override_new_move = new_move;
  1450. }
  1451. } else if (cmd == AO_CMD_SET_ANIMATION) {
  1452. // TODO: change frames send as v2s32 value
  1453. v2f range = readV2F32(is);
  1454. if (!m_is_local_player) {
  1455. m_animation_range = v2s32((s32)range.X, (s32)range.Y);
  1456. m_animation_speed = readF32(is);
  1457. m_animation_blend = readF32(is);
  1458. // these are sent inverted so we get true when the server sends nothing
  1459. m_animation_loop = !readU8(is);
  1460. updateAnimation();
  1461. } else {
  1462. LocalPlayer *player = m_env->getLocalPlayer();
  1463. if(player->last_animation == NO_ANIM)
  1464. {
  1465. m_animation_range = v2s32((s32)range.X, (s32)range.Y);
  1466. m_animation_speed = readF32(is);
  1467. m_animation_blend = readF32(is);
  1468. // these are sent inverted so we get true when the server sends nothing
  1469. m_animation_loop = !readU8(is);
  1470. }
  1471. // update animation only if local animations present
  1472. // and received animation is unknown (except idle animation)
  1473. bool is_known = false;
  1474. for (int i = 1;i<4;i++)
  1475. {
  1476. if(m_animation_range.Y == player->local_animations[i].Y)
  1477. is_known = true;
  1478. }
  1479. if(!is_known ||
  1480. (player->local_animations[1].Y + player->local_animations[2].Y < 1))
  1481. {
  1482. updateAnimation();
  1483. }
  1484. }
  1485. } else if (cmd == AO_CMD_SET_ANIMATION_SPEED) {
  1486. m_animation_speed = readF32(is);
  1487. updateAnimationSpeed();
  1488. } else if (cmd == AO_CMD_SET_BONE_POSITION) {
  1489. std::string bone = deSerializeString(is);
  1490. v3f position = readV3F32(is);
  1491. v3f rotation = readV3F32(is);
  1492. m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
  1493. // updateBonePosition(); now called every step
  1494. } else if (cmd == AO_CMD_ATTACH_TO) {
  1495. u16 parent_id = readS16(is);
  1496. std::string bone = deSerializeString(is);
  1497. v3f position = readV3F32(is);
  1498. v3f rotation = readV3F32(is);
  1499. setAttachment(parent_id, bone, position, rotation);
  1500. // localplayer itself can't be attached to localplayer
  1501. if (!m_is_local_player)
  1502. m_is_visible = !m_attached_to_local;
  1503. } else if (cmd == AO_CMD_PUNCHED) {
  1504. u16 result_hp = readU16(is);
  1505. // Use this instead of the send damage to not interfere with prediction
  1506. s32 damage = (s32)m_hp - (s32)result_hp;
  1507. m_hp = result_hp;
  1508. if (m_is_local_player)
  1509. m_env->getLocalPlayer()->hp = m_hp;
  1510. if (damage > 0)
  1511. {
  1512. if (m_hp == 0)
  1513. {
  1514. // TODO: Execute defined fast response
  1515. // As there is no definition, make a smoke puff
  1516. ClientSimpleObject *simple = createSmokePuff(
  1517. m_smgr, m_env, m_position,
  1518. v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS);
  1519. m_env->addSimpleObject(simple);
  1520. } else if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) {
  1521. m_reset_textures_timer = 0.05;
  1522. if(damage >= 2)
  1523. m_reset_textures_timer += 0.05 * damage;
  1524. updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier);
  1525. }
  1526. }
  1527. if (m_hp == 0) {
  1528. // Same as 'Server::DiePlayer'
  1529. clearParentAttachment();
  1530. // Same as 'ObjectRef::l_remove'
  1531. if (!m_is_player)
  1532. clearChildAttachments();
  1533. }
  1534. } else if (cmd == AO_CMD_UPDATE_ARMOR_GROUPS) {
  1535. m_armor_groups.clear();
  1536. int armor_groups_size = readU16(is);
  1537. for(int i=0; i<armor_groups_size; i++)
  1538. {
  1539. std::string name = deSerializeString(is);
  1540. int rating = readS16(is);
  1541. m_armor_groups[name] = rating;
  1542. }
  1543. } else if (cmd == AO_CMD_SPAWN_INFANT) {
  1544. u16 child_id = readU16(is);
  1545. u8 type = readU8(is); // maybe this will be useful later
  1546. (void)type;
  1547. addAttachmentChild(child_id);
  1548. } else if (cmd == AO_CMD_OBSOLETE1) {
  1549. // Don't do anything and also don't log a warning
  1550. } else {
  1551. warningstream << FUNCTION_NAME
  1552. << ": unknown command or outdated client \""
  1553. << +cmd << "\"" << std::endl;
  1554. }
  1555. }
  1556. /* \pre punchitem != NULL
  1557. */
  1558. bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
  1559. float time_from_last_punch)
  1560. {
  1561. assert(punchitem); // pre-condition
  1562. const ToolCapabilities *toolcap =
  1563. &punchitem->getToolCapabilities(m_client->idef());
  1564. PunchDamageResult result = getPunchDamage(
  1565. m_armor_groups,
  1566. toolcap,
  1567. punchitem,
  1568. time_from_last_punch);
  1569. if(result.did_punch && result.damage != 0)
  1570. {
  1571. if(result.damage < m_hp)
  1572. {
  1573. m_hp -= result.damage;
  1574. } else {
  1575. m_hp = 0;
  1576. // TODO: Execute defined fast response
  1577. // As there is no definition, make a smoke puff
  1578. ClientSimpleObject *simple = createSmokePuff(
  1579. m_smgr, m_env, m_position,
  1580. v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS);
  1581. m_env->addSimpleObject(simple);
  1582. }
  1583. if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) {
  1584. m_reset_textures_timer = 0.05;
  1585. if (result.damage >= 2)
  1586. m_reset_textures_timer += 0.05 * result.damage;
  1587. updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier);
  1588. }
  1589. }
  1590. return false;
  1591. }
  1592. std::string GenericCAO::debugInfoText()
  1593. {
  1594. std::ostringstream os(std::ios::binary);
  1595. os<<"GenericCAO hp="<<m_hp<<"\n";
  1596. os<<"armor={";
  1597. for(ItemGroupList::const_iterator i = m_armor_groups.begin();
  1598. i != m_armor_groups.end(); ++i)
  1599. {
  1600. os<<i->first<<"="<<i->second<<", ";
  1601. }
  1602. os<<"}";
  1603. return os.str();
  1604. }
  1605. // Prototype
  1606. GenericCAO proto_GenericCAO(NULL, NULL);