minetest/src/mapblockobject.h
2010-12-22 03:33:58 +02:00

931 lines
19 KiB
C++

/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MAPBLOCKOBJECT_HEADER
#define MAPBLOCKOBJECT_HEADER
#include "common_irrlicht.h"
#include <math.h>
#include <string>
#include "serialization.h"
#include "mapnode.h"
#include "constants.h"
#include "debug.h"
#define MAPBLOCKOBJECT_TYPE_PLAYER 0
#define MAPBLOCKOBJECT_TYPE_SIGN 2
#define MAPBLOCKOBJECT_TYPE_RAT 3
// Used for handling selecting special stuff
//#define MAPBLOCKOBJECT_TYPE_PSEUDO 1000
class MapBlock;
class MapBlockObject
{
public:
MapBlockObject(MapBlock *block, s16 id, v3f pos):
m_collision_box(NULL),
m_selection_box(NULL),
m_block(block),
m_id(id),
m_pos(pos)
{
}
virtual ~MapBlockObject()
{
}
s16 getId()
{
return m_id;
}
MapBlock* getBlock()
{
return m_block;
}
// Writes id, pos and typeId
void serializeBase(std::ostream &os, u8 version)
{
u8 buf[6];
// id
writeS16(buf, m_id);
os.write((char*)buf, 2);
// position
// stored as x1000/BS v3s16
v3s16 pos_i(m_pos.X*1000/BS, m_pos.Y*1000/BS, m_pos.Z*1000/BS);
writeV3S16(buf, pos_i);
os.write((char*)buf, 6);
// typeId
writeU16(buf, getTypeId());
os.write((char*)buf, 2);
}
// Position where the object is drawn relative to block
virtual v3f getRelativeShowPos()
{
return m_pos;
}
// Get floating point position on map
v3f getAbsolutePos();
void setBlockChanged();
// Shootline is relative to block
bool isSelected(core::line3d<f32> shootline)
{
if(m_selection_box == NULL)
return false;
core::aabbox3d<f32> offsetted_box(
m_selection_box->MinEdge + m_pos,
m_selection_box->MaxEdge + m_pos
);
return offsetted_box.intersectsWithLine(shootline);
}
core::aabbox3d<f32> getSelectionBoxOnMap()
{
v3f absolute_pos = getAbsolutePos();
core::aabbox3d<f32> box(
m_selection_box->MinEdge + absolute_pos,
m_selection_box->MaxEdge + absolute_pos
);
return box;
}
/*
Implementation interface
*/
virtual u16 getTypeId() const = 0;
// Shall call serializeBase and then write the parameters
virtual void serialize(std::ostream &os, u8 version) = 0;
// Shall read parameters from stream
virtual void update(std::istream &is, u8 version) = 0;
virtual std::string getInventoryString() { return "None"; }
// Reimplementation shall call this.
virtual void updatePos(v3f pos)
{
m_pos = pos;
}
// Shall move the object around, modify it and possibly delete it.
// Typical dtimes are 0.2 and 10000.
// A return value of true requests deletion of the object by the caller.
// NOTE: Only server calls this.
virtual bool serverStep(float dtime, u32 daynight_ratio)
{ return false; };
#ifdef SERVER
void clientStep(float dtime) {};
void addToScene(void *smgr) {};
void removeFromScene() {};
void updateLight(u8 light_at_pos) {};
#else
// This should do slight animations only or so
virtual void clientStep(float dtime) {};
// NOTE: These functions should do nothing if the asked state is
// same as the current state
// Shall add and remove relevant scene nodes for rendering the
// object in the game world
virtual void addToScene(scene::ISceneManager *smgr) = 0;
// Shall remove stuff from the scene
// Should return silently if there is nothing to remove
// NOTE: This has to be called before calling destructor
virtual void removeFromScene() = 0;
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos) {};
#endif
virtual std::string infoText() { return ""; }
// Shall be left NULL if doesn't collide
// Position is relative to m_pos in block
core::aabbox3d<f32> * m_collision_box;
// Shall be left NULL if can't be selected
core::aabbox3d<f32> * m_selection_box;
protected:
MapBlock *m_block;
// This differentiates the instance of the object
// Not same as typeId.
s16 m_id;
// Position of the object inside the block
// Units is node coordinates * BS
v3f m_pos;
friend class MapBlockObjectList;
};
#if 0
/*
Used for handling selections of special stuff
*/
class PseudoMBObject : public MapBlockObject
{
public:
// The constructor of every MapBlockObject should be like this
PseudoMBObject(MapBlock *block, s16 id, v3f pos):
MapBlockObject(block, id, pos)
{
}
virtual ~PseudoMBObject()
{
if(m_selection_box)
delete m_selection_box;
}
/*
Implementation interface
*/
virtual u16 getTypeId() const
{
return MAPBLOCKOBJECT_TYPE_PSEUDO;
}
virtual void serialize(std::ostream &os, u8 version)
{
assert(0);
}
virtual void update(std::istream &is, u8 version)
{
assert(0);
}
virtual bool serverStep(float dtime, u32 daynight_ratio)
{
assert(0);
}
/*
Special methods
*/
void setSelectionBox(core::aabbox3d<f32> box)
{
m_selection_box = new core::aabbox3d<f32>(box);
}
protected:
};
#endif
class MovingObject : public MapBlockObject
{
public:
// The constructor of every MapBlockObject should be like this
MovingObject(MapBlock *block, s16 id, v3f pos):
MapBlockObject(block, id, pos),
m_speed(0,0,0),
m_oldpos(pos),
m_pos_animation_time(0),
m_showpos(pos)
{
m_touching_ground = false;
}
virtual ~MovingObject()
{
}
/*
Implementation interface
*/
virtual u16 getTypeId() const = 0;
virtual void serialize(std::ostream &os, u8 version)
{
serializeBase(os, version);
u8 buf[6];
// Write speed
// stored as x100/BS v3s16
v3s16 speed_i(m_speed.X*100/BS, m_speed.Y*100/BS, m_speed.Z*100/BS);
writeV3S16(buf, speed_i);
os.write((char*)buf, 6);
}
virtual void update(std::istream &is, u8 version)
{
u8 buf[6];
// Read speed
// stored as x100/BS v3s16
is.read((char*)buf, 6);
v3s16 speed_i = readV3S16(buf);
v3f speed((f32)speed_i.X/100*BS,
(f32)speed_i.Y/100*BS,
(f32)speed_i.Z/100*BS);
m_speed = speed;
}
// Reimplementation shall call this.
virtual void updatePos(v3f pos)
{
m_oldpos = m_showpos;
m_pos = pos;
if(m_pos_animation_time < 0.001 || m_pos_animation_time > 1.0)
m_pos_animation_time = m_pos_animation_time_counter;
else
m_pos_animation_time = m_pos_animation_time * 0.9
+ m_pos_animation_time_counter * 0.1;
m_pos_animation_time_counter = 0;
m_pos_animation_counter = 0;
}
// Position where the object is drawn relative to block
virtual v3f getRelativeShowPos()
{
return m_showpos;
}
// Returns m_showpos relative to whole map
v3f getAbsoluteShowPos();
virtual bool serverStep(float dtime, u32 daynight_ratio)
{ return false; };
virtual void clientStep(float dtime)
{};
/*virtual void addToScene(scene::ISceneManager *smgr) = 0;
virtual void removeFromScene() = 0;*/
/*
Special methods
*/
// Move with collision detection, server side
void move(float dtime, v3f acceleration);
// Move from old position to new position, client side
void simpleMove(float dtime);
protected:
v3f m_speed;
bool m_touching_ground;
// Client-side moving
v3f m_oldpos;
f32 m_pos_animation_counter;
f32 m_pos_animation_time;
f32 m_pos_animation_time_counter;
v3f m_showpos;
};
class SignObject : public MapBlockObject
{
public:
// The constructor of every MapBlockObject should be like this
SignObject(MapBlock *block, s16 id, v3f pos):
MapBlockObject(block, id, pos),
m_node(NULL)
{
m_selection_box = new core::aabbox3d<f32>
(-BS*0.4,-BS*0.5,-BS*0.4, BS*0.4,BS*0.5,BS*0.4);
}
virtual ~SignObject()
{
delete m_selection_box;
}
/*
Implementation interface
*/
virtual u16 getTypeId() const
{
return MAPBLOCKOBJECT_TYPE_SIGN;
}
virtual void serialize(std::ostream &os, u8 version)
{
serializeBase(os, version);
u8 buf[2];
// Write yaw * 10
writeS16(buf, m_yaw * 10);
os.write((char*)buf, 2);
// Write text length
writeU16(buf, m_text.size());
os.write((char*)buf, 2);
// Write text
os.write(m_text.c_str(), m_text.size());
}
virtual void update(std::istream &is, u8 version)
{
u8 buf[2];
// Read yaw * 10
is.read((char*)buf, 2);
s16 yaw_i = readS16(buf);
m_yaw = (f32)yaw_i / 10;
// Read text length
is.read((char*)buf, 2);
u16 size = readU16(buf);
// Read text
m_text.clear();
for(u16 i=0; i<size; i++)
{
is.read((char*)buf, 1);
m_text += buf[0];
}
updateSceneNode();
}
virtual bool serverStep(float dtime, u32 daynight_ratio)
{
return false;
}
#ifndef SERVER
virtual void addToScene(scene::ISceneManager *smgr)
{
if(m_node != NULL)
return;
video::IVideoDriver* driver = smgr->getVideoDriver();
scene::SMesh *mesh = new scene::SMesh();
{ // Front
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 0,1),
video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 1,1),
video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 1,0),
video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
//buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
buf->getMaterial().setTexture
(0, driver->getTexture("../data/sign.png"));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
}
{ // Back
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
};
u16 indices[] = {0,1,2,2,3,0};
buf->append(vertices, 4, indices, 6);
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
//buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
buf->getMaterial().setTexture
(0, driver->getTexture("../data/sign_back.png"));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
// Add to mesh
mesh->addMeshBuffer(buf);
buf->drop();
}
m_node = smgr->addMeshSceneNode(mesh, NULL);
mesh->drop();
updateSceneNode();
}
virtual void removeFromScene()
{
if(m_node != NULL)
{
m_node->remove();
m_node = NULL;
}
}
virtual void updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
#endif
virtual std::string infoText()
{
return std::string("\"") + m_text + "\"";
}
virtual std::string getInventoryString()
{
return std::string("Sign ")+m_text;
}
/*
Special methods
*/
void updateSceneNode()
{
#ifndef SERVER
if(m_node != NULL)
{
m_node->setPosition(getAbsolutePos());
m_node->setRotation(v3f(0, m_yaw, 0));
}
#endif
}
void setText(std::string text)
{
if(text.size() > SIGN_TEXT_MAX_LENGTH)
text = text.substr(0, SIGN_TEXT_MAX_LENGTH);
m_text = text;
setBlockChanged();
}
void setYaw(f32 yaw)
{
m_yaw = yaw;
setBlockChanged();
}
protected:
scene::IMeshSceneNode *m_node;
std::string m_text;
f32 m_yaw;
};
class RatObject : public MovingObject
{
public:
RatObject(MapBlock *block, s16 id, v3f pos):
MovingObject(block, id, pos),
m_node(NULL)
{
m_collision_box = new core::aabbox3d<f32>
(-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3);
m_selection_box = new core::aabbox3d<f32>
(-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3);
m_counter1 = 0;
m_counter2 = 0;
m_age = 0;
}
virtual ~RatObject()
{
delete m_collision_box;
delete m_selection_box;
}
/*
Implementation interface
*/
virtual u16 getTypeId() const
{
return MAPBLOCKOBJECT_TYPE_RAT;
}
virtual void serialize(std::ostream &os, u8 version)
{
MovingObject::serialize(os, version);
u8 buf[2];
// Write yaw * 10
writeS16(buf, m_yaw * 10);
os.write((char*)buf, 2);
}
virtual void update(std::istream &is, u8 version)
{
MovingObject::update(is, version);
u8 buf[2];
// Read yaw * 10
is.read((char*)buf, 2);
s16 yaw_i = readS16(buf);
m_yaw = (f32)yaw_i / 10;
updateNodePos();
}
virtual bool serverStep(float dtime, u32 daynight_ratio)
{
m_age += dtime;
if(m_age > 60)
// Die
return true;
v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI));
f32 speed = 2*BS;
m_speed.X = speed * dir.X;
m_speed.Z = speed * dir.Z;
if(m_touching_ground && (m_oldpos - m_pos).getLength() < dtime*speed/2)
{
m_counter1 -= dtime;
if(m_counter1 < 0.0)
{
m_counter1 += 1.0;
m_speed.Y = 5.0*BS;
}
}
{
m_counter2 -= dtime;
if(m_counter2 < 0.0)
{
m_counter2 += (float)(rand()%100)/100*3.0;
m_yaw += ((float)(rand()%200)-100)/100*180;
m_yaw = wrapDegrees(m_yaw);
}
}
m_oldpos = m_pos;
//m_yaw += dtime*90;
move(dtime, v3f(0, -9.81*BS, 0));
//updateNodePos();
return false;
}
#ifndef SERVER
virtual void clientStep(float dtime)
{
//m_pos += m_speed * dtime;
MovingObject::simpleMove(dtime);
updateNodePos();
}
virtual void addToScene(scene::ISceneManager *smgr);
virtual void removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
virtual void updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
#endif
virtual std::string getInventoryString()
{
// There must be a space after the name
// Or does there?
return std::string("Rat ");
}
/*
Special methods
*/
void updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(getAbsoluteShowPos());
m_node->setRotation(v3f(0, -m_yaw+180, 0));
}
protected:
scene::IMeshSceneNode *m_node;
float m_yaw;
float m_counter1;
float m_counter2;
float m_age;
};
/*
NOTE: Not used.
*/
class PlayerObject : public MovingObject
{
public:
PlayerObject(MapBlock *block, s16 id, v3f pos):
MovingObject(block, id, pos),
m_node(NULL)
{
m_collision_box = new core::aabbox3d<f32>
(-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3);
/*m_selection_box = new core::aabbox3d<f32>
(-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3);*/
}
virtual ~PlayerObject()
{
if(m_collision_box)
delete m_collision_box;
if(m_selection_box)
delete m_selection_box;
}
/*
Implementation interface
*/
virtual u16 getTypeId() const
{
return MAPBLOCKOBJECT_TYPE_PLAYER;
}
virtual void serialize(std::ostream &os, u8 version)
{
// Object data is generated from actual player
}
virtual void update(std::istream &is, u8 version)
{
MovingObject::update(is, version);
u8 buf[2];
// Read yaw * 10
is.read((char*)buf, 2);
s16 yaw_i = readS16(buf);
m_yaw = (f32)yaw_i / 10;
updateNodePos();
}
virtual bool serverStep(float dtime, u32 daynight_ratio)
{
// Player is handled elsewhere.
// Die.
//return true;
// Actually, fail very loudly:
assert(0);
}
#ifndef SERVER
virtual void clientStep(float dtime)
{
MovingObject::simpleMove(dtime);
updateNodePos();
}
virtual void addToScene(scene::ISceneManager *smgr);
virtual void removeFromScene()
{
if(m_node == NULL)
return;
m_node->remove();
m_node = NULL;
}
virtual void updateLight(u8 light_at_pos)
{
if(m_node == NULL)
return;
u8 li = decode_light(light_at_pos);
video::SColor color(255,li,li,li);
scene::IMesh *mesh = m_node->getMesh();
u16 mc = mesh->getMeshBufferCount();
for(u16 j=0; j<mc; j++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
u16 vc = buf->getVertexCount();
for(u16 i=0; i<vc; i++)
{
vertices[i].Color = color;
}
}
}
#endif
/*
Special methods
*/
void updateNodePos()
{
if(m_node == NULL)
return;
m_node->setPosition(getAbsoluteShowPos());
m_node->setRotation(v3f(0, -m_yaw+180, 0));
}
protected:
scene::IMeshSceneNode *m_node;
float m_yaw;
v3f m_oldpos;
};
struct DistanceSortedObject
{
DistanceSortedObject(MapBlockObject *a_obj, f32 a_d)
{
obj = a_obj;
d = a_d;
}
MapBlockObject *obj;
f32 d;
bool operator < (DistanceSortedObject &other)
{
return d < other.d;
}
};
class MapBlockObjectList
{
public:
MapBlockObjectList(MapBlock *block);
~MapBlockObjectList();
// Writes the count, id, the type id and the parameters of all objects
void serialize(std::ostream &os, u8 version);
// Reads ids, type_ids and parameters.
// Creates, updates and deletes objects.
// If smgr!=NULL, new objects are added to the scene
void update(std::istream &is, u8 version, scene::ISceneManager *smgr,
u32 daynight_ratio);
// Finds a new unique id
s16 getFreeId() throw(ContainerFullException);
/*
Adds an object.
Set id to -1 to have this set it to a suitable one.
The block pointer member is set to this block.
*/
void add(MapBlockObject *object)
throw(ContainerFullException, AlreadyExistsException);
// Deletes and removes all objects
void clear();
/*
Removes an object.
Ignores inexistent objects
*/
void remove(s16 id);
/*
References an object.
The object will not be valid after step() or of course if
it is removed.
Grabbing the lock is recommended while processing.
*/
MapBlockObject * get(s16 id);
// You'll want to grab this in a SharedPtr
JMutexAutoLock * getLock()
{
return new JMutexAutoLock(m_mutex);
}
// Steps all objects and if server==true, removes those that
// want to be removed
void step(float dtime, bool server, u32 daynight_ratio);
// Wraps an object that wants to move onto this block from an another
// Returns true if wrapping was impossible
bool wrapObject(MapBlockObject *object);
// origin is relative to block
void getObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest);
// Number of objects
s32 getCount()
{
return m_objects.size();
}
private:
JMutex m_mutex;
// Key is id
core::map<s16, MapBlockObject*> m_objects;
MapBlock *m_block;
u32 m_last_update_daynight_ratio;
};
#endif