3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU 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.
20 #include "content_cao.h"
22 #include "environment.h"
23 #include "collision.h"
25 #include <ICameraSceneNode.h>
26 #include <ITextSceneNode.h>
27 #include <IBillboardSceneNode.h>
28 #include "serialization.h" // For decompressZlib
30 #include "clientobject.h"
31 #include "content_object.h"
33 #include "utility.h" // For IntervalLimiter
36 core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
42 struct SmoothTranslator
49 f32 anim_time_counter;
69 anim_time_counter = 0;
78 void update(v3f vect_new, bool is_end_position=false, float update_interval=-1)
80 aim_is_end = is_end_position;
83 if(update_interval > 0){
84 anim_time = update_interval;
86 if(anim_time < 0.001 || anim_time > 1.0)
87 anim_time = anim_time_counter;
89 anim_time = anim_time * 0.9 + anim_time_counter * 0.1;
91 anim_time_counter = 0;
95 void translate(f32 dtime)
97 anim_time_counter = anim_time_counter + dtime;
98 anim_counter = anim_counter + dtime;
99 v3f vect_move = vect_aim - vect_old;
101 if(anim_time > 0.001)
102 moveratio = anim_time_counter / anim_time;
103 // Move a bit less than should, to avoid oscillation
104 moveratio = moveratio * 0.8;
105 float move_end = 1.5;
108 if(moveratio > move_end)
109 moveratio = move_end;
110 vect_show = vect_old + vect_move * moveratio;
115 return ((anim_time_counter / anim_time) < 1.4);
123 static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
124 float txs, float tys, int col, int row)
126 video::SMaterial& material = bill->getMaterial(0);
127 core::matrix4& matrix = material.getTextureMatrix(0);
128 matrix.setTextureTranslate(txs*col, tys*row);
129 matrix.setTextureScale(txs, tys);
136 class TestCAO : public ClientActiveObject
139 TestCAO(IGameDef *gamedef, ClientEnvironment *env);
144 return ACTIVEOBJECT_TYPE_TEST;
147 static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
149 void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
150 IrrlichtDevice *irr);
151 void removeFromScene();
152 void updateLight(u8 light_at_pos);
153 v3s16 getLightPosition();
154 void updateNodePos();
156 void step(float dtime, ClientEnvironment *env);
158 void processMessage(const std::string &data);
161 scene::IMeshSceneNode *m_node;
169 class ItemCAO : public ClientActiveObject
172 ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
177 return ACTIVEOBJECT_TYPE_ITEM;
180 static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
182 void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
183 IrrlichtDevice *irr);
184 void removeFromScene();
185 void updateLight(u8 light_at_pos);
186 v3s16 getLightPosition();
187 void updateNodePos();
188 void updateInfoText();
189 void updateTexture();
191 void step(float dtime, ClientEnvironment *env);
193 void processMessage(const std::string &data);
195 void initialize(const std::string &data);
197 core::aabbox3d<f32>* getSelectionBox()
198 {return &m_selection_box;}
202 std::string infoText()
206 core::aabbox3d<f32> m_selection_box;
207 scene::IMeshSceneNode *m_node;
209 std::string m_itemstring;
210 std::string m_infotext;
217 #include "luaentity_common.h"
219 class LuaEntityCAO : public ClientActiveObject
222 core::aabbox3d<f32> m_selection_box;
223 scene::IMeshSceneNode *m_meshnode;
224 scene::IBillboardSceneNode *m_spritenode;
230 struct LuaEntityProperties *m_prop;
231 SmoothTranslator pos_translator;
232 // Spritesheet/animation stuff
235 bool m_tx_select_horiz_by_yawpitch;
237 int m_anim_num_frames;
238 float m_anim_framelength;
242 LuaEntityCAO(IGameDef *gamedef, ClientEnvironment *env):
243 ClientActiveObject(0, gamedef, env),
244 m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
247 m_position(v3f(0,10*BS,0)),
248 m_velocity(v3f(0,0,0)),
249 m_acceleration(v3f(0,0,0)),
252 m_prop(new LuaEntityProperties),
255 m_tx_select_horiz_by_yawpitch(false),
257 m_anim_num_frames(1),
258 m_anim_framelength(0.2),
262 ClientActiveObject::registerType(getType(), create);
265 void initialize(const std::string &data)
267 infostream<<"LuaEntityCAO: Got init data"<<std::endl;
269 std::istringstream is(data, std::ios::binary);
271 u8 version = readU8(is);
276 m_position = readV3F1000(is);
278 m_yaw = readF1000(is);
282 std::istringstream prop_is(deSerializeLongString(is), std::ios::binary);
283 m_prop->deSerialize(prop_is);
285 infostream<<"m_prop: "<<m_prop->dump()<<std::endl;
287 m_selection_box = m_prop->collisionbox;
288 m_selection_box.MinEdge *= BS;
289 m_selection_box.MaxEdge *= BS;
291 pos_translator.init(m_position);
293 m_tx_size.X = 1.0 / m_prop->spritediv.X;
294 m_tx_size.Y = 1.0 / m_prop->spritediv.Y;
295 m_tx_basepos.X = m_tx_size.X * m_prop->initial_sprite_basepos.X;
296 m_tx_basepos.Y = m_tx_size.Y * m_prop->initial_sprite_basepos.Y;
306 static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
308 return new LuaEntityCAO(gamedef, env);
313 return ACTIVEOBJECT_TYPE_LUAENTITY;
315 core::aabbox3d<f32>* getSelectionBox()
317 return &m_selection_box;
321 return pos_translator.vect_show;
324 void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
327 if(m_meshnode != NULL || m_spritenode != NULL)
330 //video::IVideoDriver* driver = smgr->getVideoDriver();
332 if(m_prop->visual == "sprite"){
333 infostream<<"LuaEntityCAO::addToScene(): single_sprite"<<std::endl;
334 m_spritenode = smgr->addBillboardSceneNode(
335 NULL, v2f(1, 1), v3f(0,0,0), -1);
336 m_spritenode->setMaterialTexture(0,
337 tsrc->getTextureRaw("unknown_block.png"));
338 m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
339 m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
340 m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
341 m_spritenode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
342 m_spritenode->setColor(video::SColor(255,0,0,0));
343 m_spritenode->setVisible(false); /* Set visible when brightness is known */
344 m_spritenode->setSize(m_prop->visual_size*BS);
346 const float txs = 1.0 / 1;
347 const float tys = 1.0 / 1;
348 setBillboardTextureMatrix(m_spritenode,
351 } else if(m_prop->visual == "cube"){
352 infostream<<"LuaEntityCAO::addToScene(): cube"<<std::endl;
353 scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
354 m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
357 m_meshnode->setScale(v3f(1));
358 // Will be shown when we know the brightness
359 m_meshnode->setVisible(false);
361 infostream<<"LuaEntityCAO::addToScene(): \""<<m_prop->visual
362 <<"\" not supported"<<std::endl;
368 void removeFromScene()
371 m_meshnode->remove();
375 m_spritenode->remove();
380 void updateLight(u8 light_at_pos)
382 u8 li = decode_light(light_at_pos);
383 video::SColor color(255,li,li,li);
385 setMeshColor(m_meshnode->getMesh(), color);
386 m_meshnode->setVisible(true);
389 m_spritenode->setColor(color);
390 m_spritenode->setVisible(true);
394 v3s16 getLightPosition()
396 return floatToInt(m_position, BS);
402 m_meshnode->setPosition(pos_translator.vect_show);
405 m_spritenode->setPosition(pos_translator.vect_show);
409 void step(float dtime, ClientEnvironment *env)
411 if(m_prop->physical){
412 core::aabbox3d<f32> box = m_prop->collisionbox;
415 collisionMoveResult moveresult;
416 f32 pos_max_d = BS*0.25; // Distance per iteration
417 v3f p_pos = m_position;
418 v3f p_velocity = m_velocity;
419 IGameDef *gamedef = env->getGameDef();
420 moveresult = collisionMovePrecise(&env->getMap(), gamedef,
421 pos_max_d, box, dtime, p_pos, p_velocity);
424 m_velocity = p_velocity;
426 bool is_end_position = moveresult.collides;
427 pos_translator.update(m_position, is_end_position, dtime);
428 pos_translator.translate(dtime);
431 m_velocity += dtime * m_acceleration;
433 m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
434 m_velocity += dtime * m_acceleration;
435 pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
436 pos_translator.translate(dtime);
440 m_anim_timer += dtime;
441 if(m_anim_timer >= m_anim_framelength){
442 m_anim_timer -= m_anim_framelength;
444 if(m_anim_frame >= m_anim_num_frames)
451 void updateTexturePos()
454 scene::ICameraSceneNode* camera =
455 m_spritenode->getSceneManager()->getActiveCamera();
458 v3f cam_to_entity = m_spritenode->getAbsolutePosition()
459 - camera->getAbsolutePosition();
460 cam_to_entity.normalize();
462 int row = m_tx_basepos.Y;
463 int col = m_tx_basepos.X;
465 if(m_tx_select_horiz_by_yawpitch)
467 if(cam_to_entity.Y > 0.75)
469 else if(cam_to_entity.Y < -0.75)
472 float mob_dir = atan2(cam_to_entity.Z, cam_to_entity.X) / PI * 180.;
473 float dir = mob_dir - m_yaw;
474 dir = wrapDegrees_180(dir);
475 //infostream<<"id="<<m_id<<" dir="<<dir<<std::endl;
476 if(fabs(wrapDegrees_180(dir - 0)) <= 45.1)
478 else if(fabs(wrapDegrees_180(dir - 90)) <= 45.1)
480 else if(fabs(wrapDegrees_180(dir - 180)) <= 45.1)
482 else if(fabs(wrapDegrees_180(dir + 90)) <= 45.1)
489 // Animation goes downwards
492 float txs = m_tx_size.X;
493 float tys = m_tx_size.Y;
494 setBillboardTextureMatrix(m_spritenode,
499 void updateTextures(const std::string &mod)
501 ITextureSource *tsrc = m_gamedef->tsrc();
504 std::string texturestring = "unknown_block.png";
505 if(m_prop->textures.size() >= 1)
506 texturestring = m_prop->textures[0];
507 texturestring += mod;
508 m_spritenode->setMaterialTexture(0,
509 tsrc->getTextureRaw(texturestring));
512 for (u32 i = 0; i < 6; ++i)
514 std::string texturestring = "unknown_block.png";
515 if(m_prop->textures.size() > i)
516 texturestring = m_prop->textures[i];
517 texturestring += mod;
518 AtlasPointer ap = tsrc->getTexture(texturestring);
520 // Get the tile texture and atlas transformation
521 video::ITexture* atlas = ap.atlas;
525 // Set material flags and texture
526 video::SMaterial& material = m_meshnode->getMaterial(i);
527 material.setFlag(video::EMF_LIGHTING, false);
528 material.setFlag(video::EMF_BILINEAR_FILTER, false);
529 material.setTexture(0, atlas);
530 material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
531 material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
536 void processMessage(const std::string &data)
538 //infostream<<"LuaEntityCAO: Got message"<<std::endl;
539 std::istringstream is(data, std::ios::binary);
542 if(cmd == LUAENTITY_CMD_UPDATE_POSITION) // update position
545 bool do_interpolate = readU8(is);
547 m_position = readV3F1000(is);
549 m_velocity = readV3F1000(is);
551 m_acceleration = readV3F1000(is);
553 m_yaw = readF1000(is);
554 // is_end_position (for interpolation)
555 bool is_end_position = readU8(is);
557 float update_interval = readF1000(is);
560 if(!m_prop->physical)
561 pos_translator.update(m_position, is_end_position, update_interval);
563 pos_translator.init(m_position);
567 else if(cmd == LUAENTITY_CMD_SET_TEXTURE_MOD) // set texture modification
569 std::string mod = deSerializeString(is);
572 else if(cmd == LUAENTITY_CMD_SET_SPRITE) // set sprite
574 v2s16 p = readV2S16(is);
575 int num_frames = readU16(is);
576 float framelength = readF1000(is);
577 bool select_horiz_by_yawpitch = readU8(is);
580 m_anim_num_frames = num_frames;
581 m_anim_framelength = framelength;
582 m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
586 else if(cmd == LUAENTITY_CMD_PUNCHED)
588 s16 damage = readS16(is);
589 s16 result_hp = readS16(is);
592 // TODO: Execute defined fast response
598 LuaEntityCAO proto_LuaEntityCAO(NULL, NULL);
604 class PlayerCAO : public ClientActiveObject
607 core::aabbox3d<f32> m_selection_box;
608 scene::IMeshSceneNode *m_node;
609 scene::ITextSceneNode* m_text;
613 SmoothTranslator pos_translator;
614 bool m_is_local_player;
615 LocalPlayer *m_local_player;
616 float m_damage_visual_timer;
620 PlayerCAO(IGameDef *gamedef, ClientEnvironment *env):
621 ClientActiveObject(0, gamedef, env),
622 m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.0,BS/3.),
625 m_position(v3f(0,10*BS,0)),
627 m_is_local_player(false),
628 m_local_player(NULL),
629 m_damage_visual_timer(0),
633 ClientActiveObject::registerType(getType(), create);
636 void initialize(const std::string &data)
638 infostream<<"PlayerCAO: Got init data"<<std::endl;
640 std::istringstream is(data, std::ios::binary);
642 u8 version = readU8(is);
647 m_name = deSerializeString(is);
649 m_position = readV3F1000(is);
651 m_yaw = readF1000(is);
655 pos_translator.init(m_position);
657 Player *player = m_env->getPlayer(m_name.c_str());
658 if(player && player->isLocal()){
659 m_is_local_player = true;
660 m_local_player = (LocalPlayer*)player;
670 static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env)
672 return new PlayerCAO(gamedef, env);
677 return ACTIVEOBJECT_TYPE_PLAYER;
679 core::aabbox3d<f32>* getSelectionBox()
681 if(m_is_local_player)
685 return &m_selection_box;
689 return pos_translator.vect_show;
692 void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
697 if(m_is_local_player)
700 //video::IVideoDriver* driver = smgr->getVideoDriver();
701 gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
703 scene::SMesh *mesh = new scene::SMesh();
705 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
706 video::SColor c(255,255,255,255);
707 video::S3DVertex vertices[4] =
709 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
710 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
711 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
712 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
714 u16 indices[] = {0,1,2,2,3,0};
715 buf->append(vertices, 4, indices, 6);
717 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
718 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
719 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
720 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
722 mesh->addMeshBuffer(buf);
726 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
727 video::SColor c(255,255,255,255);
728 video::S3DVertex vertices[4] =
730 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
731 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
732 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
733 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
735 u16 indices[] = {0,1,2,2,3,0};
736 buf->append(vertices, 4, indices, 6);
738 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
739 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
740 buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
741 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
743 mesh->addMeshBuffer(buf);
746 m_node = smgr->addMeshSceneNode(mesh, NULL);
748 // Set it to use the materials of the meshbuffers directly.
749 // This is needed for changing the texture in the future
750 m_node->setReadOnlyMaterials(true);
753 // Add a text node for showing the name
754 std::wstring wname = narrow_to_wide(m_name);
755 m_text = smgr->addTextSceneNode(gui->getBuiltInFont(),
756 wname.c_str(), video::SColor(255,255,255,255), m_node);
757 m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
764 void removeFromScene()
773 void updateLight(u8 light_at_pos)
778 u8 li = decode_light(light_at_pos);
779 video::SColor color(255,li,li,li);
780 setMeshColor(m_node->getMesh(), color);
785 v3s16 getLightPosition()
787 return floatToInt(m_position+v3f(0,BS*1.5,0), BS);
790 void updateVisibility()
795 m_node->setVisible(!m_dead);
803 m_node->setPosition(pos_translator.vect_show);
805 v3f rot = m_node->getRotation();
807 m_node->setRotation(rot);
810 void step(float dtime, ClientEnvironment *env)
812 pos_translator.translate(dtime);
816 if(m_damage_visual_timer > 0){
817 m_damage_visual_timer -= dtime;
818 if(m_damage_visual_timer <= 0){
824 void processMessage(const std::string &data)
826 //infostream<<"PlayerCAO: Got message"<<std::endl;
827 std::istringstream is(data, std::ios::binary);
830 if(cmd == 0) // update position
833 m_position = readV3F1000(is);
835 m_yaw = readF1000(is);
837 pos_translator.update(m_position, false);
841 else if(cmd == 1) // punched
844 s16 damage = readS16(is);
845 m_damage_visual_timer = 0.05;
847 m_damage_visual_timer += 0.05 * damage;
848 updateTextures("^[brighten");
850 else if(cmd == 2) // died or respawned
857 void updateTextures(const std::string &mod)
861 ITextureSource *tsrc = m_gamedef->tsrc();
862 scene::IMesh *mesh = m_node->getMesh();
865 std::string tname = "player.png";
867 scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
868 buf->getMaterial().setTexture(0,
869 tsrc->getTextureRaw(tname));
872 std::string tname = "player_back.png";
874 scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
875 buf->getMaterial().setTexture(0,
876 tsrc->getTextureRaw(tname));
883 PlayerCAO proto_PlayerCAO(NULL, NULL);