/* core/entityprojectile.cc This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include "core/entityprojectile.h" #include "core/application.h" #include "math/functions.h" #include "sys/sys.h" #include namespace core { EntityProjectile::EntityProjectile(const Entity *spawn) : EntityDynamic() { set_label("projectile"); set_flag(Entity::KeepAlive); set_shape(Entity::Sphere); set_radius(PROJECTILE_RADIUS); set_mass(radius()); projectile_damage = 0.0f; projectile_lifespan = 0.0f; projectile_owner_id = 0; projectile_spawn_id = 0; projectile_timestamp = game()->timestamp(); if (spawn) { set_spawn(spawn); } } EntityProjectile::EntityProjectile(std::istream & is) : EntityDynamic(is) { set_label("projectile"); set_flag(Entity::KeepAlive); set_shape(Entity::Sphere); set_radius(PROJECTILE_RADIUS); set_mass(radius()); projectile_damage = 0.0f; projectile_lifespan = 0.0f; projectile_owner_id = 0; projectile_spawn_id = 0; projectile_timestamp = game()->timestamp(); } EntityProjectile::~EntityProjectile() { } void EntityProjectile::upkeep(const unsigned long timestamp) { die(); } void EntityProjectile::collision(Entity *other) { if (state() == Entity::Destroyed) { return; } else { if (other->id() == projectile_spawn_id) { // don't shoot yourself return; } switch(other->type()) { case Projectile: return; break; case Dynamic: case Controlable: static_cast(other)->hit(this); default: set_state(Entity::Destroyed); break; } } } void EntityProjectile::frame(const unsigned long elapsed) { // transfer bullet state to entity state // do not set_dirty() as movement will be handled client-side if (entity_body) { body()->setLinearVelocity(math::to_btVector3(axis().forward() * speed())); btTransform t; entity_motionstate->getWorldTransform(t); get_location().assign(t.getOrigin()); get_axis().assign(t.getBasis()); //entity_speed = (float) entity_body->getLinearVelocity().length(); } if (projectile_timestamp + projectile_lifespan < game()->timestamp()) { set_state(Entity::Destroyed); set_speed(0.0f); set_dirty(); } if (state() == Entity::Destroyed) { die(); } } void EntityProjectile::reset() { // no bullet physics on NonSolid entities if (!radius() || has_flag(NonSolid)) { return; } // remove Docked and Destroyed entities from the physics simulation if (died() || (state() == Entity::Docked) || (state() == Entity::Destroyed)) { if (entity_body) { if (entity_motionstate) { delete entity_motionstate; entity_motionstate = 0; } if (zone() && zone()->physics()) { entity_zone->physics()->removeRigidBody(body()); } if (entity_collision_shape) { delete entity_collision_shape; entity_collision_shape = 0; } for (CollisionShapes::iterator sit = entity_collision_child_shapes.begin(); sit != entity_collision_child_shapes.end(); sit++) { delete (*sit); (*sit) = 0; } entity_collision_child_shapes.clear(); if (entity_body) { delete entity_body; entity_body = 0; } if (entity_body_info) { delete entity_body_info; entity_body_info = 0; } } return; } // location and orientation btTransform t; t.setIdentity(); t.setOrigin(to_btVector3(location())); t.setBasis(to_btMatrix3x3(axis())); // construct physics body if necessary if (!entity_body) { // use a sphere with a radius matching the entity btSphereShape *sphereshape = new btSphereShape(radius()); entity_collision_shape = sphereshape; // set margin //entity_collision_shape->setMargin(core::Cvar::sv_collisionmargin->value()); // calculate inertia btVector3 inertia(0, 0, 0); // create motion state entity_motionstate = new btDefaultMotionState(t); // create physics body entity_body_info = new btRigidBody::btRigidBodyConstructionInfo(entity_mass, entity_motionstate, entity_collision_shape, inertia); entity_body = new btRigidBody(*entity_body_info); // point the bullet user pointer to the entity entity_body->setUserPointer((void *) this); // enable custom collision callback entity_body->setCollisionFlags(entity_body->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK | btCollisionObject::CF_NO_CONTACT_RESPONSE); if (entity_mass) { entity_body->setActivationState(DISABLE_DEACTIVATION); } else { entity_body->setActivationState(ISLAND_SLEEPING); } if (zone()) zone()->physics()->addRigidBody(entity_body); } // transfer entity location to motion state body()->setDamping(0.0f, 0.0f); body()->setLinearVelocity(math::to_btVector3(axis().forward() * speed())); body()->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f)); body()->setWorldTransform(t); body()->clearForces(); if (motionstate()) { motionstate()->setWorldTransform(t); } set_dirty(); } void EntityProjectile::set_projectile_modelname(const std::string modelname) { projectile_modelname_str.assign(modelname); set_modelname("maps/projectiles/" + modelname); } void EntityProjectile::set_projectile_soundname(const std::string soundname) { projectile_soundname_str.assign(soundname); } void EntityProjectile::set_spawn(const Entity *spawn) { if (spawn) { // set spawn id projectile_spawn_id = spawn->id(); // set owner id projectile_owner_id = 0; if (spawn->type() == Entity::Controlable) { const Player *player = static_cast(spawn)->owner(); if (player) { projectile_owner_id = player->id(); } } // set colors set_color(spawn->color()); set_color_second(spawn->color_second()); //set zone set_zone(spawn->zone()); } else { projectile_spawn_id = 0; projectile_owner_id = 0; } } void EntityProjectile::serialize_server_create(std::ostream & os) const { os << moduletype() << " "; os << flags() << " "; os << (zone() ? zone()->id() : 0) << " "; os << std::setprecision(8) << location() << " "; os << color() << " "; os << color_second() << " "; os << radius() << " "; os << std::setprecision(8) << axis().forward() << " "; os << std::setprecision(8) << axis().left() << " "; os << "\"" << projectile_modelname() << "\" "; os << "\"" << projectile_soundname() << "\" "; os << roundf(speed() * 100.0f) << " "; os << state() << " ";; os << lifespan() << " "; } void EntityProjectile::receive_server_create(std::istream &is) { is >> entity_moduletypeid; unsigned int f; is >> f; set_flags(f); unsigned int zo; is >> zo; set_zone(Zone::find(zo)); if (entity_zone && !zo) { con_warn << "Received entity " << id() << " for unknown zone " << zo << "!" << std::endl; } is >> get_location(); is >> get_color(); is >> get_color_second(); float r; is >> r; set_radius(r) ; is >> get_axis()[0]; is >> get_axis()[1]; get_axis()[2].assign(math::crossproduct(axis().forward(), axis().left())); // read projectile modelname std::string n; char c; n.clear(); while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; set_projectile_modelname(n); // read projectile soundname n.clear(); while ((is.get(c)) && (c != '"')); while ((is.get(c)) && (c != '"')) n += c; set_projectile_soundname(n); is >> entity_speed; entity_speed /= 100.0f; is >> entity_state; is >> projectile_lifespan; set_dirty(false); } } // namespace core