Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include <ICameraSceneNode.h>
21 : #include <ITextSceneNode.h>
22 : #include <IBillboardSceneNode.h>
23 : #include <IMeshManipulator.h>
24 : #include <IAnimatedMeshSceneNode.h>
25 : #include <IBoneSceneNode.h>
26 : #include "content_cao.h"
27 : #include "util/numeric.h" // For IntervalLimiter
28 : #include "util/serialize.h"
29 : #include "util/mathconstants.h"
30 : #include "client/tile.h"
31 : #include "environment.h"
32 : #include "collision.h"
33 : #include "settings.h"
34 : #include "serialization.h" // For decompressZlib
35 : #include "gamedef.h"
36 : #include "clientobject.h"
37 : #include "mesh.h"
38 : #include "itemdef.h"
39 : #include "tool.h"
40 : #include "content_cso.h"
41 : #include "sound.h"
42 : #include "nodedef.h"
43 : #include "localplayer.h"
44 : #include "map.h"
45 : #include "camera.h" // CameraModes
46 : #include "wieldmesh.h"
47 : #include "log.h"
48 :
49 : class Settings;
50 : struct ToolCapabilities;
51 :
52 : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
53 :
54 1 : std::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
55 :
56 45 : SmoothTranslator::SmoothTranslator():
57 : vect_old(0,0,0),
58 : vect_show(0,0,0),
59 : vect_aim(0,0,0),
60 : anim_counter(0),
61 : anim_time(0),
62 : anim_time_counter(0),
63 45 : aim_is_end(true)
64 45 : {}
65 :
66 49 : void SmoothTranslator::init(v3f vect)
67 : {
68 49 : vect_old = vect;
69 49 : vect_show = vect;
70 49 : vect_aim = vect;
71 49 : anim_counter = 0;
72 49 : anim_time = 0;
73 49 : anim_time_counter = 0;
74 49 : aim_is_end = true;
75 49 : }
76 :
77 0 : void SmoothTranslator::sharpen()
78 : {
79 0 : init(vect_show);
80 0 : }
81 :
82 26425 : void SmoothTranslator::update(v3f vect_new, bool is_end_position, float update_interval)
83 : {
84 26425 : aim_is_end = is_end_position;
85 26425 : vect_old = vect_show;
86 26425 : vect_aim = vect_new;
87 26425 : if(update_interval > 0)
88 : {
89 26417 : anim_time = update_interval;
90 : } else {
91 8 : if(anim_time < 0.001 || anim_time > 1.0)
92 8 : anim_time = anim_time_counter;
93 : else
94 0 : anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
95 : }
96 26425 : anim_time_counter = 0;
97 26425 : anim_counter = 0;
98 26425 : }
99 :
100 26190 : void SmoothTranslator::translate(f32 dtime)
101 : {
102 26190 : anim_time_counter = anim_time_counter + dtime;
103 26190 : anim_counter = anim_counter + dtime;
104 26190 : v3f vect_move = vect_aim - vect_old;
105 26190 : f32 moveratio = 1.0;
106 26190 : if(anim_time > 0.001)
107 26186 : moveratio = anim_time_counter / anim_time;
108 : // Move a bit less than should, to avoid oscillation
109 26190 : moveratio = moveratio * 0.8;
110 26190 : float move_end = 1.5;
111 26190 : if(aim_is_end)
112 8179 : move_end = 1.0;
113 26190 : if(moveratio > move_end)
114 2 : moveratio = move_end;
115 26190 : vect_show = vect_old + vect_move * moveratio;
116 26190 : }
117 :
118 0 : bool SmoothTranslator::is_moving()
119 : {
120 0 : return ((anim_time_counter / anim_time) < 1.4);
121 : }
122 :
123 : /*
124 : Other stuff
125 : */
126 :
127 0 : static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
128 : float txs, float tys, int col, int row)
129 : {
130 0 : video::SMaterial& material = bill->getMaterial(0);
131 0 : core::matrix4& matrix = material.getTextureMatrix(0);
132 0 : matrix.setTextureTranslate(txs*col, tys*row);
133 0 : matrix.setTextureScale(txs, tys);
134 0 : }
135 :
136 : /*
137 : TestCAO
138 : */
139 :
140 : class TestCAO : public ClientActiveObject
141 : {
142 : public:
143 : TestCAO(IGameDef *gamedef, ClientEnvironment *env);
144 : virtual ~TestCAO();
145 :
146 1 : ActiveObjectType getType() const
147 : {
148 1 : return ACTIVEOBJECT_TYPE_TEST;
149 : }
150 :
151 : static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
152 :
153 : void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
154 : IrrlichtDevice *irr);
155 : void removeFromScene(bool permanent);
156 : void updateLight(u8 light_at_pos);
157 : v3s16 getLightPosition();
158 : void updateNodePos();
159 :
160 : void step(float dtime, ClientEnvironment *env);
161 :
162 : void processMessage(const std::string &data);
163 :
164 0 : bool getCollisionBox(aabb3f *toset) { return false; }
165 : private:
166 : scene::IMeshSceneNode *m_node;
167 : v3f m_position;
168 : };
169 :
170 : // Prototype
171 1 : TestCAO proto_TestCAO(NULL, NULL);
172 :
173 1 : TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env):
174 : ClientActiveObject(0, gamedef, env),
175 : m_node(NULL),
176 1 : m_position(v3f(0,10*BS,0))
177 : {
178 1 : ClientActiveObject::registerType(getType(), create);
179 1 : }
180 :
181 1 : TestCAO::~TestCAO()
182 : {
183 1 : }
184 :
185 0 : ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env)
186 : {
187 0 : return new TestCAO(gamedef, env);
188 : }
189 :
190 0 : void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
191 : IrrlichtDevice *irr)
192 : {
193 0 : if(m_node != NULL)
194 0 : return;
195 :
196 : //video::IVideoDriver* driver = smgr->getVideoDriver();
197 :
198 0 : scene::SMesh *mesh = new scene::SMesh();
199 0 : scene::IMeshBuffer *buf = new scene::SMeshBuffer();
200 0 : video::SColor c(255,255,255,255);
201 : video::S3DVertex vertices[4] =
202 : {
203 : video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
204 : video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
205 : video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
206 : video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
207 0 : };
208 0 : u16 indices[] = {0,1,2,2,3,0};
209 0 : buf->append(vertices, 4, indices, 6);
210 : // Set material
211 0 : buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
212 0 : buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
213 0 : buf->getMaterial().setTexture(0, tsrc->getTextureForMesh("rat.png"));
214 0 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
215 0 : buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
216 0 : buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
217 : // Add to mesh
218 0 : mesh->addMeshBuffer(buf);
219 0 : buf->drop();
220 0 : m_node = smgr->addMeshSceneNode(mesh, NULL);
221 0 : mesh->drop();
222 0 : updateNodePos();
223 : }
224 :
225 0 : void TestCAO::removeFromScene(bool permanent)
226 : {
227 0 : if(m_node == NULL)
228 0 : return;
229 :
230 0 : m_node->remove();
231 0 : m_node = NULL;
232 : }
233 :
234 0 : void TestCAO::updateLight(u8 light_at_pos)
235 : {
236 0 : }
237 :
238 0 : v3s16 TestCAO::getLightPosition()
239 : {
240 0 : return floatToInt(m_position, BS);
241 : }
242 :
243 0 : void TestCAO::updateNodePos()
244 : {
245 0 : if(m_node == NULL)
246 0 : return;
247 :
248 0 : m_node->setPosition(m_position);
249 : //m_node->setRotation(v3f(0, 45, 0));
250 : }
251 :
252 0 : void TestCAO::step(float dtime, ClientEnvironment *env)
253 : {
254 0 : if(m_node)
255 : {
256 0 : v3f rot = m_node->getRotation();
257 : //infostream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
258 0 : rot.Y += dtime * 180;
259 0 : m_node->setRotation(rot);
260 : }
261 0 : }
262 :
263 0 : void TestCAO::processMessage(const std::string &data)
264 : {
265 0 : infostream<<"TestCAO: Got data: "<<data<<std::endl;
266 0 : std::istringstream is(data, std::ios::binary);
267 : u16 cmd;
268 0 : is>>cmd;
269 0 : if(cmd == 0)
270 : {
271 0 : v3f newpos;
272 0 : is>>newpos.X;
273 0 : is>>newpos.Y;
274 0 : is>>newpos.Z;
275 0 : m_position = newpos;
276 0 : updateNodePos();
277 : }
278 0 : }
279 :
280 : /*
281 : ItemCAO
282 : */
283 :
284 : class ItemCAO : public ClientActiveObject
285 : {
286 : public:
287 : ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
288 : virtual ~ItemCAO();
289 :
290 1 : ActiveObjectType getType() const
291 : {
292 1 : return ACTIVEOBJECT_TYPE_ITEM;
293 : }
294 :
295 : static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
296 :
297 : void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
298 : IrrlichtDevice *irr);
299 : void removeFromScene(bool permanent);
300 : void updateLight(u8 light_at_pos);
301 : v3s16 getLightPosition();
302 : void updateNodePos();
303 : void updateInfoText();
304 : void updateTexture();
305 :
306 : void step(float dtime, ClientEnvironment *env);
307 :
308 : void processMessage(const std::string &data);
309 :
310 : void initialize(const std::string &data);
311 :
312 0 : core::aabbox3d<f32>* getSelectionBox()
313 0 : {return &m_selection_box;}
314 0 : v3f getPosition()
315 0 : {return m_position;}
316 :
317 0 : std::string infoText()
318 0 : {return m_infotext;}
319 :
320 0 : bool getCollisionBox(aabb3f *toset) { return false; }
321 : private:
322 : core::aabbox3d<f32> m_selection_box;
323 : scene::IMeshSceneNode *m_node;
324 : v3f m_position;
325 : std::string m_itemstring;
326 : std::string m_infotext;
327 : };
328 :
329 : #include "inventory.h"
330 :
331 : // Prototype
332 1 : ItemCAO proto_ItemCAO(NULL, NULL);
333 :
334 1 : ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env):
335 : ClientActiveObject(0, gamedef, env),
336 : m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.),
337 : m_node(NULL),
338 1 : m_position(v3f(0,10*BS,0))
339 : {
340 1 : if(!gamedef && !env)
341 : {
342 1 : ClientActiveObject::registerType(getType(), create);
343 : }
344 1 : }
345 :
346 1 : ItemCAO::~ItemCAO()
347 : {
348 1 : }
349 :
350 0 : ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env)
351 : {
352 0 : return new ItemCAO(gamedef, env);
353 : }
354 :
355 0 : void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
356 : IrrlichtDevice *irr)
357 : {
358 0 : if(m_node != NULL)
359 0 : return;
360 :
361 : //video::IVideoDriver* driver = smgr->getVideoDriver();
362 :
363 0 : scene::SMesh *mesh = new scene::SMesh();
364 0 : scene::IMeshBuffer *buf = new scene::SMeshBuffer();
365 0 : video::SColor c(255,255,255,255);
366 : video::S3DVertex vertices[4] =
367 : {
368 : /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
369 : video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
370 : video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
371 : video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
372 : video::S3DVertex(BS/3.,0,0, 0,0,0, c, 0,1),
373 : video::S3DVertex(-BS/3.,0,0, 0,0,0, c, 1,1),
374 : video::S3DVertex(-BS/3.,0+BS*2./3.,0, 0,0,0, c, 1,0),
375 : video::S3DVertex(BS/3.,0+BS*2./3.,0, 0,0,0, c, 0,0),
376 0 : };
377 0 : u16 indices[] = {0,1,2,2,3,0};
378 0 : buf->append(vertices, 4, indices, 6);
379 : // Set material
380 0 : buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
381 0 : buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
382 : // Initialize with a generated placeholder texture
383 0 : buf->getMaterial().setTexture(0, tsrc->getTexture(""));
384 0 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
385 0 : buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
386 0 : buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
387 : // Add to mesh
388 0 : mesh->addMeshBuffer(buf);
389 0 : buf->drop();
390 0 : m_node = smgr->addMeshSceneNode(mesh, NULL);
391 0 : mesh->drop();
392 0 : updateNodePos();
393 :
394 : /*
395 : Update image of node
396 : */
397 :
398 0 : updateTexture();
399 : }
400 :
401 0 : void ItemCAO::removeFromScene(bool permanent)
402 : {
403 0 : if(m_node == NULL)
404 0 : return;
405 :
406 0 : m_node->remove();
407 0 : m_node = NULL;
408 : }
409 :
410 0 : void ItemCAO::updateLight(u8 light_at_pos)
411 : {
412 0 : if(m_node == NULL)
413 0 : return;
414 :
415 0 : u8 li = decode_light(light_at_pos);
416 0 : video::SColor color(255,li,li,li);
417 0 : setMeshColor(m_node->getMesh(), color);
418 : }
419 :
420 0 : v3s16 ItemCAO::getLightPosition()
421 : {
422 0 : return floatToInt(m_position + v3f(0,0.5*BS,0), BS);
423 : }
424 :
425 0 : void ItemCAO::updateNodePos()
426 : {
427 0 : if(m_node == NULL)
428 0 : return;
429 :
430 0 : m_node->setPosition(m_position);
431 : }
432 :
433 0 : void ItemCAO::updateInfoText()
434 : {
435 : try{
436 0 : IItemDefManager *idef = m_gamedef->idef();
437 0 : ItemStack item;
438 0 : item.deSerialize(m_itemstring, idef);
439 0 : if(item.isKnown(idef))
440 0 : m_infotext = item.getDefinition(idef).description;
441 : else
442 0 : m_infotext = "Unknown item: '" + m_itemstring + "'";
443 0 : if(item.count >= 2)
444 0 : m_infotext += " (" + itos(item.count) + ")";
445 : }
446 0 : catch(SerializationError &e)
447 : {
448 0 : m_infotext = "Unknown item: '" + m_itemstring + "'";
449 : }
450 0 : }
451 :
452 0 : void ItemCAO::updateTexture()
453 : {
454 0 : if(m_node == NULL)
455 0 : return;
456 :
457 : // Create an inventory item to see what is its image
458 0 : std::istringstream is(m_itemstring, std::ios_base::binary);
459 0 : video::ITexture *texture = NULL;
460 : try{
461 0 : IItemDefManager *idef = m_gamedef->idef();
462 0 : ItemStack item;
463 0 : item.deSerialize(is, idef);
464 0 : texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
465 : }
466 0 : catch(SerializationError &e)
467 : {
468 0 : infostream<<"WARNING: "<<__FUNCTION_NAME
469 0 : <<": error deSerializing itemstring \""
470 0 : <<m_itemstring<<std::endl;
471 : }
472 :
473 : // Set meshbuffer texture
474 0 : m_node->getMaterial(0).setTexture(0, texture);
475 : }
476 :
477 :
478 0 : void ItemCAO::step(float dtime, ClientEnvironment *env)
479 : {
480 0 : if(m_node)
481 : {
482 : /*v3f rot = m_node->getRotation();
483 : rot.Y += dtime * 120;
484 : m_node->setRotation(rot);*/
485 0 : LocalPlayer *player = env->getLocalPlayer();
486 : assert(player);
487 0 : v3f rot = m_node->getRotation();
488 0 : rot.Y = 180.0 - (player->getYaw());
489 0 : m_node->setRotation(rot);
490 : }
491 0 : }
492 :
493 0 : void ItemCAO::processMessage(const std::string &data)
494 : {
495 : //infostream<<"ItemCAO: Got message"<<std::endl;
496 0 : std::istringstream is(data, std::ios::binary);
497 : // command
498 0 : u8 cmd = readU8(is);
499 0 : if(cmd == 0)
500 : {
501 : // pos
502 0 : m_position = readV3F1000(is);
503 0 : updateNodePos();
504 : }
505 0 : if(cmd == 1)
506 : {
507 : // itemstring
508 0 : m_itemstring = deSerializeString(is);
509 0 : updateInfoText();
510 0 : updateTexture();
511 : }
512 0 : }
513 :
514 0 : void ItemCAO::initialize(const std::string &data)
515 : {
516 0 : infostream<<"ItemCAO: Got init data"<<std::endl;
517 :
518 : {
519 0 : std::istringstream is(data, std::ios::binary);
520 : // version
521 0 : u8 version = readU8(is);
522 : // check version
523 0 : if(version != 0)
524 0 : return;
525 : // pos
526 0 : m_position = readV3F1000(is);
527 : // itemstring
528 0 : m_itemstring = deSerializeString(is);
529 : }
530 :
531 0 : updateNodePos();
532 0 : updateInfoText();
533 : }
534 :
535 : /*
536 : GenericCAO
537 : */
538 :
539 : #include "genericobject.h"
540 :
541 45 : GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
542 : ClientActiveObject(0, gamedef, env),
543 : //
544 : m_is_player(false),
545 : m_is_local_player(false),
546 : //
547 : m_smgr(NULL),
548 : m_irr(NULL),
549 : m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
550 : m_meshnode(NULL),
551 : m_animated_meshnode(NULL),
552 : m_wield_meshnode(NULL),
553 : m_spritenode(NULL),
554 : m_nametag_color(video::SColor(255, 255, 255, 255)),
555 : m_textnode(NULL),
556 : m_position(v3f(0,10*BS,0)),
557 : m_velocity(v3f(0,0,0)),
558 : m_acceleration(v3f(0,0,0)),
559 : m_yaw(0),
560 : m_hp(1),
561 : m_tx_size(1,1),
562 : m_tx_basepos(0,0),
563 : m_initial_tx_basepos_set(false),
564 : m_tx_select_horiz_by_yawpitch(false),
565 : m_animation_range(v2s32(0,0)),
566 : m_animation_speed(15),
567 : m_animation_blend(0),
568 : m_animation_loop(true),
569 : m_bone_position(std::map<std::string, core::vector2d<v3f> >()),
570 : m_attachment_bone(""),
571 : m_attachment_position(v3f(0,0,0)),
572 : m_attachment_rotation(v3f(0,0,0)),
573 : m_attached_to_local(false),
574 : m_anim_frame(0),
575 : m_anim_num_frames(1),
576 : m_anim_framelength(0.2),
577 : m_anim_timer(0),
578 : m_reset_textures_timer(-1),
579 : m_visuals_expired(false),
580 : m_step_distance_counter(0),
581 : m_last_light(255),
582 45 : m_is_visible(false)
583 : {
584 45 : if(gamedef == NULL)
585 1 : ClientActiveObject::registerType(getType(), create);
586 45 : }
587 :
588 2889 : bool GenericCAO::getCollisionBox(aabb3f *toset)
589 : {
590 2889 : if (m_prop.physical)
591 : {
592 : //update collision box
593 200 : toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
594 200 : toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
595 :
596 200 : toset->MinEdge += m_position;
597 200 : toset->MaxEdge += m_position;
598 :
599 200 : return true;
600 : }
601 :
602 2689 : return false;
603 : }
604 :
605 200 : bool GenericCAO::collideWithObjects()
606 : {
607 200 : return m_prop.collideWithObjects;
608 : }
609 :
610 44 : void GenericCAO::initialize(const std::string &data)
611 : {
612 44 : infostream<<"GenericCAO: Got init data"<<std::endl;
613 88 : std::istringstream is(data, std::ios::binary);
614 44 : int num_messages = 0;
615 : // version
616 44 : u8 version = readU8(is);
617 : // check version
618 44 : if(version == 1) // In PROTOCOL_VERSION 14
619 : {
620 44 : m_name = deSerializeString(is);
621 44 : m_is_player = readU8(is);
622 44 : m_id = readS16(is);
623 44 : m_position = readV3F1000(is);
624 44 : m_yaw = readF1000(is);
625 44 : m_hp = readS16(is);
626 44 : num_messages = readU8(is);
627 : }
628 0 : else if(version == 0) // In PROTOCOL_VERSION 13
629 : {
630 0 : m_name = deSerializeString(is);
631 0 : m_is_player = readU8(is);
632 0 : m_position = readV3F1000(is);
633 0 : m_yaw = readF1000(is);
634 0 : m_hp = readS16(is);
635 0 : num_messages = readU8(is);
636 : }
637 : else
638 : {
639 0 : errorstream<<"GenericCAO: Unsupported init data version"
640 0 : <<std::endl;
641 0 : return;
642 : }
643 :
644 221 : for(int i=0; i<num_messages; i++)
645 : {
646 354 : std::string message = deSerializeLongString(is);
647 177 : processMessage(message);
648 : }
649 :
650 44 : pos_translator.init(m_position);
651 44 : updateNodePos();
652 :
653 44 : if(m_is_player)
654 : {
655 1 : Player *player = m_env->getPlayer(m_name.c_str());
656 1 : if(player && player->isLocal())
657 : {
658 1 : m_is_local_player = true;
659 1 : m_is_visible = false;
660 1 : LocalPlayer* localplayer = dynamic_cast<LocalPlayer*>(player);
661 :
662 : assert( localplayer != NULL );
663 1 : localplayer->setCAO(this);
664 : }
665 1 : m_env->addPlayerName(m_name.c_str());
666 : }
667 : }
668 :
669 134 : GenericCAO::~GenericCAO()
670 : {
671 45 : if(m_is_player)
672 : {
673 1 : m_env->removePlayerName(m_name.c_str());
674 : }
675 45 : removeFromScene(true);
676 89 : }
677 :
678 1345 : core::aabbox3d<f32>* GenericCAO::getSelectionBox()
679 : {
680 1345 : if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
681 1153 : return NULL;
682 192 : return &m_selection_box;
683 : }
684 :
685 293372 : v3f GenericCAO::getPosition()
686 : {
687 293372 : if (getParent() != NULL) {
688 0 : scene::ISceneNode *node = getSceneNode();
689 0 : if (node)
690 0 : return node->getAbsolutePosition();
691 : else
692 0 : return m_position;
693 : }
694 293372 : return pos_translator.vect_show;
695 : }
696 :
697 55221 : scene::ISceneNode* GenericCAO::getSceneNode()
698 : {
699 55221 : if (m_meshnode)
700 32421 : return m_meshnode;
701 22800 : if (m_animated_meshnode)
702 18815 : return m_animated_meshnode;
703 3985 : if (m_wield_meshnode)
704 3779 : return m_wield_meshnode;
705 206 : if (m_spritenode)
706 0 : return m_spritenode;
707 206 : return NULL;
708 : }
709 :
710 0 : scene::IMeshSceneNode* GenericCAO::getMeshSceneNode()
711 : {
712 0 : return m_meshnode;
713 : }
714 :
715 0 : scene::IAnimatedMeshSceneNode* GenericCAO::getAnimatedMeshSceneNode()
716 : {
717 0 : return m_animated_meshnode;
718 : }
719 :
720 0 : WieldMeshSceneNode* GenericCAO::getWieldMeshSceneNode()
721 : {
722 0 : return m_wield_meshnode;
723 : }
724 :
725 0 : scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode()
726 : {
727 0 : return m_spritenode;
728 : }
729 :
730 0 : void GenericCAO::setChildrenVisible(bool toset)
731 : {
732 0 : for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
733 0 : GenericCAO *obj = m_env->getGenericCAO(m_children[i]);
734 0 : if (obj) {
735 0 : obj->setVisible(toset);
736 : }
737 : }
738 0 : }
739 :
740 0 : void GenericCAO::setAttachments()
741 : {
742 0 : updateAttachments();
743 0 : }
744 :
745 402170 : ClientActiveObject* GenericCAO::getParent()
746 : {
747 402170 : ClientActiveObject *obj = NULL;
748 :
749 402170 : u16 attached_id = m_env->attachement_parent_ids[getId()];
750 :
751 402170 : if ((attached_id != 0) &&
752 0 : (attached_id != getId())) {
753 0 : obj = m_env->getActiveObject(attached_id);
754 : }
755 402170 : return obj;
756 : }
757 :
758 138 : void GenericCAO::removeFromScene(bool permanent)
759 : {
760 : // Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
761 138 : if((m_env != NULL) && (permanent))
762 : {
763 63 : for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
764 0 : u16 ci = m_children[i];
765 0 : if (m_env->attachement_parent_ids[ci] == getId()) {
766 0 : m_env->attachement_parent_ids[ci] = 0;
767 : }
768 : }
769 :
770 63 : m_env->attachement_parent_ids[getId()] = 0;
771 :
772 63 : LocalPlayer* player = m_env->getLocalPlayer();
773 63 : if (this == player->parent) {
774 0 : player->parent = NULL;
775 0 : player->isAttached = false;
776 : }
777 : }
778 :
779 138 : if(m_meshnode)
780 : {
781 67 : m_meshnode->remove();
782 67 : m_meshnode->drop();
783 67 : m_meshnode = NULL;
784 : }
785 138 : if(m_animated_meshnode)
786 : {
787 26 : m_animated_meshnode->remove();
788 26 : m_animated_meshnode->drop();
789 26 : m_animated_meshnode = NULL;
790 : }
791 138 : if(m_wield_meshnode)
792 : {
793 25 : m_wield_meshnode->remove();
794 25 : m_wield_meshnode->drop();
795 25 : m_wield_meshnode = NULL;
796 : }
797 138 : if(m_spritenode)
798 : {
799 0 : m_spritenode->remove();
800 0 : m_spritenode->drop();
801 0 : m_spritenode = NULL;
802 : }
803 138 : if (m_textnode)
804 : {
805 0 : m_textnode->remove();
806 0 : m_textnode->drop();
807 0 : m_textnode = NULL;
808 : }
809 138 : }
810 :
811 118 : void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
812 : IrrlichtDevice *irr)
813 : {
814 118 : m_smgr = smgr;
815 118 : m_irr = irr;
816 :
817 118 : if (getSceneNode() != NULL)
818 0 : return;
819 :
820 118 : m_visuals_expired = false;
821 :
822 118 : if(!m_prop.is_visible)
823 0 : return;
824 :
825 : //video::IVideoDriver* driver = smgr->getVideoDriver();
826 :
827 118 : if(m_prop.visual == "sprite")
828 : {
829 0 : infostream<<"GenericCAO::addToScene(): single_sprite"<<std::endl;
830 0 : m_spritenode = smgr->addBillboardSceneNode(
831 0 : NULL, v2f(1, 1), v3f(0,0,0), -1);
832 0 : m_spritenode->grab();
833 0 : m_spritenode->setMaterialTexture(0,
834 0 : tsrc->getTextureForMesh("unknown_node.png"));
835 0 : m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
836 0 : m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
837 0 : m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
838 0 : m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
839 0 : u8 li = m_last_light;
840 0 : m_spritenode->setColor(video::SColor(255,li,li,li));
841 0 : m_spritenode->setSize(m_prop.visual_size*BS);
842 : {
843 0 : const float txs = 1.0 / 1;
844 0 : const float tys = 1.0 / 1;
845 0 : setBillboardTextureMatrix(m_spritenode,
846 0 : txs, tys, 0, 0);
847 : }
848 : }
849 118 : else if(m_prop.visual == "upright_sprite") {
850 67 : scene::SMesh *mesh = new scene::SMesh();
851 67 : double dx = BS*m_prop.visual_size.X/2;
852 67 : double dy = BS*m_prop.visual_size.Y/2;
853 : { // Front
854 67 : scene::IMeshBuffer *buf = new scene::SMeshBuffer();
855 67 : u8 li = m_last_light;
856 67 : video::SColor c(255,li,li,li);
857 : video::S3DVertex vertices[4] =
858 : {
859 : video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
860 : video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
861 : video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
862 : video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
863 67 : };
864 67 : u16 indices[] = {0,1,2,2,3,0};
865 67 : buf->append(vertices, 4, indices, 6);
866 : // Set material
867 67 : buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
868 67 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
869 67 : buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
870 67 : buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
871 : // Add to mesh
872 67 : mesh->addMeshBuffer(buf);
873 67 : buf->drop();
874 : }
875 : { // Back
876 67 : scene::IMeshBuffer *buf = new scene::SMeshBuffer();
877 67 : u8 li = m_last_light;
878 67 : video::SColor c(255,li,li,li);
879 : video::S3DVertex vertices[4] =
880 : {
881 : video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1),
882 : video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1),
883 : video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0),
884 : video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0),
885 67 : };
886 67 : u16 indices[] = {0,1,2,2,3,0};
887 67 : buf->append(vertices, 4, indices, 6);
888 : // Set material
889 67 : buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
890 67 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
891 67 : buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
892 67 : buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
893 : // Add to mesh
894 67 : mesh->addMeshBuffer(buf);
895 67 : buf->drop();
896 : }
897 67 : m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
898 67 : m_meshnode->grab();
899 67 : mesh->drop();
900 : // Set it to use the materials of the meshbuffers directly.
901 : // This is needed for changing the texture in the future
902 67 : m_meshnode->setReadOnlyMaterials(true);
903 : }
904 51 : else if(m_prop.visual == "cube") {
905 0 : infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
906 0 : scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
907 0 : m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
908 0 : m_meshnode->grab();
909 0 : mesh->drop();
910 :
911 0 : m_meshnode->setScale(v3f(m_prop.visual_size.X,
912 : m_prop.visual_size.Y,
913 0 : m_prop.visual_size.X));
914 0 : u8 li = m_last_light;
915 0 : setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
916 :
917 0 : m_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
918 0 : m_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
919 0 : m_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
920 0 : m_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
921 : }
922 51 : else if(m_prop.visual == "mesh") {
923 26 : infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
924 26 : scene::IAnimatedMesh *mesh = m_gamedef->getMesh(m_prop.mesh);
925 26 : if(mesh)
926 : {
927 26 : m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
928 26 : m_animated_meshnode->grab();
929 26 : mesh->drop(); // The scene node took hold of it
930 26 : m_animated_meshnode->animateJoints(); // Needed for some animations
931 104 : m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
932 : m_prop.visual_size.Y,
933 78 : m_prop.visual_size.X));
934 26 : u8 li = m_last_light;
935 26 : setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
936 :
937 26 : m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
938 26 : m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
939 26 : m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
940 26 : m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
941 : }
942 : else
943 0 : errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
944 : }
945 25 : else if(m_prop.visual == "wielditem") {
946 25 : infostream<<"GenericCAO::addToScene(): wielditem"<<std::endl;
947 25 : infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
948 25 : if(m_prop.textures.size() >= 1){
949 25 : infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
950 25 : IItemDefManager *idef = m_gamedef->idef();
951 50 : ItemStack item(m_prop.textures[0], 1, 0, "", idef);
952 :
953 : m_wield_meshnode = new WieldMeshSceneNode(
954 25 : smgr->getRootSceneNode(), smgr, -1);
955 25 : m_wield_meshnode->setItem(item, m_gamedef);
956 :
957 125 : m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2,
958 25 : m_prop.visual_size.Y/2,
959 75 : m_prop.visual_size.X/2));
960 25 : u8 li = m_last_light;
961 25 : m_wield_meshnode->setColor(video::SColor(255,li,li,li));
962 : }
963 : } else {
964 0 : infostream<<"GenericCAO::addToScene(): \""<<m_prop.visual
965 0 : <<"\" not supported"<<std::endl;
966 : }
967 118 : updateTextures("");
968 :
969 118 : scene::ISceneNode *node = getSceneNode();
970 118 : if (node && m_is_player && !m_is_local_player) {
971 : // Add a text node for showing the name
972 0 : gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
973 0 : std::wstring wname = narrow_to_wide(m_name);
974 0 : m_textnode = smgr->addTextSceneNode(gui->getSkin()->getFont(),
975 0 : wname.c_str(), m_nametag_color, node);
976 0 : m_textnode->grab();
977 0 : m_textnode->setPosition(v3f(0, BS*1.1, 0));
978 : }
979 :
980 118 : updateNodePos();
981 118 : updateAnimation();
982 118 : updateBonePosition();
983 118 : updateAttachments();
984 : }
985 :
986 3554 : void GenericCAO::updateLight(u8 light_at_pos)
987 : {
988 3554 : u8 li = decode_light(light_at_pos);
989 3554 : if(li != m_last_light)
990 : {
991 91 : m_last_light = li;
992 91 : video::SColor color(255,li,li,li);
993 91 : if(m_meshnode)
994 10 : setMeshColor(m_meshnode->getMesh(), color);
995 91 : if(m_animated_meshnode)
996 66 : setMeshColor(m_animated_meshnode->getMesh(), color);
997 91 : if(m_wield_meshnode)
998 15 : m_wield_meshnode->setColor(color);
999 91 : if(m_spritenode)
1000 0 : m_spritenode->setColor(color);
1001 : }
1002 3554 : }
1003 :
1004 3554 : v3s16 GenericCAO::getLightPosition()
1005 : {
1006 3554 : return floatToInt(m_position, BS);
1007 : }
1008 :
1009 28598 : void GenericCAO::updateNodePos()
1010 : {
1011 28598 : if (getParent() != NULL)
1012 0 : return;
1013 :
1014 28598 : scene::ISceneNode *node = getSceneNode();
1015 :
1016 28598 : if (node) {
1017 28554 : v3s16 camera_offset = m_env->getCameraOffset();
1018 28554 : node->setPosition(pos_translator.vect_show - intToFloat(camera_offset, BS));
1019 28554 : if (node != m_spritenode) { // rotate if not a sprite
1020 28554 : v3f rot = node->getRotation();
1021 28554 : rot.Y = -m_yaw;
1022 28554 : node->setRotation(rot);
1023 : }
1024 : }
1025 : }
1026 :
1027 26190 : void GenericCAO::step(float dtime, ClientEnvironment *env)
1028 : {
1029 : // Handel model of local player instantly to prevent lags
1030 26190 : if(m_is_local_player)
1031 : {
1032 1166 : LocalPlayer *player = m_env->getLocalPlayer();
1033 :
1034 1166 : if (m_is_visible)
1035 : {
1036 0 : int old_anim = player->last_animation;
1037 0 : float old_anim_speed = player->last_animation_speed;
1038 0 : m_position = player->getPosition() + v3f(0,BS,0);
1039 0 : m_velocity = v3f(0,0,0);
1040 0 : m_acceleration = v3f(0,0,0);
1041 0 : pos_translator.vect_show = m_position;
1042 0 : m_yaw = player->getYaw();
1043 0 : PlayerControl controls = player->getPlayerControl();
1044 :
1045 0 : bool walking = false;
1046 0 : if(controls.up || controls.down || controls.left || controls.right)
1047 0 : walking = true;
1048 :
1049 0 : f32 new_speed = player->local_animation_speed;
1050 0 : v2s32 new_anim = v2s32(0,0);
1051 0 : bool allow_update = false;
1052 :
1053 : // increase speed if using fast or flying fast
1054 0 : if((g_settings->getBool("fast_move") &&
1055 0 : m_gamedef->checkLocalPrivilege("fast")) &&
1056 0 : (controls.aux1 ||
1057 0 : (!player->touching_ground &&
1058 0 : g_settings->getBool("free_move") &&
1059 0 : m_gamedef->checkLocalPrivilege("fly"))))
1060 0 : new_speed *= 1.5;
1061 : // slowdown speed if sneeking
1062 0 : if(controls.sneak && walking)
1063 0 : new_speed /= 2;
1064 :
1065 0 : if(walking && (controls.LMB || controls.RMB))
1066 : {
1067 0 : new_anim = player->local_animations[3];
1068 0 : player->last_animation = WD_ANIM;
1069 0 : } else if(walking) {
1070 0 : new_anim = player->local_animations[1];
1071 0 : player->last_animation = WALK_ANIM;
1072 0 : } else if(controls.LMB || controls.RMB) {
1073 0 : new_anim = player->local_animations[2];
1074 0 : player->last_animation = DIG_ANIM;
1075 : }
1076 :
1077 : // Apply animations if input detected and not attached
1078 : // or set idle animation
1079 0 : if ((new_anim.X + new_anim.Y) > 0 && !player->isAttached)
1080 : {
1081 0 : allow_update = true;
1082 0 : m_animation_range = new_anim;
1083 0 : m_animation_speed = new_speed;
1084 0 : player->last_animation_speed = m_animation_speed;
1085 : } else {
1086 0 : player->last_animation = NO_ANIM;
1087 :
1088 0 : if (old_anim != NO_ANIM)
1089 : {
1090 0 : m_animation_range = player->local_animations[0];
1091 0 : updateAnimation();
1092 : }
1093 : }
1094 :
1095 : // Update local player animations
1096 0 : if ((player->last_animation != old_anim ||
1097 0 : m_animation_speed != old_anim_speed) &&
1098 0 : player->last_animation != NO_ANIM && allow_update)
1099 0 : updateAnimation();
1100 :
1101 : }
1102 : }
1103 :
1104 26190 : if(m_visuals_expired && m_smgr && m_irr){
1105 74 : m_visuals_expired = false;
1106 :
1107 : // Attachments, part 1: All attached objects must be unparented first,
1108 : // or Irrlicht causes a segmentation fault
1109 222 : for(std::vector<u16>::iterator ci = m_children.begin();
1110 148 : ci != m_children.end();)
1111 : {
1112 0 : if (m_env->attachement_parent_ids[*ci] != getId()) {
1113 0 : ci = m_children.erase(ci);
1114 0 : continue;
1115 : }
1116 0 : ClientActiveObject *obj = m_env->getActiveObject(*ci);
1117 0 : if (obj) {
1118 0 : scene::ISceneNode *child_node = obj->getSceneNode();
1119 0 : if (child_node)
1120 0 : child_node->setParent(m_smgr->getRootSceneNode());
1121 : }
1122 0 : ++ci;
1123 : }
1124 :
1125 74 : removeFromScene(false);
1126 74 : addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
1127 :
1128 : // Attachments, part 2: Now that the parent has been refreshed, put its attachments back
1129 74 : for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
1130 : // Get the object of the child
1131 0 : ClientActiveObject *obj = m_env->getActiveObject(m_children[i]);
1132 0 : if (obj)
1133 0 : obj->setAttachments();
1134 : }
1135 : }
1136 :
1137 : // Make sure m_is_visible is always applied
1138 26190 : scene::ISceneNode *node = getSceneNode();
1139 26190 : if (node)
1140 26190 : node->setVisible(m_is_visible);
1141 :
1142 26190 : if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
1143 : {
1144 : // Set these for later
1145 0 : m_position = getPosition();
1146 0 : m_velocity = v3f(0,0,0);
1147 0 : m_acceleration = v3f(0,0,0);
1148 0 : pos_translator.vect_show = m_position;
1149 :
1150 0 : if(m_is_local_player) // Update local player attachment position
1151 : {
1152 0 : LocalPlayer *player = m_env->getLocalPlayer();
1153 0 : player->overridePosition = getParent()->getPosition();
1154 0 : m_env->getLocalPlayer()->parent = getParent();
1155 : }
1156 : } else {
1157 26190 : v3f lastpos = pos_translator.vect_show;
1158 :
1159 26190 : if(m_prop.physical)
1160 : {
1161 7835 : core::aabbox3d<f32> box = m_prop.collisionbox;
1162 7835 : box.MinEdge *= BS;
1163 7835 : box.MaxEdge *= BS;
1164 15670 : collisionMoveResult moveresult;
1165 7835 : f32 pos_max_d = BS*0.125; // Distance per iteration
1166 7835 : v3f p_pos = m_position;
1167 7835 : v3f p_velocity = m_velocity;
1168 7835 : v3f p_acceleration = m_acceleration;
1169 15670 : moveresult = collisionMoveSimple(env,env->getGameDef(),
1170 : pos_max_d, box, m_prop.stepheight, dtime,
1171 : p_pos, p_velocity, p_acceleration,
1172 15670 : this, m_prop.collideWithObjects);
1173 : // Apply results
1174 7835 : m_position = p_pos;
1175 7835 : m_velocity = p_velocity;
1176 7835 : m_acceleration = p_acceleration;
1177 :
1178 7835 : bool is_end_position = moveresult.collides;
1179 7835 : pos_translator.update(m_position, is_end_position, dtime);
1180 7835 : pos_translator.translate(dtime);
1181 7835 : updateNodePos();
1182 : } else {
1183 18355 : m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
1184 18355 : m_velocity += dtime * m_acceleration;
1185 36710 : pos_translator.update(m_position, pos_translator.aim_is_end,
1186 18355 : pos_translator.anim_time);
1187 18355 : pos_translator.translate(dtime);
1188 18355 : updateNodePos();
1189 : }
1190 :
1191 26190 : float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
1192 26190 : m_step_distance_counter += moved;
1193 26190 : if(m_step_distance_counter > 1.5*BS)
1194 : {
1195 107 : m_step_distance_counter = 0;
1196 107 : if(!m_is_local_player && m_prop.makes_footstep_sound)
1197 : {
1198 26 : INodeDefManager *ndef = m_gamedef->ndef();
1199 52 : v3s16 p = floatToInt(getPosition() + v3f(0,
1200 52 : (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
1201 26 : MapNode n = m_env->getMap().getNodeNoEx(p);
1202 52 : SimpleSoundSpec spec = ndef->get(n).sound_footstep;
1203 26 : m_gamedef->sound()->playSoundAt(spec, false, getPosition());
1204 : }
1205 : }
1206 : }
1207 :
1208 26190 : m_anim_timer += dtime;
1209 26190 : if(m_anim_timer >= m_anim_framelength)
1210 : {
1211 3650 : m_anim_timer -= m_anim_framelength;
1212 3650 : m_anim_frame++;
1213 3650 : if(m_anim_frame >= m_anim_num_frames)
1214 3650 : m_anim_frame = 0;
1215 : }
1216 :
1217 26190 : updateTexturePos();
1218 :
1219 26190 : if(m_reset_textures_timer >= 0)
1220 : {
1221 0 : m_reset_textures_timer -= dtime;
1222 0 : if(m_reset_textures_timer <= 0){
1223 0 : m_reset_textures_timer = -1;
1224 0 : updateTextures("");
1225 : }
1226 : }
1227 26190 : if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001)
1228 : {
1229 1083 : m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
1230 1083 : updateNodePos();
1231 : }
1232 :
1233 26190 : if (getParent() == NULL && m_prop.automatic_face_movement_dir &&
1234 0 : (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
1235 : {
1236 0 : m_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
1237 0 : + m_prop.automatic_face_movement_dir_offset;
1238 0 : updateNodePos();
1239 : }
1240 26190 : }
1241 :
1242 26190 : void GenericCAO::updateTexturePos()
1243 : {
1244 26190 : if(m_spritenode)
1245 : {
1246 : scene::ICameraSceneNode* camera =
1247 0 : m_spritenode->getSceneManager()->getActiveCamera();
1248 0 : if(!camera)
1249 0 : return;
1250 0 : v3f cam_to_entity = m_spritenode->getAbsolutePosition()
1251 0 : - camera->getAbsolutePosition();
1252 0 : cam_to_entity.normalize();
1253 :
1254 0 : int row = m_tx_basepos.Y;
1255 0 : int col = m_tx_basepos.X;
1256 :
1257 0 : if(m_tx_select_horiz_by_yawpitch)
1258 : {
1259 0 : if(cam_to_entity.Y > 0.75)
1260 0 : col += 5;
1261 0 : else if(cam_to_entity.Y < -0.75)
1262 0 : col += 4;
1263 : else{
1264 : float mob_dir =
1265 0 : atan2(cam_to_entity.Z, cam_to_entity.X) / M_PI * 180.;
1266 0 : float dir = mob_dir - m_yaw;
1267 0 : dir = wrapDegrees_180(dir);
1268 : //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
1269 0 : if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
1270 0 : col += 2;
1271 0 : else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
1272 0 : col += 3;
1273 0 : else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
1274 0 : col += 0;
1275 0 : else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
1276 0 : col += 1;
1277 : else
1278 0 : col += 4;
1279 : }
1280 : }
1281 :
1282 : // Animation goes downwards
1283 0 : row += m_anim_frame;
1284 :
1285 0 : float txs = m_tx_size.X;
1286 0 : float tys = m_tx_size.Y;
1287 0 : setBillboardTextureMatrix(m_spritenode,
1288 0 : txs, tys, col, row);
1289 : }
1290 : }
1291 :
1292 118 : void GenericCAO::updateTextures(const std::string &mod)
1293 : {
1294 118 : ITextureSource *tsrc = m_gamedef->tsrc();
1295 :
1296 118 : bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
1297 118 : bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
1298 118 : bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
1299 :
1300 118 : if(m_spritenode)
1301 : {
1302 0 : if(m_prop.visual == "sprite")
1303 : {
1304 0 : std::string texturestring = "unknown_node.png";
1305 0 : if(m_prop.textures.size() >= 1)
1306 0 : texturestring = m_prop.textures[0];
1307 0 : texturestring += mod;
1308 0 : m_spritenode->setMaterialTexture(0,
1309 0 : tsrc->getTextureForMesh(texturestring));
1310 :
1311 : // This allows setting per-material colors. However, until a real lighting
1312 : // system is added, the code below will have no effect. Once MineTest
1313 : // has directional lighting, it should work automatically.
1314 0 : if(m_prop.colors.size() >= 1)
1315 : {
1316 0 : m_spritenode->getMaterial(0).AmbientColor = m_prop.colors[0];
1317 0 : m_spritenode->getMaterial(0).DiffuseColor = m_prop.colors[0];
1318 0 : m_spritenode->getMaterial(0).SpecularColor = m_prop.colors[0];
1319 : }
1320 :
1321 0 : m_spritenode->getMaterial(0).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
1322 0 : m_spritenode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
1323 0 : m_spritenode->getMaterial(0).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
1324 : }
1325 : }
1326 118 : if(m_animated_meshnode)
1327 : {
1328 26 : if(m_prop.visual == "mesh")
1329 : {
1330 130 : for (u32 i = 0; i < m_prop.textures.size() &&
1331 52 : i < m_animated_meshnode->getMaterialCount(); ++i)
1332 : {
1333 104 : std::string texturestring = m_prop.textures[i];
1334 52 : if(texturestring == "")
1335 0 : continue; // Empty texture string means don't modify that material
1336 52 : texturestring += mod;
1337 52 : video::ITexture* texture = tsrc->getTextureForMesh(texturestring);
1338 52 : if(!texture)
1339 : {
1340 0 : errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
1341 0 : continue;
1342 : }
1343 :
1344 : // Set material flags and texture
1345 52 : video::SMaterial& material = m_animated_meshnode->getMaterial(i);
1346 52 : material.TextureLayer[0].Texture = texture;
1347 52 : material.setFlag(video::EMF_LIGHTING, false);
1348 52 : material.setFlag(video::EMF_BILINEAR_FILTER, false);
1349 :
1350 104 : m_animated_meshnode->getMaterial(i)
1351 156 : .setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
1352 104 : m_animated_meshnode->getMaterial(i)
1353 156 : .setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
1354 104 : m_animated_meshnode->getMaterial(i)
1355 156 : .setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
1356 : }
1357 111 : for (u32 i = 0; i < m_prop.colors.size() &&
1358 52 : i < m_animated_meshnode->getMaterialCount(); ++i)
1359 : {
1360 : // This allows setting per-material colors. However, until a real lighting
1361 : // system is added, the code below will have no effect. Once MineTest
1362 : // has directional lighting, it should work automatically.
1363 33 : m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1364 33 : m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1365 33 : m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
1366 : }
1367 : }
1368 : }
1369 118 : if(m_meshnode)
1370 : {
1371 67 : if(m_prop.visual == "cube")
1372 : {
1373 0 : for (u32 i = 0; i < 6; ++i)
1374 : {
1375 0 : std::string texturestring = "unknown_node.png";
1376 0 : if(m_prop.textures.size() > i)
1377 0 : texturestring = m_prop.textures[i];
1378 0 : texturestring += mod;
1379 :
1380 :
1381 : // Set material flags and texture
1382 0 : video::SMaterial& material = m_meshnode->getMaterial(i);
1383 0 : material.setFlag(video::EMF_LIGHTING, false);
1384 0 : material.setFlag(video::EMF_BILINEAR_FILTER, false);
1385 0 : material.setTexture(0,
1386 0 : tsrc->getTextureForMesh(texturestring));
1387 0 : material.getTextureMatrix(0).makeIdentity();
1388 :
1389 : // This allows setting per-material colors. However, until a real lighting
1390 : // system is added, the code below will have no effect. Once MineTest
1391 : // has directional lighting, it should work automatically.
1392 0 : if(m_prop.colors.size() > i)
1393 : {
1394 0 : m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
1395 0 : m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
1396 0 : m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
1397 : }
1398 :
1399 0 : m_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
1400 0 : m_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
1401 0 : m_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
1402 : }
1403 : }
1404 67 : else if(m_prop.visual == "upright_sprite")
1405 : {
1406 67 : scene::IMesh *mesh = m_meshnode->getMesh();
1407 : {
1408 134 : std::string tname = "unknown_object.png";
1409 67 : if(m_prop.textures.size() >= 1)
1410 67 : tname = m_prop.textures[0];
1411 67 : tname += mod;
1412 67 : scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
1413 67 : buf->getMaterial().setTexture(0,
1414 134 : tsrc->getTextureForMesh(tname));
1415 :
1416 : // This allows setting per-material colors. However, until a real lighting
1417 : // system is added, the code below will have no effect. Once MineTest
1418 : // has directional lighting, it should work automatically.
1419 67 : if(m_prop.colors.size() >= 1)
1420 : {
1421 67 : buf->getMaterial().AmbientColor = m_prop.colors[0];
1422 67 : buf->getMaterial().DiffuseColor = m_prop.colors[0];
1423 67 : buf->getMaterial().SpecularColor = m_prop.colors[0];
1424 : }
1425 :
1426 67 : buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
1427 67 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
1428 67 : buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
1429 : }
1430 : {
1431 134 : std::string tname = "unknown_object.png";
1432 67 : if(m_prop.textures.size() >= 2)
1433 0 : tname = m_prop.textures[1];
1434 67 : else if(m_prop.textures.size() >= 1)
1435 67 : tname = m_prop.textures[0];
1436 67 : tname += mod;
1437 67 : scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
1438 67 : buf->getMaterial().setTexture(0,
1439 134 : tsrc->getTextureForMesh(tname));
1440 :
1441 : // This allows setting per-material colors. However, until a real lighting
1442 : // system is added, the code below will have no effect. Once MineTest
1443 : // has directional lighting, it should work automatically.
1444 67 : if(m_prop.colors.size() >= 2)
1445 : {
1446 67 : buf->getMaterial().AmbientColor = m_prop.colors[1];
1447 67 : buf->getMaterial().DiffuseColor = m_prop.colors[1];
1448 67 : buf->getMaterial().SpecularColor = m_prop.colors[1];
1449 : }
1450 0 : else if(m_prop.colors.size() >= 1)
1451 : {
1452 0 : buf->getMaterial().AmbientColor = m_prop.colors[0];
1453 0 : buf->getMaterial().DiffuseColor = m_prop.colors[0];
1454 0 : buf->getMaterial().SpecularColor = m_prop.colors[0];
1455 : }
1456 :
1457 67 : buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
1458 67 : buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
1459 67 : buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
1460 : }
1461 : }
1462 : }
1463 118 : }
1464 :
1465 304 : void GenericCAO::updateAnimation()
1466 : {
1467 304 : if(m_animated_meshnode == NULL)
1468 162 : return;
1469 :
1470 211 : if (m_animated_meshnode->getStartFrame() != m_animation_range.X ||
1471 69 : m_animated_meshnode->getEndFrame() != m_animation_range.Y)
1472 98 : m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y);
1473 142 : if (m_animated_meshnode->getAnimationSpeed() != m_animation_speed)
1474 134 : m_animated_meshnode->setAnimationSpeed(m_animation_speed);
1475 142 : m_animated_meshnode->setTransitionTime(m_animation_blend);
1476 : // Requires Irrlicht 1.8 or greater
1477 : #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR > 1
1478 142 : if (m_animated_meshnode->getLoopMode() != m_animation_loop)
1479 0 : m_animated_meshnode->setLoopMode(m_animation_loop);
1480 : #endif
1481 : }
1482 :
1483 118 : void GenericCAO::updateBonePosition()
1484 : {
1485 118 : if(m_bone_position.empty() || m_animated_meshnode == NULL)
1486 118 : return;
1487 :
1488 0 : m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
1489 0 : for(std::map<std::string,
1490 0 : core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin();
1491 0 : ii != m_bone_position.end(); ++ii)
1492 : {
1493 0 : std::string bone_name = (*ii).first;
1494 0 : v3f bone_pos = (*ii).second.X;
1495 0 : v3f bone_rot = (*ii).second.Y;
1496 0 : irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
1497 0 : if(bone)
1498 : {
1499 0 : bone->setPosition(bone_pos);
1500 0 : bone->setRotation(bone_rot);
1501 : }
1502 : }
1503 : }
1504 :
1505 197 : void GenericCAO::updateAttachments()
1506 : {
1507 :
1508 197 : if (getParent() == NULL) { // Detach or don't attach
1509 197 : scene::ISceneNode *node = getSceneNode();
1510 197 : if (node) {
1511 153 : v3f old_position = node->getAbsolutePosition();
1512 153 : v3f old_rotation = node->getRotation();
1513 153 : node->setParent(m_smgr->getRootSceneNode());
1514 153 : node->setPosition(old_position);
1515 153 : node->setRotation(old_rotation);
1516 153 : node->updateAbsolutePosition();
1517 : }
1518 197 : if (m_is_local_player) {
1519 6 : LocalPlayer *player = m_env->getLocalPlayer();
1520 6 : player->isAttached = false;
1521 : }
1522 : }
1523 : else // Attach
1524 : {
1525 0 : scene::ISceneNode *my_node = getSceneNode();
1526 :
1527 0 : scene::ISceneNode *parent_node = getParent()->getSceneNode();
1528 : scene::IAnimatedMeshSceneNode *parent_animated_mesh_node =
1529 0 : getParent()->getAnimatedMeshSceneNode();
1530 0 : if (parent_animated_mesh_node && m_attachment_bone != "") {
1531 0 : parent_node = parent_animated_mesh_node->getJointNode(m_attachment_bone.c_str());
1532 : }
1533 :
1534 0 : if (my_node && parent_node) {
1535 0 : my_node->setParent(parent_node);
1536 0 : my_node->setPosition(m_attachment_position);
1537 0 : my_node->setRotation(m_attachment_rotation);
1538 0 : my_node->updateAbsolutePosition();
1539 : }
1540 0 : if (m_is_local_player) {
1541 0 : LocalPlayer *player = m_env->getLocalPlayer();
1542 0 : player->isAttached = true;
1543 : }
1544 : }
1545 197 : }
1546 :
1547 1640 : void GenericCAO::processMessage(const std::string &data)
1548 : {
1549 : //infostream<<"GenericCAO: Got message"<<std::endl;
1550 3280 : std::istringstream is(data, std::ios::binary);
1551 : // command
1552 1640 : u8 cmd = readU8(is);
1553 1640 : if(cmd == GENERIC_CMD_SET_PROPERTIES)
1554 : {
1555 118 : m_prop = gob_read_set_properties(is);
1556 :
1557 118 : m_selection_box = m_prop.collisionbox;
1558 118 : m_selection_box.MinEdge *= BS;
1559 118 : m_selection_box.MaxEdge *= BS;
1560 :
1561 118 : m_tx_size.X = 1.0 / m_prop.spritediv.X;
1562 118 : m_tx_size.Y = 1.0 / m_prop.spritediv.Y;
1563 :
1564 118 : if(!m_initial_tx_basepos_set){
1565 44 : m_initial_tx_basepos_set = true;
1566 44 : m_tx_basepos = m_prop.initial_sprite_basepos;
1567 : }
1568 :
1569 118 : expireVisuals();
1570 : }
1571 1522 : else if(cmd == GENERIC_CMD_UPDATE_POSITION)
1572 : {
1573 : // Not sent by the server if this object is an attachment.
1574 : // We might however get here if the server notices the object being detached before the client.
1575 1163 : m_position = readV3F1000(is);
1576 1163 : m_velocity = readV3F1000(is);
1577 1163 : m_acceleration = readV3F1000(is);
1578 1163 : if(fabs(m_prop.automatic_rotate) < 0.001)
1579 1117 : m_yaw = readF1000(is);
1580 : else
1581 46 : readF1000(is);
1582 1163 : bool do_interpolate = readU8(is);
1583 1163 : bool is_end_position = readU8(is);
1584 1163 : float update_interval = readF1000(is);
1585 :
1586 : // Place us a bit higher if we're physical, to not sink into
1587 : // the ground due to sucky collision detection...
1588 1163 : if(m_prop.physical)
1589 923 : m_position += v3f(0,0.002,0);
1590 :
1591 1163 : if(getParent() != NULL) // Just in case
1592 0 : return;
1593 :
1594 1163 : if(do_interpolate)
1595 : {
1596 1158 : if(!m_prop.physical)
1597 235 : pos_translator.update(m_position, is_end_position, update_interval);
1598 : } else {
1599 5 : pos_translator.init(m_position);
1600 : }
1601 1163 : updateNodePos();
1602 : }
1603 359 : else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD) {
1604 0 : std::string mod = deSerializeString(is);
1605 0 : updateTextures(mod);
1606 : }
1607 359 : else if(cmd == GENERIC_CMD_SET_SPRITE) {
1608 0 : v2s16 p = readV2S16(is);
1609 0 : int num_frames = readU16(is);
1610 0 : float framelength = readF1000(is);
1611 0 : bool select_horiz_by_yawpitch = readU8(is);
1612 :
1613 0 : m_tx_basepos = p;
1614 0 : m_anim_num_frames = num_frames;
1615 0 : m_anim_framelength = framelength;
1616 0 : m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
1617 :
1618 0 : updateTexturePos();
1619 : }
1620 359 : else if(cmd == GENERIC_CMD_SET_PHYSICS_OVERRIDE) {
1621 3 : float override_speed = readF1000(is);
1622 3 : float override_jump = readF1000(is);
1623 3 : float override_gravity = readF1000(is);
1624 : // these are sent inverted so we get true when the server sends nothing
1625 3 : bool sneak = !readU8(is);
1626 3 : bool sneak_glitch = !readU8(is);
1627 :
1628 :
1629 3 : if(m_is_local_player)
1630 : {
1631 2 : LocalPlayer *player = m_env->getLocalPlayer();
1632 2 : player->physics_override_speed = override_speed;
1633 2 : player->physics_override_jump = override_jump;
1634 2 : player->physics_override_gravity = override_gravity;
1635 2 : player->physics_override_sneak = sneak;
1636 2 : player->physics_override_sneak_glitch = sneak_glitch;
1637 : }
1638 : }
1639 356 : else if(cmd == GENERIC_CMD_SET_ANIMATION) {
1640 : // TODO: change frames send as v2s32 value
1641 196 : v2f range = readV2F1000(is);
1642 196 : if (!m_is_local_player) {
1643 175 : m_animation_range = v2s32((s32)range.X, (s32)range.Y);
1644 175 : m_animation_speed = readF1000(is);
1645 175 : m_animation_blend = readF1000(is);
1646 : // these are sent inverted so we get true when the server sends nothing
1647 175 : m_animation_loop = !readU8(is);
1648 175 : updateAnimation();
1649 : } else {
1650 21 : LocalPlayer *player = m_env->getLocalPlayer();
1651 21 : if(player->last_animation == NO_ANIM)
1652 : {
1653 21 : m_animation_range = v2s32((s32)range.X, (s32)range.Y);
1654 21 : m_animation_speed = readF1000(is);
1655 21 : m_animation_blend = readF1000(is);
1656 : // these are sent inverted so we get true when the server sends nothing
1657 21 : m_animation_loop = !readU8(is);
1658 : }
1659 : // update animation only if local animations present
1660 : // and received animation is unknown (except idle animation)
1661 21 : bool is_known = false;
1662 84 : for (int i = 1;i<4;i++)
1663 : {
1664 63 : if(m_animation_range.Y == player->local_animations[i].Y)
1665 10 : is_known = true;
1666 : }
1667 31 : if(!is_known ||
1668 10 : (player->local_animations[1].Y + player->local_animations[2].Y < 1))
1669 : {
1670 11 : updateAnimation();
1671 : }
1672 : }
1673 : }
1674 160 : else if(cmd == GENERIC_CMD_SET_BONE_POSITION) {
1675 0 : std::string bone = deSerializeString(is);
1676 0 : v3f position = readV3F1000(is);
1677 0 : v3f rotation = readV3F1000(is);
1678 0 : m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1679 :
1680 0 : updateBonePosition();
1681 160 : } else if (cmd == GENERIC_CMD_ATTACH_TO) {
1682 79 : u16 parentID = readS16(is);
1683 79 : u16 oldparent = m_env->attachement_parent_ids[getId()];
1684 79 : if (oldparent) {
1685 : m_children.erase(std::remove(m_children.begin(), m_children.end(),
1686 0 : getId()), m_children.end());
1687 : }
1688 79 : m_env->attachement_parent_ids[getId()] = parentID;
1689 79 : GenericCAO *parentobj = m_env->getGenericCAO(parentID);
1690 :
1691 79 : if (parentobj) {
1692 0 : parentobj->m_children.push_back(getId());
1693 : }
1694 :
1695 79 : m_attachment_bone = deSerializeString(is);
1696 79 : m_attachment_position = readV3F1000(is);
1697 79 : m_attachment_rotation = readV3F1000(is);
1698 :
1699 : // localplayer itself can't be attached to localplayer
1700 79 : if (!m_is_local_player) {
1701 78 : m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
1702 : // Objects attached to the local player should be hidden by default
1703 78 : m_is_visible = !m_attached_to_local;
1704 : }
1705 :
1706 79 : updateAttachments();
1707 : }
1708 81 : else if(cmd == GENERIC_CMD_PUNCHED) {
1709 1 : /*s16 damage =*/ readS16(is);
1710 1 : s16 result_hp = readS16(is);
1711 :
1712 : // Use this instead of the send damage to not interfere with prediction
1713 1 : s16 damage = m_hp - result_hp;
1714 :
1715 1 : m_hp = result_hp;
1716 :
1717 1 : if (damage > 0)
1718 : {
1719 0 : if (m_hp <= 0)
1720 : {
1721 : // TODO: Execute defined fast response
1722 : // As there is no definition, make a smoke puff
1723 0 : ClientSimpleObject *simple = createSmokePuff(
1724 : m_smgr, m_env, m_position,
1725 0 : m_prop.visual_size * BS);
1726 0 : m_env->addSimpleObject(simple);
1727 : } else {
1728 : // TODO: Execute defined fast response
1729 : // Flashing shall suffice as there is no definition
1730 0 : m_reset_textures_timer = 0.05;
1731 0 : if(damage >= 2)
1732 0 : m_reset_textures_timer += 0.05 * damage;
1733 0 : updateTextures("^[brighten");
1734 : }
1735 : }
1736 : }
1737 80 : else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) {
1738 80 : m_armor_groups.clear();
1739 80 : int armor_groups_size = readU16(is);
1740 161 : for(int i=0; i<armor_groups_size; i++)
1741 : {
1742 162 : std::string name = deSerializeString(is);
1743 81 : int rating = readS16(is);
1744 81 : m_armor_groups[name] = rating;
1745 : }
1746 0 : } else if (cmd == GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) {
1747 0 : readU8(is); // version
1748 0 : m_nametag_color = readARGB8(is);
1749 0 : if (m_textnode != NULL) {
1750 0 : m_textnode->setTextColor(m_nametag_color);
1751 :
1752 : // Enforce hiding nametag,
1753 : // because if freetype is enabled, a grey
1754 : // shadow can remain.
1755 0 : m_textnode->setVisible(m_nametag_color.getAlpha() > 0);
1756 : }
1757 : }
1758 : }
1759 :
1760 : /* \pre punchitem != NULL
1761 : */
1762 0 : bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
1763 : float time_from_last_punch)
1764 : {
1765 : assert(punchitem); // pre-condition
1766 : const ToolCapabilities *toolcap =
1767 0 : &punchitem->getToolCapabilities(m_gamedef->idef());
1768 : PunchDamageResult result = getPunchDamage(
1769 : m_armor_groups,
1770 : toolcap,
1771 : punchitem,
1772 0 : time_from_last_punch);
1773 :
1774 0 : if(result.did_punch && result.damage != 0)
1775 : {
1776 0 : if(result.damage < m_hp)
1777 : {
1778 0 : m_hp -= result.damage;
1779 : } else {
1780 0 : m_hp = 0;
1781 : // TODO: Execute defined fast response
1782 : // As there is no definition, make a smoke puff
1783 0 : ClientSimpleObject *simple = createSmokePuff(
1784 : m_smgr, m_env, m_position,
1785 0 : m_prop.visual_size * BS);
1786 0 : m_env->addSimpleObject(simple);
1787 : }
1788 : // TODO: Execute defined fast response
1789 : // Flashing shall suffice as there is no definition
1790 0 : m_reset_textures_timer = 0.05;
1791 0 : if(result.damage >= 2)
1792 0 : m_reset_textures_timer += 0.05 * result.damage;
1793 0 : updateTextures("^[brighten");
1794 : }
1795 :
1796 0 : return false;
1797 : }
1798 :
1799 0 : std::string GenericCAO::debugInfoText()
1800 : {
1801 0 : std::ostringstream os(std::ios::binary);
1802 0 : os<<"GenericCAO hp="<<m_hp<<"\n";
1803 0 : os<<"armor={";
1804 0 : for(ItemGroupList::const_iterator i = m_armor_groups.begin();
1805 0 : i != m_armor_groups.end(); i++)
1806 : {
1807 0 : os<<i->first<<"="<<i->second<<", ";
1808 : }
1809 0 : os<<"}";
1810 0 : return os.str();
1811 : }
1812 :
1813 : // Prototype
1814 3 : GenericCAO proto_GenericCAO(NULL, NULL);
|