mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-11-04 09:15:29 +01:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		@@ -215,8 +215,8 @@ minetest.register_chatcommand("clearpassword", {
 | 
			
		||||
	privs = {password=true},
 | 
			
		||||
	func = function(name, param)
 | 
			
		||||
		toname = param
 | 
			
		||||
		if not toname then
 | 
			
		||||
			minetest.chat_send_player(toname, "Name field required")
 | 
			
		||||
		if toname == "" then
 | 
			
		||||
			minetest.chat_send_player(name, "Name field required")
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		minetest.set_player_password(toname, '')
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
Minetest Lua Modding API Reference 0.4.0
 | 
			
		||||
Minetest Lua Modding API Reference 0.4.3
 | 
			
		||||
==========================================
 | 
			
		||||
More information at http://c55.me/minetest/
 | 
			
		||||
 | 
			
		||||
@@ -694,6 +694,11 @@ image[<X>,<Y>;<W>,<H>;<texture name>]
 | 
			
		||||
^ Show an image
 | 
			
		||||
^ Position and size units are inventory slots
 | 
			
		||||
 | 
			
		||||
background[<X>,<Y>;<W>,<H>;<texture name>]
 | 
			
		||||
^ Use a background. Inventory rectangles are not drawn then.
 | 
			
		||||
^ Position and size units are inventory slots
 | 
			
		||||
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
 | 
			
		||||
 | 
			
		||||
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
 | 
			
		||||
^ Textual field; will be sent to server when a button is clicked
 | 
			
		||||
^ x and y position the field relative to the top left of the menu
 | 
			
		||||
@@ -1098,6 +1103,10 @@ methods:
 | 
			
		||||
- get_wielded_item() -> ItemStack
 | 
			
		||||
- set_wielded_item(item): replaces the wielded item, returns true if successful
 | 
			
		||||
- set_armor_groups({group1=rating, group2=rating, ...})
 | 
			
		||||
- set_animation({x=1,y=1}, frame_speed=15, frame_blend=0)
 | 
			
		||||
- set_attach(parent, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
 | 
			
		||||
- set_detach()
 | 
			
		||||
- set_bone_position("", {x=0,y=0,z=0}, {x=0,y=0,z=0})
 | 
			
		||||
- set_properties(object property table)
 | 
			
		||||
LuaEntitySAO-only: (no-op for other objects)
 | 
			
		||||
- setvelocity({x=num, y=num, z=num})
 | 
			
		||||
@@ -1123,6 +1132,10 @@ Player-only: (no-op for other objects)
 | 
			
		||||
  ^ Redefine player's inventory form
 | 
			
		||||
  ^ Should usually be called in on_joinplayer
 | 
			
		||||
- get_inventory_formspec() -> formspec string
 | 
			
		||||
- get_player_control(): returns table with player pressed keys
 | 
			
		||||
	{jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool}
 | 
			
		||||
- get_player_control_bits(): returns integer with bit packed player pressed keys
 | 
			
		||||
	bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB
 | 
			
		||||
	
 | 
			
		||||
InvRef: Reference to an inventory
 | 
			
		||||
methods:
 | 
			
		||||
@@ -1222,9 +1235,11 @@ Object Properties
 | 
			
		||||
    physical = true,
 | 
			
		||||
    weight = 5,
 | 
			
		||||
    collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
 | 
			
		||||
    visual = "cube"/"sprite"/"upright_sprite",
 | 
			
		||||
    visual = "cube"/"sprite"/"upright_sprite"/"mesh",
 | 
			
		||||
    visual_size = {x=1, y=1},
 | 
			
		||||
    mesh = "model",
 | 
			
		||||
    textures = {}, -- number of required textures depends on visual
 | 
			
		||||
    colors = {}, -- number of required colors depends on visual
 | 
			
		||||
    spritediv = {x=1, y=1},
 | 
			
		||||
    initial_sprite_basepos = {x=0, y=0},
 | 
			
		||||
    is_visible = true,
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
#include "sound.h"
 | 
			
		||||
#include "util/string.h"
 | 
			
		||||
#include "hex.h"
 | 
			
		||||
#include "IMeshCache.h"
 | 
			
		||||
 | 
			
		||||
static std::string getMediaCacheDir()
 | 
			
		||||
{
 | 
			
		||||
@@ -821,7 +822,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
 | 
			
		||||
	if(name != "")
 | 
			
		||||
	{
 | 
			
		||||
		verbosestream<<"Client: Attempting to load image "
 | 
			
		||||
				<<"file \""<<filename<<"\""<<std::endl;
 | 
			
		||||
		<<"file \""<<filename<<"\""<<std::endl;
 | 
			
		||||
 | 
			
		||||
		io::IFileSystem *irrfs = m_device->getFileSystem();
 | 
			
		||||
		video::IVideoDriver *vdrv = m_device->getVideoDriver();
 | 
			
		||||
@@ -855,11 +856,33 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
 | 
			
		||||
	if(name != "")
 | 
			
		||||
	{
 | 
			
		||||
		verbosestream<<"Client: Attempting to load sound "
 | 
			
		||||
				<<"file \""<<filename<<"\""<<std::endl;
 | 
			
		||||
		<<"file \""<<filename<<"\""<<std::endl;
 | 
			
		||||
		m_sound->loadSoundData(name, data);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char *model_ext[] = {
 | 
			
		||||
		".x", ".b3d", ".md2", ".obj",
 | 
			
		||||
		NULL
 | 
			
		||||
	};
 | 
			
		||||
	name = removeStringEnd(filename, model_ext);
 | 
			
		||||
	if(name != "")
 | 
			
		||||
	{
 | 
			
		||||
		verbosestream<<"Client: Storing model into Irrlicht: "
 | 
			
		||||
				<<"\""<<filename<<"\""<<std::endl;
 | 
			
		||||
 | 
			
		||||
		io::IFileSystem *irrfs = m_device->getFileSystem();
 | 
			
		||||
		io::IReadFile *rfile = irrfs->createMemoryReadFile(
 | 
			
		||||
				*data_rw, data_rw.getSize(), filename.c_str());
 | 
			
		||||
		assert(rfile);
 | 
			
		||||
		
 | 
			
		||||
		scene::ISceneManager *smgr = m_device->getSceneManager();
 | 
			
		||||
		scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
 | 
			
		||||
		smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
 | 
			
		||||
		
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errorstream<<"Client: Don't know how to load file \""
 | 
			
		||||
			<<filename<<"\""<<std::endl;
 | 
			
		||||
	return false;
 | 
			
		||||
@@ -1961,7 +1984,7 @@ void Client::sendPlayerPos()
 | 
			
		||||
	v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
 | 
			
		||||
	s32 pitch = myplayer->getPitch() * 100;
 | 
			
		||||
	s32 yaw = myplayer->getYaw() * 100;
 | 
			
		||||
 | 
			
		||||
	u32 keyPressed=myplayer->keyPressed;
 | 
			
		||||
	/*
 | 
			
		||||
		Format:
 | 
			
		||||
		[0] u16 command
 | 
			
		||||
@@ -1969,15 +1992,15 @@ void Client::sendPlayerPos()
 | 
			
		||||
		[2+12] v3s32 speed*100
 | 
			
		||||
		[2+12+12] s32 pitch*100
 | 
			
		||||
		[2+12+12+4] s32 yaw*100
 | 
			
		||||
		[2+12+12+4+4] u32 keyPressed
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	SharedBuffer<u8> data(2+12+12+4+4);
 | 
			
		||||
	SharedBuffer<u8> data(2+12+12+4+4+4);
 | 
			
		||||
	writeU16(&data[0], TOSERVER_PLAYERPOS);
 | 
			
		||||
	writeV3S32(&data[2], position);
 | 
			
		||||
	writeV3S32(&data[2+12], speed);
 | 
			
		||||
	writeS32(&data[2+12+12], pitch);
 | 
			
		||||
	writeS32(&data[2+12+12+4], yaw);	
 | 
			
		||||
 | 
			
		||||
	writeU32(&data[2+12+12+4+4], keyPressed);
 | 
			
		||||
	// Send as unreliable
 | 
			
		||||
	Send(0, data, false);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef,
 | 
			
		||||
 | 
			
		||||
ClientActiveObject::~ClientActiveObject()
 | 
			
		||||
{
 | 
			
		||||
	removeFromScene();
 | 
			
		||||
	removeFromScene(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,
 | 
			
		||||
 
 | 
			
		||||
@@ -49,13 +49,19 @@ public:
 | 
			
		||||
 | 
			
		||||
	virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
 | 
			
		||||
			IrrlichtDevice *irr){}
 | 
			
		||||
	virtual void removeFromScene(){}
 | 
			
		||||
	virtual void removeFromScene(bool permanent){}
 | 
			
		||||
	// 0 <= light_at_pos <= LIGHT_SUN
 | 
			
		||||
	virtual void updateLight(u8 light_at_pos){}
 | 
			
		||||
	virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
 | 
			
		||||
	virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
 | 
			
		||||
	virtual core::aabbox3d<f32>* getCollisionBox(){return NULL;}
 | 
			
		||||
	virtual v3f getPosition(){return v3f(0,0,0);}
 | 
			
		||||
	virtual scene::IMeshSceneNode *getMeshSceneNode(){return NULL;}
 | 
			
		||||
	virtual scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode(){return NULL;}
 | 
			
		||||
	virtual scene::IBillboardSceneNode *getSpriteSceneNode(){return NULL;}
 | 
			
		||||
	virtual bool isPlayer(){return false;}
 | 
			
		||||
	virtual bool isLocalPlayer(){return false;}
 | 
			
		||||
	virtual void setAttachments(){}
 | 
			
		||||
	virtual bool doShowSelectionBox(){return true;}
 | 
			
		||||
	
 | 
			
		||||
	// Step object in time
 | 
			
		||||
 
 | 
			
		||||
@@ -67,9 +67,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
		TOCLIENT_DETACHED_INVENTORY
 | 
			
		||||
	PROTOCOL_VERSION 13:
 | 
			
		||||
		InventoryList field "Width" (deserialization fails with old versions)
 | 
			
		||||
	PROTOCOL_VERSION 14:
 | 
			
		||||
		Added transfer of player pressed keys to the server
 | 
			
		||||
		Added new messages for mesh and bone animation, as well as attachments
 | 
			
		||||
		GENERIC_CMD_SET_ANIMATION
 | 
			
		||||
		GENERIC_CMD_SET_BONE_POSITION
 | 
			
		||||
		GENERIC_CMD_SET_ATTACHMENT
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define PROTOCOL_VERSION 13
 | 
			
		||||
#define PROTOCOL_VERSION 14
 | 
			
		||||
 | 
			
		||||
#define PROTOCOL_ID 0x4f457403
 | 
			
		||||
 | 
			
		||||
@@ -366,6 +372,7 @@ enum ToServerCommand
 | 
			
		||||
		[2+12] v3s32 speed*100
 | 
			
		||||
		[2+12+12] s32 pitch*100
 | 
			
		||||
		[2+12+12+4] s32 yaw*100
 | 
			
		||||
		[2+12+12+4+4] u32 keyPressed
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	TOSERVER_GOTBLOCKS = 0x24,
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
#include "util/serialize.h"
 | 
			
		||||
#include "util/mathconstants.h"
 | 
			
		||||
#include "map.h"
 | 
			
		||||
#include "main.h" // g_settings
 | 
			
		||||
#include <IMeshManipulator.h>
 | 
			
		||||
#include <IAnimatedMeshSceneNode.h>
 | 
			
		||||
#include <IBoneSceneNode.h>
 | 
			
		||||
 | 
			
		||||
class Settings;
 | 
			
		||||
struct ToolCapabilities;
 | 
			
		||||
@@ -552,7 +555,8 @@ private:
 | 
			
		||||
	// Only set at initialization
 | 
			
		||||
	std::string m_name;
 | 
			
		||||
	bool m_is_player;
 | 
			
		||||
	bool m_is_local_player; // determined locally
 | 
			
		||||
	bool m_is_local_player;
 | 
			
		||||
	int m_id;
 | 
			
		||||
	// Property-ish things
 | 
			
		||||
	ObjectProperties m_prop;
 | 
			
		||||
	//
 | 
			
		||||
@@ -560,6 +564,7 @@ private:
 | 
			
		||||
	IrrlichtDevice *m_irr;
 | 
			
		||||
	core::aabbox3d<f32> m_selection_box;
 | 
			
		||||
	scene::IMeshSceneNode *m_meshnode;
 | 
			
		||||
	scene::IAnimatedMeshSceneNode *m_animated_meshnode;
 | 
			
		||||
	scene::IBillboardSceneNode *m_spritenode;
 | 
			
		||||
	scene::ITextSceneNode* m_textnode;
 | 
			
		||||
	v3f m_position;
 | 
			
		||||
@@ -573,6 +578,14 @@ private:
 | 
			
		||||
	v2s16 m_tx_basepos;
 | 
			
		||||
	bool m_initial_tx_basepos_set;
 | 
			
		||||
	bool m_tx_select_horiz_by_yawpitch;
 | 
			
		||||
	v2f m_animation_range;
 | 
			
		||||
	int m_animation_speed;
 | 
			
		||||
	int m_animation_blend;
 | 
			
		||||
	std::map<std::string, core::vector2d<v3f> > m_bone_position; // stores position and rotation for each bone name
 | 
			
		||||
	std::string m_attachment_bone;
 | 
			
		||||
	v3f m_attachment_position;
 | 
			
		||||
	v3f m_attachment_rotation;
 | 
			
		||||
	bool m_attached_to_local;
 | 
			
		||||
	int m_anim_frame;
 | 
			
		||||
	int m_anim_num_frames;
 | 
			
		||||
	float m_anim_framelength;
 | 
			
		||||
@@ -582,6 +595,7 @@ private:
 | 
			
		||||
	bool m_visuals_expired;
 | 
			
		||||
	float m_step_distance_counter;
 | 
			
		||||
	u8 m_last_light;
 | 
			
		||||
	bool m_is_visible;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
 | 
			
		||||
@@ -589,11 +603,13 @@ public:
 | 
			
		||||
		//
 | 
			
		||||
		m_is_player(false),
 | 
			
		||||
		m_is_local_player(false),
 | 
			
		||||
		m_id(0),
 | 
			
		||||
		//
 | 
			
		||||
		m_smgr(NULL),
 | 
			
		||||
		m_irr(NULL),
 | 
			
		||||
		m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.),
 | 
			
		||||
		m_meshnode(NULL),
 | 
			
		||||
		m_animated_meshnode(NULL),
 | 
			
		||||
		m_spritenode(NULL),
 | 
			
		||||
		m_textnode(NULL),
 | 
			
		||||
		m_position(v3f(0,10*BS,0)),
 | 
			
		||||
@@ -605,6 +621,14 @@ public:
 | 
			
		||||
		m_tx_basepos(0,0),
 | 
			
		||||
		m_initial_tx_basepos_set(false),
 | 
			
		||||
		m_tx_select_horiz_by_yawpitch(false),
 | 
			
		||||
		m_animation_range(v2f(0,0)),
 | 
			
		||||
		m_animation_speed(15),
 | 
			
		||||
		m_animation_blend(0),
 | 
			
		||||
		m_bone_position(std::map<std::string, core::vector2d<v3f> >()),
 | 
			
		||||
		m_attachment_bone(""),
 | 
			
		||||
		m_attachment_position(v3f(0,0,0)),
 | 
			
		||||
		m_attachment_rotation(v3f(0,0,0)),
 | 
			
		||||
		m_attached_to_local(false),
 | 
			
		||||
		m_anim_frame(0),
 | 
			
		||||
		m_anim_num_frames(1),
 | 
			
		||||
		m_anim_framelength(0.2),
 | 
			
		||||
@@ -612,7 +636,8 @@ public:
 | 
			
		||||
		m_reset_textures_timer(-1),
 | 
			
		||||
		m_visuals_expired(false),
 | 
			
		||||
		m_step_distance_counter(0),
 | 
			
		||||
		m_last_light(255)
 | 
			
		||||
		m_last_light(255),
 | 
			
		||||
		m_is_visible(false)
 | 
			
		||||
	{
 | 
			
		||||
		if(gamedef == NULL)
 | 
			
		||||
			ClientActiveObject::registerType(getType(), create);
 | 
			
		||||
@@ -632,6 +657,7 @@ public:
 | 
			
		||||
		}
 | 
			
		||||
		m_name = deSerializeString(is);
 | 
			
		||||
		m_is_player = readU8(is);
 | 
			
		||||
		m_id = readS16(is);
 | 
			
		||||
		m_position = readV3F1000(is);
 | 
			
		||||
		m_yaw = readF1000(is);
 | 
			
		||||
		m_hp = readS16(is);
 | 
			
		||||
@@ -668,21 +694,113 @@ public:
 | 
			
		||||
	}
 | 
			
		||||
	core::aabbox3d<f32>* getSelectionBox()
 | 
			
		||||
	{
 | 
			
		||||
		if(!m_prop.is_visible || m_is_local_player)
 | 
			
		||||
		if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		return &m_selection_box;
 | 
			
		||||
	}
 | 
			
		||||
	v3f getPosition()
 | 
			
		||||
	{
 | 
			
		||||
		if(getParent() != NULL){
 | 
			
		||||
			if(m_meshnode)
 | 
			
		||||
				return m_meshnode->getAbsolutePosition();
 | 
			
		||||
			if(m_animated_meshnode)
 | 
			
		||||
				return m_animated_meshnode->getAbsolutePosition();
 | 
			
		||||
			if(m_spritenode)
 | 
			
		||||
				return m_spritenode->getAbsolutePosition();
 | 
			
		||||
			return m_position;
 | 
			
		||||
		}
 | 
			
		||||
		return pos_translator.vect_show;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void removeFromScene()
 | 
			
		||||
	scene::IMeshSceneNode *getMeshSceneNode()
 | 
			
		||||
	{
 | 
			
		||||
		if(m_meshnode)
 | 
			
		||||
			return m_meshnode;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode()
 | 
			
		||||
	{
 | 
			
		||||
		if(m_animated_meshnode)
 | 
			
		||||
			return m_animated_meshnode;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scene::IBillboardSceneNode *getSpriteSceneNode()
 | 
			
		||||
	{
 | 
			
		||||
		if(m_spritenode)
 | 
			
		||||
			return m_spritenode;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool isPlayer()
 | 
			
		||||
	{
 | 
			
		||||
		return m_is_player;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool isLocalPlayer()
 | 
			
		||||
	{
 | 
			
		||||
		return m_is_local_player;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void setAttachments()
 | 
			
		||||
	{
 | 
			
		||||
		updateAttachments();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ClientActiveObject *getParent()
 | 
			
		||||
	{
 | 
			
		||||
		ClientActiveObject *obj = NULL;
 | 
			
		||||
		for(std::vector<core::vector2d<int> >::const_iterator cii = m_env->attachment_list.begin(); cii != m_env->attachment_list.end(); cii++)
 | 
			
		||||
		{
 | 
			
		||||
			if(cii->X == getId()){ // This ID is our child
 | 
			
		||||
				if(cii->Y > 0){ // A parent ID exists for our child
 | 
			
		||||
					if(cii->X != cii->Y){ // The parent and child ID are not the same
 | 
			
		||||
						obj = m_env->getActiveObject(cii->Y);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if(obj)
 | 
			
		||||
			return obj;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void removeFromScene(bool permanent)
 | 
			
		||||
	{
 | 
			
		||||
		if(permanent) // Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
 | 
			
		||||
		{
 | 
			
		||||
			// Detach this object's children
 | 
			
		||||
			for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
 | 
			
		||||
			{
 | 
			
		||||
				if(ii->Y == getId()) // Is a child of our object
 | 
			
		||||
				{
 | 
			
		||||
					ii->Y = 0;
 | 
			
		||||
					ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
 | 
			
		||||
					if(obj)
 | 
			
		||||
						obj->setAttachments();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			// Delete this object from the attachments list
 | 
			
		||||
			for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
 | 
			
		||||
			{
 | 
			
		||||
				if(ii->X == getId()) // Is our object
 | 
			
		||||
				{
 | 
			
		||||
					m_env->attachment_list.erase(ii);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(m_meshnode){
 | 
			
		||||
			m_meshnode->remove();
 | 
			
		||||
			m_meshnode = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if(m_animated_meshnode){
 | 
			
		||||
			m_animated_meshnode->remove();
 | 
			
		||||
			m_animated_meshnode = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if(m_spritenode){
 | 
			
		||||
			m_spritenode->remove();
 | 
			
		||||
			m_spritenode = NULL;
 | 
			
		||||
@@ -695,7 +813,7 @@ public:
 | 
			
		||||
		m_smgr = smgr;
 | 
			
		||||
		m_irr = irr;
 | 
			
		||||
 | 
			
		||||
		if(m_meshnode != NULL || m_spritenode != NULL)
 | 
			
		||||
		if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
 | 
			
		||||
			return;
 | 
			
		||||
		
 | 
			
		||||
		m_visuals_expired = false;
 | 
			
		||||
@@ -791,7 +909,24 @@ public:
 | 
			
		||||
					m_prop.visual_size.X));
 | 
			
		||||
			u8 li = m_last_light;
 | 
			
		||||
			setMeshColor(m_meshnode->getMesh(), video::SColor(255,li,li,li));
 | 
			
		||||
		} else if(m_prop.visual == "wielditem"){
 | 
			
		||||
		}
 | 
			
		||||
		else if(m_prop.visual == "mesh"){
 | 
			
		||||
			infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
 | 
			
		||||
			scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
 | 
			
		||||
			if(mesh)
 | 
			
		||||
			{
 | 
			
		||||
				m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
 | 
			
		||||
				m_animated_meshnode->animateJoints(); // Needed for some animations
 | 
			
		||||
				m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
 | 
			
		||||
						m_prop.visual_size.Y,
 | 
			
		||||
						m_prop.visual_size.X));
 | 
			
		||||
				u8 li = m_last_light;
 | 
			
		||||
				setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
				errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
 | 
			
		||||
		}
 | 
			
		||||
		else if(m_prop.visual == "wielditem"){
 | 
			
		||||
			infostream<<"GenericCAO::addToScene(): node"<<std::endl;
 | 
			
		||||
			infostream<<"textures: "<<m_prop.textures.size()<<std::endl;
 | 
			
		||||
			if(m_prop.textures.size() >= 1){
 | 
			
		||||
@@ -823,6 +958,8 @@ public:
 | 
			
		||||
		scene::ISceneNode *node = NULL;
 | 
			
		||||
		if(m_spritenode)
 | 
			
		||||
			node = m_spritenode;
 | 
			
		||||
		else if(m_animated_meshnode)
 | 
			
		||||
			node = m_animated_meshnode;
 | 
			
		||||
		else if(m_meshnode)
 | 
			
		||||
			node = m_meshnode;
 | 
			
		||||
		if(node && m_is_player && !m_is_local_player){
 | 
			
		||||
@@ -835,6 +972,9 @@ public:
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		updateNodePos();
 | 
			
		||||
		updateAnimation();
 | 
			
		||||
		updateBonePosition();
 | 
			
		||||
		updateAttachments();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void expireVisuals()
 | 
			
		||||
@@ -844,19 +984,16 @@ public:
 | 
			
		||||
		
 | 
			
		||||
	void updateLight(u8 light_at_pos)
 | 
			
		||||
	{
 | 
			
		||||
		bool is_visible = (m_hp != 0);
 | 
			
		||||
		u8 li = decode_light(light_at_pos);
 | 
			
		||||
		if(li != m_last_light){
 | 
			
		||||
			m_last_light = li;
 | 
			
		||||
			video::SColor color(255,li,li,li);
 | 
			
		||||
			if(m_meshnode){
 | 
			
		||||
			if(m_meshnode)
 | 
			
		||||
				setMeshColor(m_meshnode->getMesh(), color);
 | 
			
		||||
				m_meshnode->setVisible(is_visible);
 | 
			
		||||
			}
 | 
			
		||||
			if(m_spritenode){
 | 
			
		||||
			if(m_animated_meshnode)
 | 
			
		||||
				setMeshColor(m_animated_meshnode->getMesh(), color);
 | 
			
		||||
			if(m_spritenode)
 | 
			
		||||
				m_spritenode->setColor(color);
 | 
			
		||||
				m_spritenode->setVisible(is_visible);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -867,12 +1004,21 @@ public:
 | 
			
		||||
 | 
			
		||||
	void updateNodePos()
 | 
			
		||||
	{
 | 
			
		||||
		if(getParent() != NULL)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		if(m_meshnode){
 | 
			
		||||
			m_meshnode->setPosition(pos_translator.vect_show);
 | 
			
		||||
			v3f rot = m_meshnode->getRotation();
 | 
			
		||||
			rot.Y = -m_yaw;
 | 
			
		||||
			m_meshnode->setRotation(rot);
 | 
			
		||||
		}
 | 
			
		||||
		if(m_animated_meshnode){
 | 
			
		||||
			m_animated_meshnode->setPosition(pos_translator.vect_show);
 | 
			
		||||
			v3f rot = m_animated_meshnode->getRotation();
 | 
			
		||||
			rot.Y = -m_yaw;
 | 
			
		||||
			m_animated_meshnode->setRotation(rot);
 | 
			
		||||
		}
 | 
			
		||||
		if(m_spritenode){
 | 
			
		||||
			m_spritenode->setPosition(pos_translator.vect_show);
 | 
			
		||||
		}
 | 
			
		||||
@@ -880,56 +1026,116 @@ public:
 | 
			
		||||
 | 
			
		||||
	void step(float dtime, ClientEnvironment *env)
 | 
			
		||||
	{
 | 
			
		||||
		v3f lastpos = pos_translator.vect_show;
 | 
			
		||||
 | 
			
		||||
		if(m_visuals_expired && m_smgr && m_irr){
 | 
			
		||||
			m_visuals_expired = false;
 | 
			
		||||
			removeFromScene();
 | 
			
		||||
 | 
			
		||||
			// Attachments, part 1: All attached objects must be unparented first, or Irrlicht causes a segmentation fault
 | 
			
		||||
			for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
 | 
			
		||||
			{
 | 
			
		||||
				if(ii->Y == getId()) // This is a child of our parent
 | 
			
		||||
				{
 | 
			
		||||
					ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
 | 
			
		||||
					if(obj)
 | 
			
		||||
					{
 | 
			
		||||
						scene::IMeshSceneNode *m_child_meshnode = obj->getMeshSceneNode();
 | 
			
		||||
						scene::IAnimatedMeshSceneNode *m_child_animated_meshnode = obj->getAnimatedMeshSceneNode();
 | 
			
		||||
						scene::IBillboardSceneNode *m_child_spritenode = obj->getSpriteSceneNode();
 | 
			
		||||
						if(m_child_meshnode)
 | 
			
		||||
							m_child_meshnode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
						if(m_child_animated_meshnode)
 | 
			
		||||
							m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
						if(m_child_spritenode)
 | 
			
		||||
							m_child_spritenode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			removeFromScene(false);
 | 
			
		||||
			addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
 | 
			
		||||
 | 
			
		||||
			// Attachments, part 2: Now that the parent has been refreshed, put its attachments back
 | 
			
		||||
			for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
 | 
			
		||||
			{
 | 
			
		||||
				if(ii->Y == getId()) // This is a child of our parent
 | 
			
		||||
				{
 | 
			
		||||
					ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
 | 
			
		||||
					if(obj)
 | 
			
		||||
						obj->setAttachments();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(m_prop.physical){
 | 
			
		||||
			core::aabbox3d<f32> box = m_prop.collisionbox;
 | 
			
		||||
			box.MinEdge *= BS;
 | 
			
		||||
			box.MaxEdge *= BS;
 | 
			
		||||
			collisionMoveResult moveresult;
 | 
			
		||||
			f32 pos_max_d = BS*0.125; // Distance per iteration
 | 
			
		||||
			f32 stepheight = 0;
 | 
			
		||||
			v3f p_pos = m_position;
 | 
			
		||||
			v3f p_velocity = m_velocity;
 | 
			
		||||
			v3f p_acceleration = m_acceleration;
 | 
			
		||||
			IGameDef *gamedef = env->getGameDef();
 | 
			
		||||
			moveresult = collisionMoveSimple(&env->getMap(), gamedef,
 | 
			
		||||
					pos_max_d, box, stepheight, dtime,
 | 
			
		||||
					p_pos, p_velocity, p_acceleration);
 | 
			
		||||
			// Apply results
 | 
			
		||||
			m_position = p_pos;
 | 
			
		||||
			m_velocity = p_velocity;
 | 
			
		||||
			m_acceleration = p_acceleration;
 | 
			
		||||
		// Make sure m_is_visible is always applied
 | 
			
		||||
		if(m_meshnode)
 | 
			
		||||
			m_meshnode->setVisible(m_is_visible);
 | 
			
		||||
		if(m_animated_meshnode)
 | 
			
		||||
			m_animated_meshnode->setVisible(m_is_visible);
 | 
			
		||||
		if(m_spritenode)
 | 
			
		||||
			m_spritenode->setVisible(m_is_visible);
 | 
			
		||||
		if(m_textnode)
 | 
			
		||||
			m_textnode->setVisible(m_is_visible);
 | 
			
		||||
 | 
			
		||||
			bool is_end_position = moveresult.collides;
 | 
			
		||||
			pos_translator.update(m_position, is_end_position, dtime);
 | 
			
		||||
			pos_translator.translate(dtime);
 | 
			
		||||
			updateNodePos();
 | 
			
		||||
		} else {
 | 
			
		||||
			m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
 | 
			
		||||
			m_velocity += dtime * m_acceleration;
 | 
			
		||||
			pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
 | 
			
		||||
			pos_translator.translate(dtime);
 | 
			
		||||
			updateNodePos();
 | 
			
		||||
		if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
 | 
			
		||||
		{
 | 
			
		||||
			// Set these for later
 | 
			
		||||
			m_position = getPosition();
 | 
			
		||||
			m_velocity = v3f(0,0,0);
 | 
			
		||||
			m_acceleration = v3f(0,0,0);
 | 
			
		||||
			pos_translator.vect_show = m_position;
 | 
			
		||||
 | 
			
		||||
			if(m_is_local_player) // Update local player attachment position
 | 
			
		||||
			{
 | 
			
		||||
				LocalPlayer *player = m_env->getLocalPlayer();
 | 
			
		||||
				player->overridePosition = getParent()->getPosition();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			v3f lastpos = pos_translator.vect_show;
 | 
			
		||||
 | 
			
		||||
		float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
 | 
			
		||||
		m_step_distance_counter += moved;
 | 
			
		||||
		if(m_step_distance_counter > 1.5*BS){
 | 
			
		||||
			m_step_distance_counter = 0;
 | 
			
		||||
			if(!m_is_local_player && m_prop.makes_footstep_sound){
 | 
			
		||||
				INodeDefManager *ndef = m_gamedef->ndef();
 | 
			
		||||
				v3s16 p = floatToInt(getPosition() + v3f(0,
 | 
			
		||||
						(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
 | 
			
		||||
				MapNode n = m_env->getMap().getNodeNoEx(p);
 | 
			
		||||
				SimpleSoundSpec spec = ndef->get(n).sound_footstep;
 | 
			
		||||
				m_gamedef->sound()->playSoundAt(spec, false, getPosition());
 | 
			
		||||
			if(m_prop.physical){
 | 
			
		||||
				core::aabbox3d<f32> box = m_prop.collisionbox;
 | 
			
		||||
				box.MinEdge *= BS;
 | 
			
		||||
				box.MaxEdge *= BS;
 | 
			
		||||
				collisionMoveResult moveresult;
 | 
			
		||||
				f32 pos_max_d = BS*0.125; // Distance per iteration
 | 
			
		||||
				f32 stepheight = 0;
 | 
			
		||||
				v3f p_pos = m_position;
 | 
			
		||||
				v3f p_velocity = m_velocity;
 | 
			
		||||
				v3f p_acceleration = m_acceleration;
 | 
			
		||||
				IGameDef *gamedef = env->getGameDef();
 | 
			
		||||
				moveresult = collisionMoveSimple(&env->getMap(), gamedef,
 | 
			
		||||
						pos_max_d, box, stepheight, dtime,
 | 
			
		||||
						p_pos, p_velocity, p_acceleration);
 | 
			
		||||
				// Apply results
 | 
			
		||||
				m_position = p_pos;
 | 
			
		||||
				m_velocity = p_velocity;
 | 
			
		||||
				m_acceleration = p_acceleration;
 | 
			
		||||
				
 | 
			
		||||
				bool is_end_position = moveresult.collides;
 | 
			
		||||
				pos_translator.update(m_position, is_end_position, dtime);
 | 
			
		||||
				pos_translator.translate(dtime);
 | 
			
		||||
				updateNodePos();
 | 
			
		||||
			} else {
 | 
			
		||||
				m_position += dtime * m_velocity + 0.5 * dtime * dtime * m_acceleration;
 | 
			
		||||
				m_velocity += dtime * m_acceleration;
 | 
			
		||||
				pos_translator.update(m_position, pos_translator.aim_is_end, pos_translator.anim_time);
 | 
			
		||||
				pos_translator.translate(dtime);
 | 
			
		||||
				updateNodePos();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			float moved = lastpos.getDistanceFrom(pos_translator.vect_show);
 | 
			
		||||
			m_step_distance_counter += moved;
 | 
			
		||||
			if(m_step_distance_counter > 1.5*BS){
 | 
			
		||||
				m_step_distance_counter = 0;
 | 
			
		||||
				if(!m_is_local_player && m_prop.makes_footstep_sound){
 | 
			
		||||
					INodeDefManager *ndef = m_gamedef->ndef();
 | 
			
		||||
					v3s16 p = floatToInt(getPosition() + v3f(0,
 | 
			
		||||
							(m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS);
 | 
			
		||||
					MapNode n = m_env->getMap().getNodeNoEx(p);
 | 
			
		||||
					SimpleSoundSpec spec = ndef->get(n).sound_footstep;
 | 
			
		||||
					m_gamedef->sound()->playSoundAt(spec, false, getPosition());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -950,7 +1156,7 @@ public:
 | 
			
		||||
				updateTextures("");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if(fabs(m_prop.automatic_rotate) > 0.001){
 | 
			
		||||
		if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001){
 | 
			
		||||
			m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
 | 
			
		||||
			updateNodePos();
 | 
			
		||||
		}
 | 
			
		||||
@@ -1008,6 +1214,10 @@ public:
 | 
			
		||||
	{
 | 
			
		||||
		ITextureSource *tsrc = m_gamedef->tsrc();
 | 
			
		||||
 | 
			
		||||
		bool use_trilinear_filter = g_settings->getBool("trilinear_filter");
 | 
			
		||||
		bool use_bilinear_filter = g_settings->getBool("bilinear_filter");
 | 
			
		||||
		bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter");
 | 
			
		||||
 | 
			
		||||
		if(m_spritenode)
 | 
			
		||||
		{
 | 
			
		||||
			if(m_prop.visual == "sprite")
 | 
			
		||||
@@ -1018,6 +1228,58 @@ public:
 | 
			
		||||
				texturestring += mod;
 | 
			
		||||
				m_spritenode->setMaterialTexture(0,
 | 
			
		||||
						tsrc->getTextureRaw(texturestring));
 | 
			
		||||
 | 
			
		||||
				// This allows setting per-material colors. However, until a real lighting
 | 
			
		||||
				// system is added, the code below will have no effect. Once MineTest
 | 
			
		||||
				// has directional lighting, it should work automatically.
 | 
			
		||||
				if(m_prop.colors.size() >= 1)
 | 
			
		||||
				{
 | 
			
		||||
					m_spritenode->getMaterial(0).AmbientColor = m_prop.colors[0];
 | 
			
		||||
					m_spritenode->getMaterial(0).DiffuseColor = m_prop.colors[0];
 | 
			
		||||
					m_spritenode->getMaterial(0).SpecularColor = m_prop.colors[0];
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				m_spritenode->getMaterial(0).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
 | 
			
		||||
				m_spritenode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
 | 
			
		||||
				m_spritenode->getMaterial(0).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if(m_animated_meshnode)
 | 
			
		||||
		{
 | 
			
		||||
			if(m_prop.visual == "mesh")
 | 
			
		||||
			{
 | 
			
		||||
				for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
 | 
			
		||||
				{
 | 
			
		||||
					std::string texturestring = m_prop.textures[i];
 | 
			
		||||
					if(texturestring == "")
 | 
			
		||||
						continue; // Empty texture string means don't modify that material
 | 
			
		||||
					texturestring += mod;
 | 
			
		||||
					video::ITexture* texture = tsrc->getTextureRaw(texturestring);
 | 
			
		||||
					if(!texture)
 | 
			
		||||
					{
 | 
			
		||||
						errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// Set material flags and texture
 | 
			
		||||
					m_animated_meshnode->setMaterialTexture(i, texture);
 | 
			
		||||
					video::SMaterial& material = m_animated_meshnode->getMaterial(i);
 | 
			
		||||
					material.setFlag(video::EMF_LIGHTING, false);
 | 
			
		||||
					material.setFlag(video::EMF_BILINEAR_FILTER, false);
 | 
			
		||||
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
 | 
			
		||||
				}
 | 
			
		||||
				for (u32 i = 0; i < m_prop.colors.size() && i < m_animated_meshnode->getMaterialCount(); ++i)
 | 
			
		||||
				{
 | 
			
		||||
					// This allows setting per-material colors. However, until a real lighting
 | 
			
		||||
					// system is added, the code below will have no effect. Once MineTest
 | 
			
		||||
					// has directional lighting, it should work automatically.
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
 | 
			
		||||
					m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if(m_meshnode)
 | 
			
		||||
@@ -1044,6 +1306,20 @@ public:
 | 
			
		||||
					material.setTexture(0, atlas);
 | 
			
		||||
					material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
 | 
			
		||||
					material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
 | 
			
		||||
 | 
			
		||||
					// This allows setting per-material colors. However, until a real lighting
 | 
			
		||||
					// system is added, the code below will have no effect. Once MineTest
 | 
			
		||||
					// has directional lighting, it should work automatically.
 | 
			
		||||
					if(m_prop.colors.size() > i)
 | 
			
		||||
					{
 | 
			
		||||
						m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
 | 
			
		||||
						m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
 | 
			
		||||
						m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					m_meshnode->getMaterial(i).setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
 | 
			
		||||
					m_meshnode->getMaterial(i).setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
 | 
			
		||||
					m_meshnode->getMaterial(i).setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if(m_prop.visual == "upright_sprite")
 | 
			
		||||
@@ -1057,6 +1333,20 @@ public:
 | 
			
		||||
					scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
 | 
			
		||||
					buf->getMaterial().setTexture(0,
 | 
			
		||||
							tsrc->getTextureRaw(tname));
 | 
			
		||||
					
 | 
			
		||||
					// This allows setting per-material colors. However, until a real lighting
 | 
			
		||||
					// system is added, the code below will have no effect. Once MineTest
 | 
			
		||||
					// has directional lighting, it should work automatically.
 | 
			
		||||
					if(m_prop.colors.size() >= 1)
 | 
			
		||||
					{
 | 
			
		||||
						buf->getMaterial().AmbientColor = m_prop.colors[0];
 | 
			
		||||
						buf->getMaterial().DiffuseColor = m_prop.colors[0];
 | 
			
		||||
						buf->getMaterial().SpecularColor = m_prop.colors[0];
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
 | 
			
		||||
				}
 | 
			
		||||
				{
 | 
			
		||||
					std::string tname = "unknown_object.png";
 | 
			
		||||
@@ -1068,11 +1358,213 @@ public:
 | 
			
		||||
					scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
 | 
			
		||||
					buf->getMaterial().setTexture(0,
 | 
			
		||||
							tsrc->getTextureRaw(tname));
 | 
			
		||||
 | 
			
		||||
					// This allows setting per-material colors. However, until a real lighting
 | 
			
		||||
					// system is added, the code below will have no effect. Once MineTest
 | 
			
		||||
					// has directional lighting, it should work automatically.
 | 
			
		||||
					if(m_prop.colors.size() >= 2)
 | 
			
		||||
					{
 | 
			
		||||
						buf->getMaterial().AmbientColor = m_prop.colors[1];
 | 
			
		||||
						buf->getMaterial().DiffuseColor = m_prop.colors[1];
 | 
			
		||||
						buf->getMaterial().SpecularColor = m_prop.colors[1];
 | 
			
		||||
					}
 | 
			
		||||
					else if(m_prop.colors.size() >= 1)
 | 
			
		||||
					{
 | 
			
		||||
						buf->getMaterial().AmbientColor = m_prop.colors[0];
 | 
			
		||||
						buf->getMaterial().DiffuseColor = m_prop.colors[0];
 | 
			
		||||
						buf->getMaterial().SpecularColor = m_prop.colors[0];
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter);
 | 
			
		||||
					buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void updateAnimation()
 | 
			
		||||
	{
 | 
			
		||||
		if(m_animated_meshnode == NULL)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		m_animated_meshnode->setFrameLoop((int)m_animation_range.X, (int)m_animation_range.Y);
 | 
			
		||||
		m_animated_meshnode->setAnimationSpeed(m_animation_speed);
 | 
			
		||||
		m_animated_meshnode->setTransitionTime(m_animation_blend);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void updateBonePosition()
 | 
			
		||||
	{
 | 
			
		||||
		if(!m_bone_position.size() || m_animated_meshnode == NULL)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
 | 
			
		||||
		for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
 | 
			
		||||
			std::string bone_name = (*ii).first;
 | 
			
		||||
			v3f bone_pos = (*ii).second.X;
 | 
			
		||||
			v3f bone_rot = (*ii).second.Y;
 | 
			
		||||
			irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
 | 
			
		||||
			if(bone)
 | 
			
		||||
			{
 | 
			
		||||
				bone->setPosition(bone_pos);
 | 
			
		||||
				bone->setRotation(bone_rot);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	void updateAttachments()
 | 
			
		||||
	{
 | 
			
		||||
		m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
 | 
			
		||||
		m_is_visible = !m_attached_to_local; // Objects attached to the local player should always be hidden
 | 
			
		||||
 | 
			
		||||
		if(getParent() == NULL || m_attached_to_local) // Detach or don't attach
 | 
			
		||||
		{
 | 
			
		||||
			if(m_meshnode)
 | 
			
		||||
			{
 | 
			
		||||
				v3f old_position = m_meshnode->getAbsolutePosition();
 | 
			
		||||
				v3f old_rotation = m_meshnode->getRotation();
 | 
			
		||||
				m_meshnode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
				m_meshnode->setPosition(old_position);
 | 
			
		||||
				m_meshnode->setRotation(old_rotation);
 | 
			
		||||
				m_meshnode->updateAbsolutePosition();
 | 
			
		||||
			}
 | 
			
		||||
			if(m_animated_meshnode)
 | 
			
		||||
			{
 | 
			
		||||
				v3f old_position = m_animated_meshnode->getAbsolutePosition();
 | 
			
		||||
				v3f old_rotation = m_animated_meshnode->getRotation();
 | 
			
		||||
				m_animated_meshnode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
				m_animated_meshnode->setPosition(old_position);
 | 
			
		||||
				m_animated_meshnode->setRotation(old_rotation);
 | 
			
		||||
				m_animated_meshnode->updateAbsolutePosition();
 | 
			
		||||
			}
 | 
			
		||||
			if(m_spritenode)
 | 
			
		||||
			{
 | 
			
		||||
				v3f old_position = m_spritenode->getAbsolutePosition();
 | 
			
		||||
				v3f old_rotation = m_spritenode->getRotation();
 | 
			
		||||
				m_spritenode->setParent(m_smgr->getRootSceneNode());
 | 
			
		||||
				m_spritenode->setPosition(old_position);
 | 
			
		||||
				m_spritenode->setRotation(old_rotation);
 | 
			
		||||
				m_spritenode->updateAbsolutePosition();
 | 
			
		||||
			}
 | 
			
		||||
			if(m_is_local_player)
 | 
			
		||||
			{
 | 
			
		||||
				LocalPlayer *player = m_env->getLocalPlayer();
 | 
			
		||||
				player->isAttached = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else // Attach
 | 
			
		||||
		{
 | 
			
		||||
			scene::IMeshSceneNode *parent_mesh = NULL;
 | 
			
		||||
			if(getParent()->getMeshSceneNode())
 | 
			
		||||
				parent_mesh = getParent()->getMeshSceneNode();
 | 
			
		||||
			scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
 | 
			
		||||
			if(getParent()->getAnimatedMeshSceneNode())
 | 
			
		||||
				parent_animated_mesh = getParent()->getAnimatedMeshSceneNode();
 | 
			
		||||
			scene::IBillboardSceneNode *parent_sprite = NULL;
 | 
			
		||||
			if(getParent()->getSpriteSceneNode())
 | 
			
		||||
				parent_sprite = getParent()->getSpriteSceneNode();
 | 
			
		||||
 | 
			
		||||
			scene::IBoneSceneNode *parent_bone = NULL;
 | 
			
		||||
			if(parent_animated_mesh && m_attachment_bone != "")
 | 
			
		||||
				parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
 | 
			
		||||
 | 
			
		||||
			// The spaghetti code below makes sure attaching works if either the parent or child is a spritenode, meshnode, or animatedmeshnode
 | 
			
		||||
			// TODO: Perhaps use polymorphism here to save code duplication
 | 
			
		||||
			if(m_meshnode){
 | 
			
		||||
				if(parent_bone){
 | 
			
		||||
					m_meshnode->setParent(parent_bone);
 | 
			
		||||
					m_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
					m_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
					m_meshnode->updateAbsolutePosition();
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					if(parent_mesh){
 | 
			
		||||
						m_meshnode->setParent(parent_mesh);
 | 
			
		||||
						m_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_animated_mesh){
 | 
			
		||||
						m_meshnode->setParent(parent_animated_mesh);
 | 
			
		||||
						m_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_sprite){
 | 
			
		||||
						m_meshnode->setParent(parent_sprite);
 | 
			
		||||
						m_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if(m_animated_meshnode){
 | 
			
		||||
				if(parent_bone){
 | 
			
		||||
					m_animated_meshnode->setParent(parent_bone);
 | 
			
		||||
					m_animated_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
					m_animated_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
					m_animated_meshnode->updateAbsolutePosition();
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					if(parent_mesh){
 | 
			
		||||
						m_animated_meshnode->setParent(parent_mesh);
 | 
			
		||||
						m_animated_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_animated_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_animated_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_animated_mesh){
 | 
			
		||||
						m_animated_meshnode->setParent(parent_animated_mesh);
 | 
			
		||||
						m_animated_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_animated_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_animated_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_sprite){
 | 
			
		||||
						m_animated_meshnode->setParent(parent_sprite);
 | 
			
		||||
						m_animated_meshnode->setPosition(m_attachment_position);
 | 
			
		||||
						m_animated_meshnode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_animated_meshnode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if(m_spritenode){
 | 
			
		||||
				if(parent_bone){
 | 
			
		||||
					m_spritenode->setParent(parent_bone);
 | 
			
		||||
					m_spritenode->setPosition(m_attachment_position);
 | 
			
		||||
					m_spritenode->setRotation(m_attachment_rotation);
 | 
			
		||||
					m_spritenode->updateAbsolutePosition();
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					if(parent_mesh){
 | 
			
		||||
						m_spritenode->setParent(parent_mesh);
 | 
			
		||||
						m_spritenode->setPosition(m_attachment_position);
 | 
			
		||||
						m_spritenode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_spritenode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_animated_mesh){
 | 
			
		||||
						m_spritenode->setParent(parent_animated_mesh);
 | 
			
		||||
						m_spritenode->setPosition(m_attachment_position);
 | 
			
		||||
						m_spritenode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_spritenode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
					else if(parent_sprite){
 | 
			
		||||
						m_spritenode->setParent(parent_sprite);
 | 
			
		||||
						m_spritenode->setPosition(m_attachment_position);
 | 
			
		||||
						m_spritenode->setRotation(m_attachment_rotation);
 | 
			
		||||
						m_spritenode->updateAbsolutePosition();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if(m_is_local_player)
 | 
			
		||||
			{
 | 
			
		||||
				LocalPlayer *player = m_env->getLocalPlayer();
 | 
			
		||||
				player->isAttached = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void processMessage(const std::string &data)
 | 
			
		||||
	{
 | 
			
		||||
		//infostream<<"GenericCAO: Got message"<<std::endl;
 | 
			
		||||
@@ -1099,6 +1591,8 @@ public:
 | 
			
		||||
		}
 | 
			
		||||
		else if(cmd == GENERIC_CMD_UPDATE_POSITION)
 | 
			
		||||
		{
 | 
			
		||||
			// Not sent by the server if this object is an attachment.
 | 
			
		||||
			// We might however get here if the server notices the object being detached before the client.
 | 
			
		||||
			m_position = readV3F1000(is);
 | 
			
		||||
			m_velocity = readV3F1000(is);
 | 
			
		||||
			m_acceleration = readV3F1000(is);
 | 
			
		||||
@@ -1113,6 +1607,9 @@ public:
 | 
			
		||||
			if(m_prop.physical)
 | 
			
		||||
				m_position += v3f(0,0.002,0);
 | 
			
		||||
 | 
			
		||||
			if(getParent() != NULL) // Just in case
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
			if(do_interpolate){
 | 
			
		||||
				if(!m_prop.physical)
 | 
			
		||||
					pos_translator.update(m_position, is_end_position, update_interval);
 | 
			
		||||
@@ -1140,6 +1637,41 @@ public:
 | 
			
		||||
 | 
			
		||||
			updateTexturePos();
 | 
			
		||||
		}
 | 
			
		||||
		else if(cmd == GENERIC_CMD_SET_ANIMATION)
 | 
			
		||||
		{
 | 
			
		||||
			m_animation_range = readV2F1000(is);
 | 
			
		||||
			m_animation_speed = readF1000(is);
 | 
			
		||||
			m_animation_blend = readF1000(is);
 | 
			
		||||
 | 
			
		||||
			updateAnimation();
 | 
			
		||||
		}
 | 
			
		||||
		else if(cmd == GENERIC_CMD_SET_BONE_POSITION)
 | 
			
		||||
		{
 | 
			
		||||
			std::string bone = deSerializeString(is);
 | 
			
		||||
			v3f position = readV3F1000(is);
 | 
			
		||||
			v3f rotation = readV3F1000(is);
 | 
			
		||||
			m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
 | 
			
		||||
 | 
			
		||||
			updateBonePosition();
 | 
			
		||||
		}
 | 
			
		||||
		else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
 | 
			
		||||
		{
 | 
			
		||||
			// If an entry already exists for this object, delete it first to avoid duplicates
 | 
			
		||||
			for(std::vector<core::vector2d<int> >::iterator ii = m_env->attachment_list.begin(); ii != m_env->attachment_list.end(); ii++)
 | 
			
		||||
			{
 | 
			
		||||
				if(ii->X == getId()) // This is the ID of our object
 | 
			
		||||
				{
 | 
			
		||||
					m_env->attachment_list.erase(ii);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			m_env->attachment_list.push_back(core::vector2d<int>(getId(), readS16(is)));
 | 
			
		||||
			m_attachment_bone = deSerializeString(is);
 | 
			
		||||
			m_attachment_position = readV3F1000(is);
 | 
			
		||||
			m_attachment_rotation = readV3F1000(is);
 | 
			
		||||
 | 
			
		||||
			updateAttachments();
 | 
			
		||||
		}
 | 
			
		||||
		else if(cmd == GENERIC_CMD_PUNCHED)
 | 
			
		||||
		{
 | 
			
		||||
			/*s16 damage =*/ readS16(is);
 | 
			
		||||
 
 | 
			
		||||
@@ -355,7 +355,10 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
 | 
			
		||||
	m_last_sent_velocity(0,0,0),
 | 
			
		||||
	m_last_sent_position_timer(0),
 | 
			
		||||
	m_last_sent_move_precision(0),
 | 
			
		||||
	m_armor_groups_sent(false)
 | 
			
		||||
	m_armor_groups_sent(false),
 | 
			
		||||
	m_animation_sent(false),
 | 
			
		||||
	m_bone_position_sent(false),
 | 
			
		||||
	m_attachment_sent(false)
 | 
			
		||||
{
 | 
			
		||||
	// Only register type if no environment supplied
 | 
			
		||||
	if(env == NULL){
 | 
			
		||||
@@ -429,6 +432,17 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
 | 
			
		||||
	return sao;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LuaEntitySAO::isAttached()
 | 
			
		||||
{
 | 
			
		||||
	if(!m_attachment_parent_id)
 | 
			
		||||
		return false;
 | 
			
		||||
	// Check if the parent still exists
 | 
			
		||||
	ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
 | 
			
		||||
	if(obj)
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::step(float dtime, bool send_recommended)
 | 
			
		||||
{
 | 
			
		||||
	if(!m_properties_sent)
 | 
			
		||||
@@ -440,30 +454,52 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If attached, check that our parent is still there. If it isn't, detach.
 | 
			
		||||
	if(m_attachment_parent_id && !isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		m_attachment_parent_id = 0;
 | 
			
		||||
		m_attachment_bone = "";
 | 
			
		||||
		m_attachment_position = v3f(0,0,0);
 | 
			
		||||
		m_attachment_rotation = v3f(0,0,0);
 | 
			
		||||
		sendPosition(false, true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_last_sent_position_timer += dtime;
 | 
			
		||||
 | 
			
		||||
	if(m_prop.physical){
 | 
			
		||||
		core::aabbox3d<f32> box = m_prop.collisionbox;
 | 
			
		||||
		box.MinEdge *= BS;
 | 
			
		||||
		box.MaxEdge *= BS;
 | 
			
		||||
		collisionMoveResult moveresult;
 | 
			
		||||
		f32 pos_max_d = BS*0.25; // Distance per iteration
 | 
			
		||||
		f32 stepheight = 0; // Maximum climbable step height
 | 
			
		||||
		v3f p_pos = m_base_position;
 | 
			
		||||
		v3f p_velocity = m_velocity;
 | 
			
		||||
		v3f p_acceleration = m_acceleration;
 | 
			
		||||
		IGameDef *gamedef = m_env->getGameDef();
 | 
			
		||||
		moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
 | 
			
		||||
				pos_max_d, box, stepheight, dtime,
 | 
			
		||||
				p_pos, p_velocity, p_acceleration);
 | 
			
		||||
		// Apply results
 | 
			
		||||
		m_base_position = p_pos;
 | 
			
		||||
		m_velocity = p_velocity;
 | 
			
		||||
		m_acceleration = p_acceleration;
 | 
			
		||||
	} else {
 | 
			
		||||
		m_base_position += dtime * m_velocity + 0.5 * dtime
 | 
			
		||||
				* dtime * m_acceleration;
 | 
			
		||||
		m_velocity += dtime * m_acceleration;
 | 
			
		||||
	// Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
 | 
			
		||||
	// If the object gets detached this comes into effect automatically from the last known origin
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
 | 
			
		||||
		m_base_position = pos;
 | 
			
		||||
		m_velocity = v3f(0,0,0);
 | 
			
		||||
		m_acceleration = v3f(0,0,0);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if(m_prop.physical){
 | 
			
		||||
			core::aabbox3d<f32> box = m_prop.collisionbox;
 | 
			
		||||
			box.MinEdge *= BS;
 | 
			
		||||
			box.MaxEdge *= BS;
 | 
			
		||||
			collisionMoveResult moveresult;
 | 
			
		||||
			f32 pos_max_d = BS*0.25; // Distance per iteration
 | 
			
		||||
			f32 stepheight = 0; // Maximum climbable step height
 | 
			
		||||
			v3f p_pos = m_base_position;
 | 
			
		||||
			v3f p_velocity = m_velocity;
 | 
			
		||||
			v3f p_acceleration = m_acceleration;
 | 
			
		||||
			IGameDef *gamedef = m_env->getGameDef();
 | 
			
		||||
			moveresult = collisionMoveSimple(&m_env->getMap(), gamedef,
 | 
			
		||||
					pos_max_d, box, stepheight, dtime,
 | 
			
		||||
					p_pos, p_velocity, p_acceleration);
 | 
			
		||||
			// Apply results
 | 
			
		||||
			m_base_position = p_pos;
 | 
			
		||||
			m_velocity = p_velocity;
 | 
			
		||||
			m_acceleration = p_acceleration;
 | 
			
		||||
		} else {
 | 
			
		||||
			m_base_position += dtime * m_velocity + 0.5 * dtime
 | 
			
		||||
					* dtime * m_acceleration;
 | 
			
		||||
			m_velocity += dtime * m_acceleration;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_registered){
 | 
			
		||||
@@ -474,19 +510,22 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 | 
			
		||||
	if(send_recommended == false)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	// TODO: force send when acceleration changes enough?
 | 
			
		||||
	float minchange = 0.2*BS;
 | 
			
		||||
	if(m_last_sent_position_timer > 1.0){
 | 
			
		||||
		minchange = 0.01*BS;
 | 
			
		||||
	} else if(m_last_sent_position_timer > 0.2){
 | 
			
		||||
		minchange = 0.05*BS;
 | 
			
		||||
	}
 | 
			
		||||
	float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
 | 
			
		||||
	move_d += m_last_sent_move_precision;
 | 
			
		||||
	float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
 | 
			
		||||
	if(move_d > minchange || vel_d > minchange ||
 | 
			
		||||
			fabs(m_yaw - m_last_sent_yaw) > 1.0){
 | 
			
		||||
		sendPosition(true, false);
 | 
			
		||||
	if(!isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: force send when acceleration changes enough?
 | 
			
		||||
		float minchange = 0.2*BS;
 | 
			
		||||
		if(m_last_sent_position_timer > 1.0){
 | 
			
		||||
			minchange = 0.01*BS;
 | 
			
		||||
		} else if(m_last_sent_position_timer > 0.2){
 | 
			
		||||
			minchange = 0.05*BS;
 | 
			
		||||
		}
 | 
			
		||||
		float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
 | 
			
		||||
		move_d += m_last_sent_move_precision;
 | 
			
		||||
		float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
 | 
			
		||||
		if(move_d > minchange || vel_d > minchange ||
 | 
			
		||||
				fabs(m_yaw - m_last_sent_yaw) > 1.0){
 | 
			
		||||
			sendPosition(true, false);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_armor_groups_sent == false){
 | 
			
		||||
@@ -497,6 +536,32 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_animation_sent == false){
 | 
			
		||||
		m_animation_sent = true;
 | 
			
		||||
		std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
 | 
			
		||||
		// create message and add to list
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_bone_position_sent == false){
 | 
			
		||||
		m_bone_position_sent = true;
 | 
			
		||||
		for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
 | 
			
		||||
			std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
 | 
			
		||||
			// create message and add to list
 | 
			
		||||
			ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
			m_messages_out.push_back(aom);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_attachment_sent == false){
 | 
			
		||||
		m_attachment_sent = true;
 | 
			
		||||
		std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
 | 
			
		||||
		// create message and add to list
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string LuaEntitySAO::getClientInitializationData()
 | 
			
		||||
@@ -504,13 +569,21 @@ std::string LuaEntitySAO::getClientInitializationData()
 | 
			
		||||
	std::ostringstream os(std::ios::binary);
 | 
			
		||||
	writeU8(os, 0); // version
 | 
			
		||||
	os<<serializeString(""); // name
 | 
			
		||||
	writeS16(os, getId()); //id
 | 
			
		||||
	writeU8(os, 0); // is_player
 | 
			
		||||
	writeV3F1000(os, m_base_position);
 | 
			
		||||
	writeF1000(os, m_yaw);
 | 
			
		||||
	writeS16(os, m_hp);
 | 
			
		||||
	writeU8(os, 2); // number of messages stuffed in here
 | 
			
		||||
 | 
			
		||||
	writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
 | 
			
		||||
	os<<serializeLongString(getPropertyPacket()); // message 1
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
 | 
			
		||||
	for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
 | 
			
		||||
		os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
 | 
			
		||||
	}
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
 | 
			
		||||
 | 
			
		||||
	// return result
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
@@ -551,6 +624,10 @@ int LuaEntitySAO::punch(v3f dir,
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// It's best that attachments cannot be punched 
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return 0;
 | 
			
		||||
	
 | 
			
		||||
	ItemStack *punchitem = NULL;
 | 
			
		||||
	ItemStack punchitem_static;
 | 
			
		||||
	if(puncher){
 | 
			
		||||
@@ -594,18 +671,25 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
 | 
			
		||||
{
 | 
			
		||||
	if(!m_registered)
 | 
			
		||||
		return;
 | 
			
		||||
	// It's best that attachments cannot be clicked
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	lua_State *L = m_env->getLua();
 | 
			
		||||
	scriptapi_luaentity_rightclick(L, m_id, clicker);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::setPos(v3f pos)
 | 
			
		||||
{
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	m_base_position = pos;
 | 
			
		||||
	sendPosition(false, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::moveTo(v3f pos, bool continuous)
 | 
			
		||||
{
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	m_base_position = pos;
 | 
			
		||||
	if(!continuous)
 | 
			
		||||
		sendPosition(true, true);
 | 
			
		||||
@@ -644,6 +728,37 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
 | 
			
		||||
	m_armor_groups_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
 | 
			
		||||
{
 | 
			
		||||
	m_animation_range = frame_range;
 | 
			
		||||
	m_animation_speed = frame_speed;
 | 
			
		||||
	m_animation_blend = frame_blend;
 | 
			
		||||
	m_animation_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
 | 
			
		||||
	m_bone_position_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	// Attachments need to be handled on both the server and client.
 | 
			
		||||
	// If we just attach on the server, we can only copy the position of the parent. Attachments
 | 
			
		||||
	// are still sent to clients at an interval so players might see them lagging, plus we can't
 | 
			
		||||
	// read and attach to skeletal bones.
 | 
			
		||||
	// If we just attach on the client, the server still sees the child at its original location.
 | 
			
		||||
	// This breaks some things so we also give the server the most accurate representation
 | 
			
		||||
	// even if players only see the client changes.
 | 
			
		||||
 | 
			
		||||
	m_attachment_parent_id = parent_id;
 | 
			
		||||
	m_attachment_bone = bone;
 | 
			
		||||
	m_attachment_position = position;
 | 
			
		||||
	m_attachment_rotation = rotation;
 | 
			
		||||
	m_attachment_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjectProperties* LuaEntitySAO::accessObjectProperties()
 | 
			
		||||
{
 | 
			
		||||
	return &m_prop;
 | 
			
		||||
@@ -718,6 +833,10 @@ std::string LuaEntitySAO::getPropertyPacket()
 | 
			
		||||
 | 
			
		||||
void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
 | 
			
		||||
{
 | 
			
		||||
	// If the object is attached client-side, don't waste bandwidth sending its position to clients
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	
 | 
			
		||||
	m_last_sent_move_precision = m_base_position.getDistanceFrom(
 | 
			
		||||
			m_last_sent_position);
 | 
			
		||||
	m_last_sent_position_timer = 0;
 | 
			
		||||
@@ -765,8 +884,11 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
 | 
			
		||||
	m_properties_sent(true),
 | 
			
		||||
	m_privs(privs),
 | 
			
		||||
	m_is_singleplayer(is_singleplayer),
 | 
			
		||||
	m_animation_sent(false),
 | 
			
		||||
	m_bone_position_sent(false),
 | 
			
		||||
	m_attachment_sent(false),
 | 
			
		||||
	// public
 | 
			
		||||
	m_teleported(false),
 | 
			
		||||
	m_moved(false),
 | 
			
		||||
	m_inventory_not_sent(false),
 | 
			
		||||
	m_hp_not_sent(false),
 | 
			
		||||
	m_wielded_item_not_sent(false)
 | 
			
		||||
@@ -782,13 +904,17 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
 | 
			
		||||
	m_prop.physical = false;
 | 
			
		||||
	m_prop.weight = 75;
 | 
			
		||||
	m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
 | 
			
		||||
	// start of default appearance, this should be overwritten by LUA
 | 
			
		||||
	m_prop.visual = "upright_sprite";
 | 
			
		||||
	m_prop.visual_size = v2f(1, 2);
 | 
			
		||||
	m_prop.textures.clear();
 | 
			
		||||
	m_prop.textures.push_back("player.png");
 | 
			
		||||
	m_prop.textures.push_back("player_back.png");
 | 
			
		||||
	m_prop.colors.clear();
 | 
			
		||||
	m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
 | 
			
		||||
	m_prop.spritediv = v2s16(1,1);
 | 
			
		||||
	m_prop.is_visible = (getHP() != 0);
 | 
			
		||||
	// end of default appearance
 | 
			
		||||
	m_prop.is_visible = true;
 | 
			
		||||
	m_prop.makes_footstep_sound = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -842,12 +968,21 @@ std::string PlayerSAO::getClientInitializationData()
 | 
			
		||||
	writeU8(os, 0); // version
 | 
			
		||||
	os<<serializeString(m_player->getName()); // name
 | 
			
		||||
	writeU8(os, 1); // is_player
 | 
			
		||||
	writeS16(os, getId()); //id
 | 
			
		||||
	writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
 | 
			
		||||
	writeF1000(os, m_player->getYaw());
 | 
			
		||||
	writeS16(os, getHP());
 | 
			
		||||
	writeU8(os, 2); // number of messages stuffed in here
 | 
			
		||||
 | 
			
		||||
	writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
 | 
			
		||||
	os<<serializeLongString(getPropertyPacket()); // message 1
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
 | 
			
		||||
	for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
 | 
			
		||||
		os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
 | 
			
		||||
	}
 | 
			
		||||
	os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
 | 
			
		||||
 | 
			
		||||
	// return result
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -857,6 +992,17 @@ std::string PlayerSAO::getStaticData()
 | 
			
		||||
	return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PlayerSAO::isAttached()
 | 
			
		||||
{
 | 
			
		||||
	if(!m_attachment_parent_id)
 | 
			
		||||
		return false;
 | 
			
		||||
	// Check if the parent still exists
 | 
			
		||||
	ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
 | 
			
		||||
	if(obj)
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::step(float dtime, bool send_recommended)
 | 
			
		||||
{
 | 
			
		||||
	if(!m_properties_sent)
 | 
			
		||||
@@ -868,73 +1014,102 @@ void PlayerSAO::step(float dtime, bool send_recommended)
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If attached, check that our parent is still there. If it isn't, detach.
 | 
			
		||||
	if(m_attachment_parent_id && !isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		m_attachment_parent_id = 0;
 | 
			
		||||
		m_attachment_bone = "";
 | 
			
		||||
		m_attachment_position = v3f(0,0,0);
 | 
			
		||||
		m_attachment_rotation = v3f(0,0,0);
 | 
			
		||||
		m_player->setPosition(m_last_good_position);
 | 
			
		||||
		m_moved = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_time_from_last_punch += dtime;
 | 
			
		||||
	m_nocheat_dig_time += dtime;
 | 
			
		||||
 | 
			
		||||
	if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
 | 
			
		||||
	// Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
 | 
			
		||||
	// If the object gets detached this comes into effect automatically from the last known origin
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		m_last_good_position = m_player->getPosition();
 | 
			
		||||
		v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
 | 
			
		||||
		m_last_good_position = pos;
 | 
			
		||||
		m_last_good_position_age = 0;
 | 
			
		||||
		m_player->setPosition(pos);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		/*
 | 
			
		||||
			Check player movements
 | 
			
		||||
 | 
			
		||||
			NOTE: Actually the server should handle player physics like the
 | 
			
		||||
			client does and compare player's position to what is calculated
 | 
			
		||||
			on our side. This is required when eg. players fly due to an
 | 
			
		||||
			explosion. Altough a node-based alternative might be possible
 | 
			
		||||
			too, and much more lightweight.
 | 
			
		||||
		*/
 | 
			
		||||
 | 
			
		||||
		float player_max_speed = 0;
 | 
			
		||||
		float player_max_speed_up = 0;
 | 
			
		||||
		if(m_privs.count("fast") != 0){
 | 
			
		||||
			// Fast speed
 | 
			
		||||
			player_max_speed = BS * 20;
 | 
			
		||||
			player_max_speed_up = BS * 20;
 | 
			
		||||
		} else {
 | 
			
		||||
			// Normal speed
 | 
			
		||||
			player_max_speed = BS * 4.0;
 | 
			
		||||
			player_max_speed_up = BS * 4.0;
 | 
			
		||||
		}
 | 
			
		||||
		// Tolerance
 | 
			
		||||
		player_max_speed *= 2.5;
 | 
			
		||||
		player_max_speed_up *= 2.5;
 | 
			
		||||
 | 
			
		||||
		m_last_good_position_age += dtime;
 | 
			
		||||
		if(m_last_good_position_age >= 1.0){
 | 
			
		||||
			float age = m_last_good_position_age;
 | 
			
		||||
			v3f diff = (m_player->getPosition() - m_last_good_position);
 | 
			
		||||
			float d_vert = diff.Y;
 | 
			
		||||
			diff.Y = 0;
 | 
			
		||||
			float d_horiz = diff.getLength();
 | 
			
		||||
			/*infostream<<m_player->getName()<<"'s horizontal speed is "
 | 
			
		||||
					<<(d_horiz/age)<<std::endl;*/
 | 
			
		||||
			if(d_horiz <= age * player_max_speed &&
 | 
			
		||||
					(d_vert < 0 || d_vert < age * player_max_speed_up)){
 | 
			
		||||
				m_last_good_position = m_player->getPosition();
 | 
			
		||||
			} else {
 | 
			
		||||
				actionstream<<"Player "<<m_player->getName()
 | 
			
		||||
						<<" moved too fast; resetting position"
 | 
			
		||||
						<<std::endl;
 | 
			
		||||
				m_player->setPosition(m_last_good_position);
 | 
			
		||||
				m_teleported = true;
 | 
			
		||||
			}
 | 
			
		||||
		if(m_is_singleplayer || g_settings->getBool("disable_anticheat"))
 | 
			
		||||
		{
 | 
			
		||||
			m_last_good_position = m_player->getPosition();
 | 
			
		||||
			m_last_good_position_age = 0;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			/*
 | 
			
		||||
				Check player movements
 | 
			
		||||
 | 
			
		||||
				NOTE: Actually the server should handle player physics like the
 | 
			
		||||
				client does and compare player's position to what is calculated
 | 
			
		||||
				on our side. This is required when eg. players fly due to an
 | 
			
		||||
				explosion. Altough a node-based alternative might be possible
 | 
			
		||||
				too, and much more lightweight.
 | 
			
		||||
			*/
 | 
			
		||||
 | 
			
		||||
			float player_max_speed = 0;
 | 
			
		||||
			float player_max_speed_up = 0;
 | 
			
		||||
			if(m_privs.count("fast") != 0){
 | 
			
		||||
				// Fast speed
 | 
			
		||||
				player_max_speed = BS * 20;
 | 
			
		||||
				player_max_speed_up = BS * 20;
 | 
			
		||||
			} else {
 | 
			
		||||
				// Normal speed
 | 
			
		||||
				player_max_speed = BS * 4.0;
 | 
			
		||||
				player_max_speed_up = BS * 4.0;
 | 
			
		||||
			}
 | 
			
		||||
			// Tolerance
 | 
			
		||||
			player_max_speed *= 2.5;
 | 
			
		||||
			player_max_speed_up *= 2.5;
 | 
			
		||||
 | 
			
		||||
			m_last_good_position_age += dtime;
 | 
			
		||||
			if(m_last_good_position_age >= 1.0){
 | 
			
		||||
				float age = m_last_good_position_age;
 | 
			
		||||
				v3f diff = (m_player->getPosition() - m_last_good_position);
 | 
			
		||||
				float d_vert = diff.Y;
 | 
			
		||||
				diff.Y = 0;
 | 
			
		||||
				float d_horiz = diff.getLength();
 | 
			
		||||
				/*infostream<<m_player->getName()<<"'s horizontal speed is "
 | 
			
		||||
						<<(d_horiz/age)<<std::endl;*/
 | 
			
		||||
				if(d_horiz <= age * player_max_speed &&
 | 
			
		||||
						(d_vert < 0 || d_vert < age * player_max_speed_up)){
 | 
			
		||||
					m_last_good_position = m_player->getPosition();
 | 
			
		||||
				} else {
 | 
			
		||||
					actionstream<<"Player "<<m_player->getName()
 | 
			
		||||
							<<" moved too fast; resetting position"
 | 
			
		||||
							<<std::endl;
 | 
			
		||||
					m_player->setPosition(m_last_good_position);
 | 
			
		||||
					m_moved = true;
 | 
			
		||||
				}
 | 
			
		||||
				m_last_good_position_age = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(send_recommended == false)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if(m_position_not_sent)
 | 
			
		||||
	// If the object is attached client-side, don't waste bandwidth sending its position to clients
 | 
			
		||||
	if(m_position_not_sent && !isAttached())
 | 
			
		||||
	{
 | 
			
		||||
		m_position_not_sent = false;
 | 
			
		||||
		float update_interval = m_env->getSendRecommendedInterval();
 | 
			
		||||
		v3f pos;
 | 
			
		||||
		if(isAttached()) // Just in case we ever do send attachment position too
 | 
			
		||||
			pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
 | 
			
		||||
		else
 | 
			
		||||
			pos = m_player->getPosition() + v3f(0,BS*1,0);
 | 
			
		||||
		std::string str = gob_cmd_update_position(
 | 
			
		||||
			m_player->getPosition() + v3f(0,BS*1,0),
 | 
			
		||||
			pos,
 | 
			
		||||
			v3f(0,0,0),
 | 
			
		||||
			v3f(0,0,0),
 | 
			
		||||
			m_player->getYaw(),
 | 
			
		||||
@@ -961,32 +1136,63 @@ void PlayerSAO::step(float dtime, bool send_recommended)
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_animation_sent == false){
 | 
			
		||||
		m_animation_sent = true;
 | 
			
		||||
		std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
 | 
			
		||||
		// create message and add to list
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_bone_position_sent == false){
 | 
			
		||||
		m_bone_position_sent = true;
 | 
			
		||||
		for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
 | 
			
		||||
			std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
 | 
			
		||||
			// create message and add to list
 | 
			
		||||
			ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
			m_messages_out.push_back(aom);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(m_attachment_sent == false){
 | 
			
		||||
		m_attachment_sent = true;
 | 
			
		||||
		std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
 | 
			
		||||
		// create message and add to list
 | 
			
		||||
		ActiveObjectMessage aom(getId(), true, str);
 | 
			
		||||
		m_messages_out.push_back(aom);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::setBasePosition(const v3f &position)
 | 
			
		||||
{
 | 
			
		||||
	// This needs to be ran for attachments too
 | 
			
		||||
	ServerActiveObject::setBasePosition(position);
 | 
			
		||||
	m_position_not_sent = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::setPos(v3f pos)
 | 
			
		||||
{
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	m_player->setPosition(pos);
 | 
			
		||||
	// Movement caused by this command is always valid
 | 
			
		||||
	m_last_good_position = pos;
 | 
			
		||||
	m_last_good_position_age = 0;
 | 
			
		||||
	// Force position change on client
 | 
			
		||||
	m_teleported = true;
 | 
			
		||||
	m_moved = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::moveTo(v3f pos, bool continuous)
 | 
			
		||||
{
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return;
 | 
			
		||||
	m_player->setPosition(pos);
 | 
			
		||||
	// Movement caused by this command is always valid
 | 
			
		||||
	m_last_good_position = pos;
 | 
			
		||||
	m_last_good_position_age = 0;
 | 
			
		||||
	// Force position change on client
 | 
			
		||||
	m_teleported = true;
 | 
			
		||||
	m_moved = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int PlayerSAO::punch(v3f dir,
 | 
			
		||||
@@ -994,6 +1200,10 @@ int PlayerSAO::punch(v3f dir,
 | 
			
		||||
	ServerActiveObject *puncher,
 | 
			
		||||
	float time_from_last_punch)
 | 
			
		||||
{
 | 
			
		||||
	// It's best that attachments cannot be punched 
 | 
			
		||||
	if(isAttached())
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if(!toolcap)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
@@ -1075,6 +1285,39 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
 | 
			
		||||
	m_armor_groups_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
 | 
			
		||||
{
 | 
			
		||||
	// store these so they can be updated to clients
 | 
			
		||||
	m_animation_range = frame_range;
 | 
			
		||||
	m_animation_speed = frame_speed;
 | 
			
		||||
	m_animation_blend = frame_blend;
 | 
			
		||||
	m_animation_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	// store these so they can be updated to clients
 | 
			
		||||
	m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
 | 
			
		||||
	m_bone_position_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	// Attachments need to be handled on both the server and client.
 | 
			
		||||
	// If we just attach on the server, we can only copy the position of the parent. Attachments
 | 
			
		||||
	// are still sent to clients at an interval so players might see them lagging, plus we can't
 | 
			
		||||
	// read and attach to skeletal bones.
 | 
			
		||||
	// If we just attach on the client, the server still sees the child at its original location.
 | 
			
		||||
	// This breaks some things so we also give the server the most accurate representation
 | 
			
		||||
	// even if players only see the client changes.
 | 
			
		||||
 | 
			
		||||
	m_attachment_parent_id = parent_id;
 | 
			
		||||
	m_attachment_bone = bone;
 | 
			
		||||
	m_attachment_position = position;
 | 
			
		||||
	m_attachment_rotation = rotation;
 | 
			
		||||
	m_attachment_sent = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjectProperties* PlayerSAO::accessObjectProperties()
 | 
			
		||||
{
 | 
			
		||||
	return &m_prop;
 | 
			
		||||
@@ -1138,7 +1381,7 @@ void PlayerSAO::disconnected()
 | 
			
		||||
 | 
			
		||||
std::string PlayerSAO::getPropertyPacket()
 | 
			
		||||
{
 | 
			
		||||
	m_prop.is_visible = (getHP() != 0);
 | 
			
		||||
	m_prop.is_visible = (true);
 | 
			
		||||
	return gob_cmd_set_properties(m_prop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ public:
 | 
			
		||||
	virtual void addedToEnvironment(u32 dtime_s);
 | 
			
		||||
	static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
 | 
			
		||||
			const std::string &data);
 | 
			
		||||
	bool isAttached();
 | 
			
		||||
	void step(float dtime, bool send_recommended);
 | 
			
		||||
	std::string getClientInitializationData();
 | 
			
		||||
	std::string getStaticData();
 | 
			
		||||
@@ -61,6 +62,9 @@ public:
 | 
			
		||||
	void setHP(s16 hp);
 | 
			
		||||
	s16 getHP() const;
 | 
			
		||||
	void setArmorGroups(const ItemGroupList &armor_groups);
 | 
			
		||||
	void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
 | 
			
		||||
	void setBonePosition(std::string bone, v3f position, v3f rotation);
 | 
			
		||||
	void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
 | 
			
		||||
	ObjectProperties* accessObjectProperties();
 | 
			
		||||
	void notifyObjectPropertiesModified();
 | 
			
		||||
	/* LuaEntitySAO-specific */
 | 
			
		||||
@@ -96,6 +100,20 @@ private:
 | 
			
		||||
	float m_last_sent_position_timer;
 | 
			
		||||
	float m_last_sent_move_precision;
 | 
			
		||||
	bool m_armor_groups_sent;
 | 
			
		||||
	
 | 
			
		||||
	v2f m_animation_range;
 | 
			
		||||
	float m_animation_speed;
 | 
			
		||||
	float m_animation_blend;
 | 
			
		||||
	bool m_animation_sent;
 | 
			
		||||
 | 
			
		||||
	std::map<std::string, core::vector2d<v3f> > m_bone_position;
 | 
			
		||||
	bool m_bone_position_sent;
 | 
			
		||||
 | 
			
		||||
	int m_attachment_parent_id;
 | 
			
		||||
	std::string m_attachment_bone;
 | 
			
		||||
	v3f m_attachment_position;
 | 
			
		||||
	v3f m_attachment_rotation;
 | 
			
		||||
	bool m_attachment_sent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -124,6 +142,7 @@ public:
 | 
			
		||||
	bool unlimitedTransferDistance() const;
 | 
			
		||||
	std::string getClientInitializationData();
 | 
			
		||||
	std::string getStaticData();
 | 
			
		||||
	bool isAttached();
 | 
			
		||||
	void step(float dtime, bool send_recommended);
 | 
			
		||||
	void setBasePosition(const v3f &position);
 | 
			
		||||
	void setPos(v3f pos);
 | 
			
		||||
@@ -142,6 +161,9 @@ public:
 | 
			
		||||
	void setHP(s16 hp);
 | 
			
		||||
	
 | 
			
		||||
	void setArmorGroups(const ItemGroupList &armor_groups);
 | 
			
		||||
	void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
 | 
			
		||||
	void setBonePosition(std::string bone, v3f position, v3f rotation);
 | 
			
		||||
	void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
 | 
			
		||||
	ObjectProperties* accessObjectProperties();
 | 
			
		||||
	void notifyObjectPropertiesModified();
 | 
			
		||||
 | 
			
		||||
@@ -229,15 +251,32 @@ private:
 | 
			
		||||
	bool m_position_not_sent;
 | 
			
		||||
	ItemGroupList m_armor_groups;
 | 
			
		||||
	bool m_armor_groups_sent;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	bool m_properties_sent;
 | 
			
		||||
	struct ObjectProperties m_prop;
 | 
			
		||||
	// Cached privileges for enforcement
 | 
			
		||||
	std::set<std::string> m_privs;
 | 
			
		||||
	bool m_is_singleplayer;
 | 
			
		||||
 | 
			
		||||
	v2f m_animation_range;
 | 
			
		||||
	float m_animation_speed;
 | 
			
		||||
	float m_animation_blend;
 | 
			
		||||
	bool m_animation_sent;
 | 
			
		||||
 | 
			
		||||
	std::map<std::string, core::vector2d<v3f> > m_bone_position; // Stores position and rotation for each bone name
 | 
			
		||||
	bool m_bone_position_sent;
 | 
			
		||||
 | 
			
		||||
	int m_attachment_parent_id;
 | 
			
		||||
	std::string m_attachment_bone;
 | 
			
		||||
	v3f m_attachment_position;
 | 
			
		||||
	v3f m_attachment_rotation;
 | 
			
		||||
	bool m_attachment_sent;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	// Some flags used by Server
 | 
			
		||||
	bool m_teleported;
 | 
			
		||||
	bool m_moved;
 | 
			
		||||
	bool m_inventory_not_sent;
 | 
			
		||||
	bool m_hp_not_sent;
 | 
			
		||||
	bool m_wielded_item_not_sent;
 | 
			
		||||
 
 | 
			
		||||
@@ -2315,7 +2315,7 @@ void ClientEnvironment::removeActiveObject(u16 id)
 | 
			
		||||
				<<"id="<<id<<" not found"<<std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	obj->removeFromScene();
 | 
			
		||||
	obj->removeFromScene(true);
 | 
			
		||||
	delete obj;
 | 
			
		||||
	m_active_objects.remove(id);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -464,6 +464,8 @@ public:
 | 
			
		||||
	// Get event from queue. CEE_NONE is returned if queue is empty.
 | 
			
		||||
	ClientEnvEvent getClientEvent();
 | 
			
		||||
 | 
			
		||||
	std::vector<core::vector2d<int> > attachment_list; // X is child ID, Y is parent ID
 | 
			
		||||
	
 | 
			
		||||
private:
 | 
			
		||||
	ClientMap *m_map;
 | 
			
		||||
	scene::ISceneManager *m_smgr;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								src/game.cpp
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/game.cpp
									
									
									
									
									
								
							@@ -1881,6 +1881,8 @@ void the_game(
 | 
			
		||||
			bool a_jump,
 | 
			
		||||
			bool a_superspeed,
 | 
			
		||||
			bool a_sneak,
 | 
			
		||||
			bool a_LMB,
 | 
			
		||||
			bool a_RMB,
 | 
			
		||||
			float a_pitch,
 | 
			
		||||
			float a_yaw*/
 | 
			
		||||
			PlayerControl control(
 | 
			
		||||
@@ -1891,10 +1893,24 @@ void the_game(
 | 
			
		||||
				input->isKeyDown(getKeySetting("keymap_jump")),
 | 
			
		||||
				input->isKeyDown(getKeySetting("keymap_special1")),
 | 
			
		||||
				input->isKeyDown(getKeySetting("keymap_sneak")),
 | 
			
		||||
				input->getLeftState(),
 | 
			
		||||
				input->getRightState(),
 | 
			
		||||
				camera_pitch,
 | 
			
		||||
				camera_yaw
 | 
			
		||||
			);
 | 
			
		||||
			client.setPlayerControl(control);
 | 
			
		||||
			u32 keyPressed=
 | 
			
		||||
			1*(int)input->isKeyDown(getKeySetting("keymap_forward"))+
 | 
			
		||||
			2*(int)input->isKeyDown(getKeySetting("keymap_backward"))+
 | 
			
		||||
			4*(int)input->isKeyDown(getKeySetting("keymap_left"))+
 | 
			
		||||
			8*(int)input->isKeyDown(getKeySetting("keymap_right"))+
 | 
			
		||||
			16*(int)input->isKeyDown(getKeySetting("keymap_jump"))+
 | 
			
		||||
			32*(int)input->isKeyDown(getKeySetting("keymap_special1"))+
 | 
			
		||||
			64*(int)input->isKeyDown(getKeySetting("keymap_sneak"))+
 | 
			
		||||
			128*(int)input->getLeftState()+
 | 
			
		||||
			256*(int)input->getRightState();
 | 
			
		||||
			LocalPlayer* player = client.getEnv().getLocalPlayer();
 | 
			
		||||
			player->keyPressed=keyPressed;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		/*
 | 
			
		||||
 
 | 
			
		||||
@@ -92,6 +92,43 @@ std::string gob_cmd_set_sprite(
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream os(std::ios::binary);
 | 
			
		||||
	// command 
 | 
			
		||||
	writeU8(os, GENERIC_CMD_SET_ANIMATION);
 | 
			
		||||
	// parameters
 | 
			
		||||
	writeV2F1000(os, frames);
 | 
			
		||||
	writeF1000(os, frame_speed);
 | 
			
		||||
	writeF1000(os, frame_blend);
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream os(std::ios::binary);
 | 
			
		||||
	// command 
 | 
			
		||||
	writeU8(os, GENERIC_CMD_SET_BONE_POSITION);
 | 
			
		||||
	// parameters
 | 
			
		||||
	os<<serializeString(bone);
 | 
			
		||||
	writeV3F1000(os, position);
 | 
			
		||||
	writeV3F1000(os, rotation);
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream os(std::ios::binary);
 | 
			
		||||
	// command 
 | 
			
		||||
	writeU8(os, GENERIC_CMD_SET_ATTACHMENT);
 | 
			
		||||
	// parameters
 | 
			
		||||
	writeS16(os, parent_id);
 | 
			
		||||
	os<<serializeString(bone);
 | 
			
		||||
	writeV3F1000(os, position);
 | 
			
		||||
	writeV3F1000(os, rotation);
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_punched(s16 damage, s16 result_hp)
 | 
			
		||||
{
 | 
			
		||||
	std::ostringstream os(std::ios::binary);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
#define GENERIC_CMD_UPDATE_POSITION 1
 | 
			
		||||
#define GENERIC_CMD_SET_TEXTURE_MOD 2
 | 
			
		||||
#define GENERIC_CMD_SET_SPRITE 3
 | 
			
		||||
#define GENERIC_CMD_PUNCHED 4
 | 
			
		||||
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5
 | 
			
		||||
#define GENERIC_CMD_SET_ANIMATION 4
 | 
			
		||||
#define GENERIC_CMD_SET_BONE_POSITION 5
 | 
			
		||||
#define GENERIC_CMD_SET_ATTACHMENT 6
 | 
			
		||||
#define GENERIC_CMD_PUNCHED 7
 | 
			
		||||
#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 8
 | 
			
		||||
 | 
			
		||||
#include "object_properties.h"
 | 
			
		||||
std::string gob_cmd_set_properties(const ObjectProperties &prop);
 | 
			
		||||
@@ -54,6 +57,12 @@ std::string gob_cmd_set_sprite(
 | 
			
		||||
	bool select_horiz_by_yawpitch
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend);
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation);
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation);
 | 
			
		||||
 | 
			
		||||
std::string gob_cmd_punched(s16 damage, s16 result_hp);
 | 
			
		||||
 | 
			
		||||
#include "itemgroup.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 | 
			
		||||
	
 | 
			
		||||
	m_inventorylists.clear();
 | 
			
		||||
	m_images.clear();
 | 
			
		||||
	m_backgrounds.clear();
 | 
			
		||||
	m_fields.clear();
 | 
			
		||||
 | 
			
		||||
	Strfnd f(m_formspec_string);
 | 
			
		||||
@@ -278,9 +279,26 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 | 
			
		||||
					<<", geom=("<<geom.X<<","<<geom.Y<<")"
 | 
			
		||||
					<<std::endl;
 | 
			
		||||
			if(bp_set != 2)
 | 
			
		||||
				errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
 | 
			
		||||
				errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
 | 
			
		||||
			m_images.push_back(ImageDrawSpec(name, pos, geom));
 | 
			
		||||
		}
 | 
			
		||||
		else if(type == "background")
 | 
			
		||||
		{
 | 
			
		||||
			v2s32 pos = basepos;
 | 
			
		||||
			pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
 | 
			
		||||
			pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
 | 
			
		||||
			v2s32 geom;
 | 
			
		||||
			geom.X = stof(f.next(",")) * (float)spacing.X;
 | 
			
		||||
			geom.Y = stof(f.next(";")) * (float)spacing.Y;
 | 
			
		||||
			std::string name = f.next("]");
 | 
			
		||||
			infostream<<"image name="<<name
 | 
			
		||||
					<<", pos=("<<pos.X<<","<<pos.Y<<")"
 | 
			
		||||
					<<", geom=("<<geom.X<<","<<geom.Y<<")"
 | 
			
		||||
					<<std::endl;
 | 
			
		||||
			if(bp_set != 2)
 | 
			
		||||
				errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
 | 
			
		||||
			m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
 | 
			
		||||
		}
 | 
			
		||||
		else if(type == "field")
 | 
			
		||||
		{
 | 
			
		||||
			std::string fname = f.next(";");
 | 
			
		||||
@@ -458,6 +476,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 | 
			
		||||
			
 | 
			
		||||
			video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
 | 
			
		||||
			gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
 | 
			
		||||
			e->setUseAlphaChannel(true);
 | 
			
		||||
			e->setImage(texture);
 | 
			
		||||
			e->setPressedImage(texture);
 | 
			
		||||
			e->setScaleImage(true);
 | 
			
		||||
@@ -691,6 +710,26 @@ void GUIFormSpecMenu::drawMenu()
 | 
			
		||||
 | 
			
		||||
	m_tooltip_element->setVisible(false);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		Draw backgrounds
 | 
			
		||||
	*/
 | 
			
		||||
	for(u32 i=0; i<m_backgrounds.size(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		const ImageDrawSpec &spec = m_backgrounds[i];
 | 
			
		||||
		video::ITexture *texture =
 | 
			
		||||
				m_gamedef->tsrc()->getTextureRaw(spec.name);
 | 
			
		||||
		// Image size on screen
 | 
			
		||||
		core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
 | 
			
		||||
		// Image rectangle on screen
 | 
			
		||||
		core::rect<s32> rect = imgrect + spec.pos;
 | 
			
		||||
		const video::SColor color(255,255,255,255);
 | 
			
		||||
		const video::SColor colors[] = {color,color,color,color};
 | 
			
		||||
		driver->draw2DImage(texture, rect,
 | 
			
		||||
			core::rect<s32>(core::position2d<s32>(0,0),
 | 
			
		||||
					core::dimension2di(texture->getOriginalSize())),
 | 
			
		||||
			NULL/*&AbsoluteClippingRect*/, colors, true);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/*
 | 
			
		||||
		Draw images
 | 
			
		||||
	*/
 | 
			
		||||
@@ -715,8 +754,11 @@ void GUIFormSpecMenu::drawMenu()
 | 
			
		||||
		Draw items
 | 
			
		||||
		Phase 0: Item slot rectangles
 | 
			
		||||
		Phase 1: Item images; prepare tooltip
 | 
			
		||||
		If backgrounds used, do not draw Item slot rectangles
 | 
			
		||||
	*/
 | 
			
		||||
	for(int phase=0; phase<=1; phase++)
 | 
			
		||||
	int start_phase=0;
 | 
			
		||||
	if (m_backgrounds.size() > 0) start_phase=1;
 | 
			
		||||
	for(int phase=start_phase; phase<=1; phase++)
 | 
			
		||||
	for(u32 i=0; i<m_inventorylists.size(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		drawList(m_inventorylists[i], phase);
 | 
			
		||||
 
 | 
			
		||||
@@ -206,6 +206,7 @@ protected:
 | 
			
		||||
	TextDest *m_text_dst;
 | 
			
		||||
 | 
			
		||||
	core::array<ListDrawSpec> m_inventorylists;
 | 
			
		||||
	core::array<ImageDrawSpec> m_backgrounds;	
 | 
			
		||||
	core::array<ImageDrawSpec> m_images;
 | 
			
		||||
	core::array<FieldSpec> m_fields;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
 | 
			
		||||
LocalPlayer::LocalPlayer(IGameDef *gamedef):
 | 
			
		||||
	Player(gamedef),
 | 
			
		||||
	isAttached(false),
 | 
			
		||||
	overridePosition(v3f(0,0,0)),
 | 
			
		||||
	m_sneak_node(32767,32767,32767),
 | 
			
		||||
	m_sneak_node_exists(false),
 | 
			
		||||
	m_old_node_below(32767,32767,32767),
 | 
			
		||||
@@ -59,6 +61,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 | 
			
		||||
 | 
			
		||||
	v3f old_speed = m_speed;
 | 
			
		||||
 | 
			
		||||
	// Copy parent position if local player is attached
 | 
			
		||||
	if(isAttached)
 | 
			
		||||
	{
 | 
			
		||||
		setPosition(overridePosition);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip collision detection if a special movement mode is used
 | 
			
		||||
	bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
 | 
			
		||||
	bool free_move = fly_allowed && g_settings->getBool("free_move");
 | 
			
		||||
@@ -353,6 +362,13 @@ void LocalPlayer::applyControl(float dtime)
 | 
			
		||||
	setPitch(control.pitch);
 | 
			
		||||
	setYaw(control.yaw);
 | 
			
		||||
 | 
			
		||||
	// Nullify speed and don't run positioning code if the player is attached
 | 
			
		||||
	if(isAttached)
 | 
			
		||||
	{
 | 
			
		||||
		setSpeed(v3f(0,0,0));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v3f move_direction = v3f(0,0,1);
 | 
			
		||||
	move_direction.rotateXZBy(getYaw());
 | 
			
		||||
	
 | 
			
		||||
 
 | 
			
		||||
@@ -22,53 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
 | 
			
		||||
#include "player.h"
 | 
			
		||||
 | 
			
		||||
struct PlayerControl
 | 
			
		||||
{
 | 
			
		||||
	PlayerControl()
 | 
			
		||||
	{
 | 
			
		||||
		up = false;
 | 
			
		||||
		down = false;
 | 
			
		||||
		left = false;
 | 
			
		||||
		right = false;
 | 
			
		||||
		jump = false;
 | 
			
		||||
		aux1 = false;
 | 
			
		||||
		sneak = false;
 | 
			
		||||
		pitch = 0;
 | 
			
		||||
		yaw = 0;
 | 
			
		||||
	}
 | 
			
		||||
	PlayerControl(
 | 
			
		||||
		bool a_up,
 | 
			
		||||
		bool a_down,
 | 
			
		||||
		bool a_left,
 | 
			
		||||
		bool a_right,
 | 
			
		||||
		bool a_jump,
 | 
			
		||||
		bool a_aux1,
 | 
			
		||||
		bool a_sneak,
 | 
			
		||||
		float a_pitch,
 | 
			
		||||
		float a_yaw
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		up = a_up;
 | 
			
		||||
		down = a_down;
 | 
			
		||||
		left = a_left;
 | 
			
		||||
		right = a_right;
 | 
			
		||||
		jump = a_jump;
 | 
			
		||||
		aux1 = a_aux1;
 | 
			
		||||
		sneak = a_sneak;
 | 
			
		||||
		pitch = a_pitch;
 | 
			
		||||
		yaw = a_yaw;
 | 
			
		||||
	}
 | 
			
		||||
	bool up;
 | 
			
		||||
	bool down;
 | 
			
		||||
	bool left;
 | 
			
		||||
	bool right;
 | 
			
		||||
	bool jump;
 | 
			
		||||
	bool aux1;
 | 
			
		||||
	bool sneak;
 | 
			
		||||
	float pitch;
 | 
			
		||||
	float yaw;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LocalPlayer : public Player
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
@@ -80,6 +33,10 @@ public:
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool isAttached;
 | 
			
		||||
 | 
			
		||||
	v3f overridePosition;
 | 
			
		||||
	
 | 
			
		||||
	void move(f32 dtime, Map &map, f32 pos_max_d,
 | 
			
		||||
			core::list<CollisionInfo> *collision_info);
 | 
			
		||||
	void move(f32 dtime, Map &map, f32 pos_max_d);
 | 
			
		||||
@@ -87,9 +44,6 @@ public:
 | 
			
		||||
	void applyControl(float dtime);
 | 
			
		||||
 | 
			
		||||
	v3s16 getStandingNodePos();
 | 
			
		||||
	
 | 
			
		||||
	PlayerControl control;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// This is used for determining the sneaking range
 | 
			
		||||
	v3s16 m_sneak_node;
 | 
			
		||||
 
 | 
			
		||||
@@ -132,7 +132,8 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
 | 
			
		||||
	for(s16 ii=0; ii<trunk_h; ii++)
 | 
			
		||||
	{
 | 
			
		||||
		if(vmanip.m_area.contains(p1))
 | 
			
		||||
			vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
 | 
			
		||||
			if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
 | 
			
		||||
				vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
 | 
			
		||||
		p1.Y++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -433,9 +433,6 @@ video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create render target texture
 | 
			
		||||
	video::ITexture *oldtexture = driver->findTexture(texture_name.c_str());
 | 
			
		||||
	if(oldtexture)
 | 
			
		||||
		driver->removeTexture(oldtexture);
 | 
			
		||||
	video::ITexture *rtt = driver->addRenderTargetTexture(
 | 
			
		||||
			dim, texture_name.c_str(), video::ECF_A8R8G8B8);
 | 
			
		||||
	if(rtt == NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -217,7 +217,7 @@ void ContentFeatures::reset()
 | 
			
		||||
 | 
			
		||||
void ContentFeatures::serialize(std::ostream &os)
 | 
			
		||||
{
 | 
			
		||||
	writeU8(os, 5); // version
 | 
			
		||||
	writeU8(os, 6); // version
 | 
			
		||||
	os<<serializeString(name);
 | 
			
		||||
	writeU16(os, groups.size());
 | 
			
		||||
	for(ItemGroupList::const_iterator
 | 
			
		||||
@@ -254,6 +254,7 @@ void ContentFeatures::serialize(std::ostream &os)
 | 
			
		||||
	os<<serializeString(liquid_alternative_flowing);
 | 
			
		||||
	os<<serializeString(liquid_alternative_source);
 | 
			
		||||
	writeU8(os, liquid_viscosity);
 | 
			
		||||
	writeU8(os, liquid_renewable);
 | 
			
		||||
	writeU8(os, light_source);
 | 
			
		||||
	writeU32(os, damage_per_second);
 | 
			
		||||
	node_box.serialize(os);
 | 
			
		||||
@@ -265,13 +266,12 @@ void ContentFeatures::serialize(std::ostream &os)
 | 
			
		||||
	serializeSimpleSoundSpec(sound_dug, os);
 | 
			
		||||
	// Stuff below should be moved to correct place in a version that otherwise changes
 | 
			
		||||
	// the protocol version
 | 
			
		||||
	writeU8(os, liquid_renewable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ContentFeatures::deSerialize(std::istream &is)
 | 
			
		||||
{
 | 
			
		||||
	int version = readU8(is);
 | 
			
		||||
	if(version != 5)
 | 
			
		||||
	if(version != 6)
 | 
			
		||||
		throw SerializationError("unsupported ContentFeatures version");
 | 
			
		||||
	name = deSerializeString(is);
 | 
			
		||||
	groups.clear();
 | 
			
		||||
@@ -311,6 +311,7 @@ void ContentFeatures::deSerialize(std::istream &is)
 | 
			
		||||
	liquid_alternative_flowing = deSerializeString(is);
 | 
			
		||||
	liquid_alternative_source = deSerializeString(is);
 | 
			
		||||
	liquid_viscosity = readU8(is);
 | 
			
		||||
	liquid_renewable = readU8(is);
 | 
			
		||||
	light_source = readU8(is);
 | 
			
		||||
	damage_per_second = readU32(is);
 | 
			
		||||
	node_box.deSerialize(is);
 | 
			
		||||
@@ -325,7 +326,6 @@ void ContentFeatures::deSerialize(std::istream &is)
 | 
			
		||||
	try{
 | 
			
		||||
		// Stuff below should be moved to correct place in a version that
 | 
			
		||||
		// otherwise changes the protocol version
 | 
			
		||||
		liquid_renewable = readU8(is);
 | 
			
		||||
	}catch(SerializationError &e) {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "object_properties.h"
 | 
			
		||||
#include "irrlichttypes_bloated.h"
 | 
			
		||||
#include "util/serialize.h"
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 | 
			
		||||
#define PP2(x) "("<<(x).X<<","<<(x).Y<<")"
 | 
			
		||||
@@ -30,6 +32,7 @@ ObjectProperties::ObjectProperties():
 | 
			
		||||
	weight(5),
 | 
			
		||||
	collisionbox(-0.5,-0.5,-0.5, 0.5,0.5,0.5),
 | 
			
		||||
	visual("sprite"),
 | 
			
		||||
	mesh(""),
 | 
			
		||||
	visual_size(1,1),
 | 
			
		||||
	spritediv(1,1),
 | 
			
		||||
	initial_sprite_basepos(0,0),
 | 
			
		||||
@@ -38,6 +41,7 @@ ObjectProperties::ObjectProperties():
 | 
			
		||||
	automatic_rotate(0)
 | 
			
		||||
{
 | 
			
		||||
	textures.push_back("unknown_object.png");
 | 
			
		||||
	colors.push_back(video::SColor(255,255,255,255));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ObjectProperties::dump()
 | 
			
		||||
@@ -48,12 +52,18 @@ std::string ObjectProperties::dump()
 | 
			
		||||
	os<<", weight="<<weight;
 | 
			
		||||
	os<<", collisionbox="<<PP(collisionbox.MinEdge)<<","<<PP(collisionbox.MaxEdge);
 | 
			
		||||
	os<<", visual="<<visual;
 | 
			
		||||
	os<<", mesh="<<mesh;
 | 
			
		||||
	os<<", visual_size="<<PP2(visual_size);
 | 
			
		||||
	os<<", textures=[";
 | 
			
		||||
	for(u32 i=0; i<textures.size(); i++){
 | 
			
		||||
		os<<"\""<<textures[i]<<"\" ";
 | 
			
		||||
	}
 | 
			
		||||
	os<<"]";
 | 
			
		||||
	os<<", colors=[";
 | 
			
		||||
	for(u32 i=0; i<colors.size(); i++){
 | 
			
		||||
		os<<"\""<<colors[i].getAlpha()<<","<<colors[i].getRed()<<","<<colors[i].getGreen()<<","<<colors[i].getBlue()<<"\" ";
 | 
			
		||||
	}
 | 
			
		||||
	os<<"]";
 | 
			
		||||
	os<<", spritediv="<<PP2(spritediv);
 | 
			
		||||
	os<<", initial_sprite_basepos="<<PP2(initial_sprite_basepos);
 | 
			
		||||
	os<<", is_visible="<<is_visible;
 | 
			
		||||
@@ -71,11 +81,16 @@ void ObjectProperties::serialize(std::ostream &os) const
 | 
			
		||||
	writeV3F1000(os, collisionbox.MinEdge);
 | 
			
		||||
	writeV3F1000(os, collisionbox.MaxEdge);
 | 
			
		||||
	os<<serializeString(visual);
 | 
			
		||||
	os<<serializeString(mesh);
 | 
			
		||||
	writeV2F1000(os, visual_size);
 | 
			
		||||
	writeU16(os, textures.size());
 | 
			
		||||
	for(u32 i=0; i<textures.size(); i++){
 | 
			
		||||
		os<<serializeString(textures[i]);
 | 
			
		||||
	}
 | 
			
		||||
	writeU16(os, colors.size());
 | 
			
		||||
	for(u32 i=0; i<colors.size(); i++){
 | 
			
		||||
		writeARGB8(os, colors[i]);
 | 
			
		||||
	}
 | 
			
		||||
	writeV2S16(os, spritediv);
 | 
			
		||||
	writeV2S16(os, initial_sprite_basepos);
 | 
			
		||||
	writeU8(os, is_visible);
 | 
			
		||||
@@ -94,12 +109,17 @@ void ObjectProperties::deSerialize(std::istream &is)
 | 
			
		||||
	collisionbox.MinEdge = readV3F1000(is);
 | 
			
		||||
	collisionbox.MaxEdge = readV3F1000(is);
 | 
			
		||||
	visual = deSerializeString(is);
 | 
			
		||||
	mesh = deSerializeString(is);
 | 
			
		||||
	visual_size = readV2F1000(is);
 | 
			
		||||
	textures.clear();
 | 
			
		||||
	u32 texture_count = readU16(is);
 | 
			
		||||
	for(u32 i=0; i<texture_count; i++){
 | 
			
		||||
		textures.push_back(deSerializeString(is));
 | 
			
		||||
	}
 | 
			
		||||
	u32 color_count = readU16(is);
 | 
			
		||||
	for(u32 i=0; i<color_count; i++){
 | 
			
		||||
		colors.push_back(readARGB8(is));
 | 
			
		||||
	}
 | 
			
		||||
	spritediv = readV2S16(is);
 | 
			
		||||
	initial_sprite_basepos = readV2S16(is);
 | 
			
		||||
	is_visible = readU8(is);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "irrlichttypes_bloated.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
struct ObjectProperties
 | 
			
		||||
{
 | 
			
		||||
@@ -32,14 +33,17 @@ struct ObjectProperties
 | 
			
		||||
	float weight;
 | 
			
		||||
	core::aabbox3d<f32> collisionbox;
 | 
			
		||||
	std::string visual;
 | 
			
		||||
	std::string mesh;
 | 
			
		||||
	v2f visual_size;
 | 
			
		||||
	core::array<std::string> textures;
 | 
			
		||||
	core::array<video::SColor> colors;
 | 
			
		||||
	v2s16 spritediv;
 | 
			
		||||
	v2s16 initial_sprite_basepos;
 | 
			
		||||
	bool is_visible;
 | 
			
		||||
	bool makes_footstep_sound;
 | 
			
		||||
	float automatic_rotate;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	ObjectProperties();
 | 
			
		||||
	std::string dump();
 | 
			
		||||
	void serialize(std::ostream &os) const;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								src/player.h
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								src/player.h
									
									
									
									
									
								
							@@ -28,6 +28,61 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
 | 
			
		||||
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
 | 
			
		||||
 | 
			
		||||
struct PlayerControl
 | 
			
		||||
{
 | 
			
		||||
	PlayerControl()
 | 
			
		||||
	{
 | 
			
		||||
		up = false;
 | 
			
		||||
		down = false;
 | 
			
		||||
		left = false;
 | 
			
		||||
		right = false;
 | 
			
		||||
		jump = false;
 | 
			
		||||
		aux1 = false;
 | 
			
		||||
		sneak = false;
 | 
			
		||||
		LMB = false;
 | 
			
		||||
		RMB = false;
 | 
			
		||||
		pitch = 0;
 | 
			
		||||
		yaw = 0;
 | 
			
		||||
	}
 | 
			
		||||
	PlayerControl(
 | 
			
		||||
		bool a_up,
 | 
			
		||||
		bool a_down,
 | 
			
		||||
		bool a_left,
 | 
			
		||||
		bool a_right,
 | 
			
		||||
		bool a_jump,
 | 
			
		||||
		bool a_aux1,
 | 
			
		||||
		bool a_sneak,
 | 
			
		||||
		bool a_LMB,
 | 
			
		||||
		bool a_RMB,
 | 
			
		||||
		float a_pitch,
 | 
			
		||||
		float a_yaw
 | 
			
		||||
	)
 | 
			
		||||
	{
 | 
			
		||||
		up = a_up;
 | 
			
		||||
		down = a_down;
 | 
			
		||||
		left = a_left;
 | 
			
		||||
		right = a_right;
 | 
			
		||||
		jump = a_jump;
 | 
			
		||||
		aux1 = a_aux1;
 | 
			
		||||
		sneak = a_sneak;
 | 
			
		||||
		LMB = a_LMB;
 | 
			
		||||
		RMB = a_RMB;
 | 
			
		||||
		pitch = a_pitch;
 | 
			
		||||
		yaw = a_yaw;
 | 
			
		||||
	}
 | 
			
		||||
	bool up;
 | 
			
		||||
	bool down;
 | 
			
		||||
	bool left;
 | 
			
		||||
	bool right;
 | 
			
		||||
	bool jump;
 | 
			
		||||
	bool aux1;
 | 
			
		||||
	bool sneak;
 | 
			
		||||
	bool LMB;
 | 
			
		||||
	bool RMB;
 | 
			
		||||
	float pitch;
 | 
			
		||||
	float yaw;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Map;
 | 
			
		||||
class IGameDef;
 | 
			
		||||
struct CollisionInfo;
 | 
			
		||||
@@ -158,6 +213,14 @@ public:
 | 
			
		||||
	
 | 
			
		||||
	std::string inventory_formspec;
 | 
			
		||||
	
 | 
			
		||||
	PlayerControl control;
 | 
			
		||||
	PlayerControl getPlayerControl()
 | 
			
		||||
	{
 | 
			
		||||
		return control;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	u32 keyPressed;
 | 
			
		||||
	
 | 
			
		||||
protected:
 | 
			
		||||
	IGameDef *m_gamedef;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -937,6 +937,8 @@ static void read_object_properties(lua_State *L, int index,
 | 
			
		||||
 | 
			
		||||
	getstringfield(L, -1, "visual", prop->visual);
 | 
			
		||||
 | 
			
		||||
	getstringfield(L, -1, "mesh", prop->mesh);
 | 
			
		||||
	
 | 
			
		||||
	lua_getfield(L, -1, "visual_size");
 | 
			
		||||
	if(lua_istable(L, -1))
 | 
			
		||||
		prop->visual_size = read_v2f(L, -1);
 | 
			
		||||
@@ -959,6 +961,23 @@ static void read_object_properties(lua_State *L, int index,
 | 
			
		||||
	}
 | 
			
		||||
	lua_pop(L, 1);
 | 
			
		||||
 | 
			
		||||
	lua_getfield(L, -1, "colors");
 | 
			
		||||
	if(lua_istable(L, -1)){
 | 
			
		||||
		prop->colors.clear();
 | 
			
		||||
		int table = lua_gettop(L);
 | 
			
		||||
		lua_pushnil(L);
 | 
			
		||||
		while(lua_next(L, table) != 0){
 | 
			
		||||
			// key at index -2 and value at index -1
 | 
			
		||||
			if(lua_isstring(L, -1))
 | 
			
		||||
				prop->colors.push_back(readARGB8(L, -1));
 | 
			
		||||
			else
 | 
			
		||||
				prop->colors.push_back(video::SColor(255, 255, 255, 255));
 | 
			
		||||
			// removes value, keeps key for next iteration
 | 
			
		||||
			lua_pop(L, 1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	lua_pop(L, 1);
 | 
			
		||||
	
 | 
			
		||||
	lua_getfield(L, -1, "spritediv");
 | 
			
		||||
	if(lua_istable(L, -1))
 | 
			
		||||
		prop->spritediv = read_v2s16(L, -1);
 | 
			
		||||
@@ -2697,6 +2716,80 @@ private:
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set_animation(self, frame_range, frame_speed, frame_blend)
 | 
			
		||||
	static int l_set_animation(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		ServerActiveObject *co = getobject(ref);
 | 
			
		||||
		if(co == NULL) return 0;
 | 
			
		||||
		// Do it
 | 
			
		||||
		v2f frames = v2f(1, 1);
 | 
			
		||||
		if(!lua_isnil(L, 2))
 | 
			
		||||
			frames = read_v2f(L, 2);
 | 
			
		||||
		float frame_speed = 15;
 | 
			
		||||
		if(!lua_isnil(L, 3))
 | 
			
		||||
			frame_speed = lua_tonumber(L, 3);
 | 
			
		||||
		float frame_blend = 0;
 | 
			
		||||
		if(!lua_isnil(L, 4))
 | 
			
		||||
			frame_blend = lua_tonumber(L, 4);
 | 
			
		||||
		co->setAnimation(frames, frame_speed, frame_blend);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set_bone_position(self, std::string bone, v3f position, v3f rotation)
 | 
			
		||||
	static int l_set_bone_position(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		ServerActiveObject *co = getobject(ref);
 | 
			
		||||
		if(co == NULL) return 0;
 | 
			
		||||
		// Do it
 | 
			
		||||
		std::string bone = "";
 | 
			
		||||
		if(!lua_isnil(L, 2))
 | 
			
		||||
			bone = lua_tostring(L, 2);
 | 
			
		||||
		v3f position = v3f(0, 0, 0);
 | 
			
		||||
		if(!lua_isnil(L, 3))
 | 
			
		||||
			position = read_v3f(L, 3);
 | 
			
		||||
		v3f rotation = v3f(0, 0, 0);
 | 
			
		||||
		if(!lua_isnil(L, 4))
 | 
			
		||||
			rotation = read_v3f(L, 4);
 | 
			
		||||
		co->setBonePosition(bone, position, rotation);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set_attach(self, parent, bone, position, rotation)
 | 
			
		||||
	static int l_set_attach(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		ObjectRef *parent_ref = checkobject(L, 2);
 | 
			
		||||
		ServerActiveObject *co = getobject(ref);
 | 
			
		||||
		ServerActiveObject *parent = getobject(parent_ref);
 | 
			
		||||
		if(co == NULL) return 0;
 | 
			
		||||
		if(parent == NULL) return 0;
 | 
			
		||||
		// Do it
 | 
			
		||||
		std::string bone = "";
 | 
			
		||||
		if(!lua_isnil(L, 3))
 | 
			
		||||
			bone = lua_tostring(L, 3);
 | 
			
		||||
		v3f position = v3f(0, 0, 0);
 | 
			
		||||
		if(!lua_isnil(L, 4))
 | 
			
		||||
			position = read_v3f(L, 4);
 | 
			
		||||
		v3f rotation = v3f(0, 0, 0);
 | 
			
		||||
		if(!lua_isnil(L, 5))
 | 
			
		||||
			rotation = read_v3f(L, 5);
 | 
			
		||||
		co->setAttachment(parent->getId(), bone, position, rotation);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set_detach(self)
 | 
			
		||||
	static int l_set_detach(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		ServerActiveObject *co = getobject(ref);
 | 
			
		||||
		if(co == NULL) return 0;
 | 
			
		||||
		// Do it
 | 
			
		||||
		co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set_properties(self, properties)
 | 
			
		||||
	static int l_set_properties(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
@@ -2933,6 +3026,53 @@ private:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// get_player_control(self)
 | 
			
		||||
	static int l_get_player_control(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		Player *player = getplayer(ref);
 | 
			
		||||
		if(player == NULL){
 | 
			
		||||
			lua_pushlstring(L, "", 0);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		// Do it
 | 
			
		||||
		PlayerControl control = player->getPlayerControl();
 | 
			
		||||
		lua_newtable(L);
 | 
			
		||||
		lua_pushboolean(L, control.up);
 | 
			
		||||
		lua_setfield(L, -2, "up");
 | 
			
		||||
		lua_pushboolean(L, control.down);
 | 
			
		||||
		lua_setfield(L, -2, "down");
 | 
			
		||||
		lua_pushboolean(L, control.left);
 | 
			
		||||
		lua_setfield(L, -2, "left");
 | 
			
		||||
		lua_pushboolean(L, control.right);
 | 
			
		||||
		lua_setfield(L, -2, "right");
 | 
			
		||||
		lua_pushboolean(L, control.jump);
 | 
			
		||||
		lua_setfield(L, -2, "jump");
 | 
			
		||||
		lua_pushboolean(L, control.aux1);
 | 
			
		||||
		lua_setfield(L, -2, "aux1");
 | 
			
		||||
		lua_pushboolean(L, control.sneak);
 | 
			
		||||
		lua_setfield(L, -2, "sneak");
 | 
			
		||||
		lua_pushboolean(L, control.LMB);
 | 
			
		||||
		lua_setfield(L, -2, "LMB");
 | 
			
		||||
		lua_pushboolean(L, control.RMB);
 | 
			
		||||
		lua_setfield(L, -2, "RMB");
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// get_player_control_bits(self)
 | 
			
		||||
	static int l_get_player_control_bits(lua_State *L)
 | 
			
		||||
	{
 | 
			
		||||
		ObjectRef *ref = checkobject(L, 1);
 | 
			
		||||
		Player *player = getplayer(ref);
 | 
			
		||||
		if(player == NULL){
 | 
			
		||||
			lua_pushlstring(L, "", 0);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		// Do it	
 | 
			
		||||
		lua_pushnumber(L, player->keyPressed);
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
public:
 | 
			
		||||
	ObjectRef(ServerActiveObject *object):
 | 
			
		||||
		m_object(object)
 | 
			
		||||
@@ -3011,6 +3151,10 @@ const luaL_reg ObjectRef::methods[] = {
 | 
			
		||||
	method(ObjectRef, get_wielded_item),
 | 
			
		||||
	method(ObjectRef, set_wielded_item),
 | 
			
		||||
	method(ObjectRef, set_armor_groups),
 | 
			
		||||
	method(ObjectRef, set_animation),
 | 
			
		||||
	method(ObjectRef, set_bone_position),
 | 
			
		||||
	method(ObjectRef, set_attach),
 | 
			
		||||
	method(ObjectRef, set_detach),
 | 
			
		||||
	method(ObjectRef, set_properties),
 | 
			
		||||
	// LuaEntitySAO-only
 | 
			
		||||
	method(ObjectRef, setvelocity),
 | 
			
		||||
@@ -3031,6 +3175,8 @@ const luaL_reg ObjectRef::methods[] = {
 | 
			
		||||
	method(ObjectRef, get_look_yaw),
 | 
			
		||||
	method(ObjectRef, set_inventory_formspec),
 | 
			
		||||
	method(ObjectRef, get_inventory_formspec),
 | 
			
		||||
	method(ObjectRef, get_player_control),
 | 
			
		||||
	method(ObjectRef, get_player_control_bits),
 | 
			
		||||
	{0,0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -6589,6 +6735,8 @@ void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
 | 
			
		||||
 | 
			
		||||
	getstringfield(L, -1, "visual", prop->visual);
 | 
			
		||||
 | 
			
		||||
	getstringfield(L, -1, "mesh", prop->mesh);
 | 
			
		||||
	
 | 
			
		||||
	// Deprecated: read object properties directly
 | 
			
		||||
	read_object_properties(L, -1, prop);
 | 
			
		||||
	
 | 
			
		||||
 
 | 
			
		||||
@@ -1371,9 +1371,9 @@ void Server::AsyncRunStep()
 | 
			
		||||
			/*
 | 
			
		||||
				Send player inventories and HPs if necessary
 | 
			
		||||
			*/
 | 
			
		||||
			if(playersao->m_teleported){
 | 
			
		||||
			if(playersao->m_moved){
 | 
			
		||||
				SendMovePlayer(client->peer_id);
 | 
			
		||||
				playersao->m_teleported = false;
 | 
			
		||||
				playersao->m_moved = false;
 | 
			
		||||
			}
 | 
			
		||||
			if(playersao->m_inventory_not_sent){
 | 
			
		||||
				UpdateCrafting(client->peer_id);
 | 
			
		||||
@@ -2369,7 +2369,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 | 
			
		||||
 | 
			
		||||
	if(command == TOSERVER_PLAYERPOS)
 | 
			
		||||
	{
 | 
			
		||||
		if(datasize < 2+12+12+4+4)
 | 
			
		||||
		if(datasize < 2+12+12+4+4+4)
 | 
			
		||||
			return;
 | 
			
		||||
	
 | 
			
		||||
		u32 start = 0;
 | 
			
		||||
@@ -2377,6 +2377,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 | 
			
		||||
		v3s32 ss = readV3S32(&data[start+2+12]);
 | 
			
		||||
		f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
 | 
			
		||||
		f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
 | 
			
		||||
		u32 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
 | 
			
		||||
		v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
 | 
			
		||||
		v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
 | 
			
		||||
		pitch = wrapDegrees(pitch);
 | 
			
		||||
@@ -2386,6 +2387,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 | 
			
		||||
		player->setSpeed(speed);
 | 
			
		||||
		player->setPitch(pitch);
 | 
			
		||||
		player->setYaw(yaw);
 | 
			
		||||
		player->keyPressed=keyPressed;
 | 
			
		||||
		player->control.up = (bool)(keyPressed&1);
 | 
			
		||||
		player->control.down = (bool)(keyPressed&2);
 | 
			
		||||
		player->control.left = (bool)(keyPressed&4);
 | 
			
		||||
		player->control.right = (bool)(keyPressed&8);
 | 
			
		||||
		player->control.jump = (bool)(keyPressed&16);
 | 
			
		||||
		player->control.aux1 = (bool)(keyPressed&32);
 | 
			
		||||
		player->control.sneak = (bool)(keyPressed&64);
 | 
			
		||||
		player->control.LMB = (bool)(keyPressed&128);
 | 
			
		||||
		player->control.RMB = (bool)(keyPressed&256);
 | 
			
		||||
		
 | 
			
		||||
		/*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
 | 
			
		||||
				<<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
 | 
			
		||||
@@ -3167,6 +3178,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 | 
			
		||||
 | 
			
		||||
		} // action == 4
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
			Catch invalid actions
 | 
			
		||||
		*/
 | 
			
		||||
@@ -4029,6 +4041,7 @@ void Server::fillMediaCache()
 | 
			
		||||
		paths.push_back(mod.path + DIR_DELIM + "textures");
 | 
			
		||||
		paths.push_back(mod.path + DIR_DELIM + "sounds");
 | 
			
		||||
		paths.push_back(mod.path + DIR_DELIM + "media");
 | 
			
		||||
		paths.push_back(mod.path + DIR_DELIM + "models");
 | 
			
		||||
	}
 | 
			
		||||
	std::string path_all = "textures";
 | 
			
		||||
	paths.push_back(path_all + DIR_DELIM + "all");
 | 
			
		||||
@@ -4054,6 +4067,7 @@ void Server::fillMediaCache()
 | 
			
		||||
				".png", ".jpg", ".bmp", ".tga",
 | 
			
		||||
				".pcx", ".ppm", ".psd", ".wal", ".rgb",
 | 
			
		||||
				".ogg",
 | 
			
		||||
				".x", ".b3d", ".md2", ".obj",
 | 
			
		||||
				NULL
 | 
			
		||||
			};
 | 
			
		||||
			if(removeStringEnd(filename, supported_ext) == ""){
 | 
			
		||||
 
 | 
			
		||||
@@ -152,6 +152,12 @@ public:
 | 
			
		||||
 | 
			
		||||
	virtual void setArmorGroups(const ItemGroupList &armor_groups)
 | 
			
		||||
	{}
 | 
			
		||||
	virtual void setAnimation(v2f frames, float frame_speed, float frame_blend)
 | 
			
		||||
	{}
 | 
			
		||||
	virtual void setBonePosition(std::string bone, v3f position, v3f rotation)
 | 
			
		||||
	{}
 | 
			
		||||
	virtual void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
 | 
			
		||||
	{}
 | 
			
		||||
	virtual ObjectProperties* accessObjectProperties()
 | 
			
		||||
	{ return NULL; }
 | 
			
		||||
	virtual void notifyObjectPropertiesModified()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								src/tile.cpp
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								src/tile.cpp
									
									
									
									
									
								
							@@ -518,15 +518,6 @@ core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<
 | 
			
		||||
// Apply transform to image data
 | 
			
		||||
void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Adds a new texture to the video driver and returns a pointer to it.
 | 
			
		||||
	This pointer should not be dropped. Any texture that was registered
 | 
			
		||||
	with that name before is removed (this may invalidate some ITexture
 | 
			
		||||
	pointers).
 | 
			
		||||
*/
 | 
			
		||||
video::ITexture* register_texture(video::IVideoDriver *driver,
 | 
			
		||||
		std::string name, video::IImage *img);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Generate image based on a string like "stone.png" or "[crack0".
 | 
			
		||||
	if baseimg is NULL, it is created. Otherwise stuff is made on it.
 | 
			
		||||
@@ -695,9 +686,11 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
 | 
			
		||||
				" create texture \""<<name<<"\""<<std::endl;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Create texture from resulting image
 | 
			
		||||
	if(baseimg != NULL)
 | 
			
		||||
		t = register_texture(driver, name, baseimg);
 | 
			
		||||
	{
 | 
			
		||||
		// Create texture from resulting image
 | 
			
		||||
		t = driver->addTexture(name.c_str(), baseimg);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/*
 | 
			
		||||
		Add texture to caches (add NULL textures too)
 | 
			
		||||
@@ -816,7 +809,7 @@ void TextureSource::rebuildImagesAndTextures()
 | 
			
		||||
		// Create texture from resulting image
 | 
			
		||||
		video::ITexture *t = NULL;
 | 
			
		||||
		if(img)
 | 
			
		||||
			t = register_texture(driver, sap->name, img);
 | 
			
		||||
			t = driver->addTexture(sap->name.c_str(), img);
 | 
			
		||||
		
 | 
			
		||||
		// Replace texture
 | 
			
		||||
		sap->a.atlas = t;
 | 
			
		||||
@@ -1051,7 +1044,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 | 
			
		||||
	/*
 | 
			
		||||
		Make texture
 | 
			
		||||
	*/
 | 
			
		||||
	video::ITexture *t = register_texture(driver, "__main_atlas__", atlas_img);
 | 
			
		||||
	video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
 | 
			
		||||
	assert(t);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@@ -1142,15 +1135,6 @@ video::IImage* generate_image_from_scratch(std::string name,
 | 
			
		||||
	return baseimg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
video::ITexture* register_texture(video::IVideoDriver *driver,
 | 
			
		||||
		std::string name, video::IImage *img)
 | 
			
		||||
{
 | 
			
		||||
	video::ITexture *old_texture = driver->findTexture(name.c_str());
 | 
			
		||||
	if(old_texture)
 | 
			
		||||
		driver->removeTexture(old_texture);
 | 
			
		||||
	return driver->addTexture(name.c_str(), img);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool generate_image(std::string part_of_name, video::IImage *& baseimg,
 | 
			
		||||
		IrrlichtDevice *device, SourceImageCache *sourcecache)
 | 
			
		||||
{
 | 
			
		||||
@@ -1557,12 +1541,12 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
 | 
			
		||||
			assert(img_top && img_left && img_right);
 | 
			
		||||
 | 
			
		||||
			// Create textures from images
 | 
			
		||||
			video::ITexture *texture_top = register_texture(driver,
 | 
			
		||||
					imagename_top + "__temp1__", img_top);
 | 
			
		||||
			video::ITexture *texture_left = register_texture(driver,
 | 
			
		||||
					imagename_left + "__temp2__", img_left);
 | 
			
		||||
			video::ITexture *texture_right = register_texture(driver,
 | 
			
		||||
					imagename_right + "__temp3__", img_right);
 | 
			
		||||
			video::ITexture *texture_top = driver->addTexture(
 | 
			
		||||
					(imagename_top + "__temp__").c_str(), img_top);
 | 
			
		||||
			video::ITexture *texture_left = driver->addTexture(
 | 
			
		||||
					(imagename_left + "__temp__").c_str(), img_left);
 | 
			
		||||
			video::ITexture *texture_right = driver->addTexture(
 | 
			
		||||
					(imagename_right + "__temp__").c_str(), img_right);
 | 
			
		||||
			assert(texture_top && texture_left && texture_right);
 | 
			
		||||
 | 
			
		||||
			// Drop images
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 | 
			
		||||
#define UTIL_SERIALIZE_HEADER
 | 
			
		||||
 | 
			
		||||
#include "../irrlichttypes.h"
 | 
			
		||||
#include "../irrlichttypes_bloated.h"
 | 
			
		||||
#include "../irr_v2d.h"
 | 
			
		||||
#include "../irr_v3d.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -197,6 +198,24 @@ inline v3s16 readV3S16(u8 *data)
 | 
			
		||||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void writeARGB8(u8 *data, video::SColor p)
 | 
			
		||||
{
 | 
			
		||||
	writeU8(&data[0], p.getAlpha());
 | 
			
		||||
	writeU8(&data[1], p.getRed());
 | 
			
		||||
	writeU8(&data[2], p.getGreen());
 | 
			
		||||
	writeU8(&data[3], p.getBlue());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline video::SColor readARGB8(u8 *data)
 | 
			
		||||
{
 | 
			
		||||
	video::SColor p;
 | 
			
		||||
	p.setAlpha(readU8(&data[0]));
 | 
			
		||||
	p.setRed(readU8(&data[1]));
 | 
			
		||||
	p.setGreen(readU8(&data[2]));
 | 
			
		||||
	p.setBlue(readU8(&data[3]));
 | 
			
		||||
	return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	The above stuff directly interfaced to iostream
 | 
			
		||||
*/
 | 
			
		||||
@@ -344,6 +363,20 @@ inline v3s16 readV3S16(std::istream &is)
 | 
			
		||||
	return readV3S16((u8*)buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void writeARGB8(std::ostream &os, video::SColor p)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4] = {0};
 | 
			
		||||
	writeARGB8((u8*)buf, p);
 | 
			
		||||
	os.write(buf, 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline video::SColor readARGB8(std::istream &is)
 | 
			
		||||
{
 | 
			
		||||
	char buf[4] = {0};
 | 
			
		||||
	is.read(buf, 4);
 | 
			
		||||
	return readARGB8((u8*)buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	More serialization stuff
 | 
			
		||||
*/
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user