/* $Id: collidableobject.cpp,v 1.40.4.2 2006/01/20 11:33:52 chfreund Exp $ */ #include "collidableobject.hpp" #include "world.hpp" #include "flag.hpp" /**********************************************************/ CollidableObject::CollidableObject() : m_damageSampleTimeout( 0 ), m_focusPriority ( 0 ), m_collRectSet( false ), m_firstUpdate( true ), m_wasHurt( false ) { m_bounceThresholdVel2 = 5.0, m_bounceFactorHLVel = 5.0, m_bounceFactorLow = 0.9, m_bounceFactorHigh = 0.5, m_maxGroundedForce2 = 2000.0; m_maxGroundedForceVert = 0.01; } /**********************************************************/ CollidableObject::~CollidableObject() { removeCollRect(); } /**********************************************************/ bool CollidableObject::testGrounded() { int dummy; return ! m_worldPointer->getMap()->testLinePassableSmoothing( ROUND( m_pos.x ) + m_collRectX, ROUND( m_pos.y ) + m_collRectY + m_collRectHeight, ROUND( m_pos.x ) + m_collRectX + m_collRectWidth - 1, ROUND( m_pos.y ) + m_collRectY + m_collRectHeight, dummy, dummy, dummy, dummy, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ); } /**********************************************************/ void CollidableObject::setCollRect() { if( m_removeMeAfterUpdate || ! isLiving() ) return; DBG( 3 ) CHECK( ! m_collRectSet, "CollidableObject::setCollRect: collRect already set\n" ); const int posX = ROUND( m_pos.x ) + m_collRectX, posY = ROUND( m_pos.y ) + m_collRectY; LOG( 5 ) INFO( "CollidableObject::setCollRect: set at %i, %i\n", posX, posY ); m_worldPointer->getMap()->setCollRect( posX, posY, m_collRectWidth, m_collRectHeight ); m_collRectSet = true; } /**********************************************************/ void CollidableObject::removeCollRect() { if( ! m_collRectSet ) return; const int posX = ROUND( m_pos.x ) + m_collRectX, posY = ROUND( m_pos.y ) + m_collRectY; LOG( 5 ) INFO( "CollidableObject::removeCollRect: set at %i, %i\n", posX, posY ); m_worldPointer->getMap()->removeCollRect( posX, posY, m_collRectWidth, m_collRectHeight ); m_collRectSet = false; } /**********************************************************/ bool CollidableObject::testCollRect() { int dummy; const int posX = ROUND( m_pos.x ) + m_collRectX, posY = ROUND( m_pos.y ) + m_collRectY; LOG( 5 ) INFO( "CollidableObject::testCollRect: set at %i, %i\n", posX, posY ); return m_worldPointer->getMap()->testCollRect( posX, posY, m_collRectWidth, m_collRectHeight, dummy, dummy ); } /**********************************************************/ int CollidableObject::applyDamage( const Particles::ParticleData& p ) { m_wasHurt = true; const real vDiff = min( (p.vel - m_vel).abs2(), static_cast( 900.0 )); const int damage = min( ROUND( p.damage * vDiff ), m_health ); // recoil by particles (mass := 1) Vector forceAdd = p.vel * 2.0; forceAdd /= m_worldPointer->getDT( m_pos ); m_force += forceAdd; // factor 2 is unphysical but also more fun setState( GROUNDED, false ); LOG( 5 ) INFO( "CollidableObject::applyDamage: health: %i, damage: %i\n", m_health, damage ); m_health -= damage; return damage; } /**********************************************************/ int CollidableObject::applyDamage( const int damage, Player* shooter ) { m_wasHurt = true; const int realDamage = min( damage, m_health ); m_health -= realDamage; return realDamage; } /**********************************************************/ void CollidableObject::update() { if( ! isLiving() ) return; if( ! preBallistics() ) return; if( ! doBallistics() ) return; postBallistics(); } /**********************************************************/ bool CollidableObject::preBallistics( const bool continueUpdate ) { if( m_damageSampleTimeout > 0 ) m_damageSampleTimeout--; // remove obstacle stencil removeCollRect(); if( isGrounded() && (m_force.y < -m_maxGroundedForceVert || m_force.abs2() > m_maxGroundedForce2 || ! testGrounded()) ) { setState( GROUNDED, false ); } return continueUpdate; } /**********************************************************/ bool CollidableObject::doBallistics( const bool continueUpdate ) { const real DT = m_worldPointer->getDT( m_pos ); int colX, colY, preColX, preColY; if( isGrounded() ) { m_force.set( 0.0, GRAVITATION * m_mass ); } else { Vector velAdd = m_force / m_mass; velAdd *= DT; m_vel += velAdd; Vector newPos = m_pos + m_vel * DT; m_force.set( 0.0, GRAVITATION * m_mass ); // is there something to collide? if( m_worldPointer->getMap()->testLineCollRect( ROUND( m_pos.x ) + m_collRectX, ROUND( m_pos.y ) + m_collRectY, ROUND( newPos.x ) + m_collRectX, ROUND( newPos.y ) + m_collRectY, m_collRectWidth, m_collRectHeight, preColX, preColY, colX, colY ) ) { // no, then just keep on flying m_pos = newPos; } else { // set position to coordinates close to collision m_pos.set( preColX - m_collRectX, preColY - m_collRectY ); return collidedWithObstacle( colX, colY ); } } return continueUpdate; } /**********************************************************/ bool CollidableObject::postBallistics( const bool continueUpdate ) { setCollRect(); m_firstUpdate = false; DBG( 5 ) m_worldPointer->getMap()->setColorAt( ROUND( m_pos.x ), ROUND( m_pos.y ), 0xffffff ); return continueUpdate; } /**********************************************************/ bool CollidableObject::collidedWithObstacle( const Sint32 colX, const Sint32 colY ) { const real vel2 = m_vel.abs2(); if( vel2 < m_bounceThresholdVel2 && testGrounded() ) { setState( GROUNDED, true ); m_vel.set( 0.0, 0.0 ); } else { const real vel = SQRT_REAL( vel2 ), outAngle = m_worldPointer->getMap()->calculateBounceAngle( m_vel.x, m_vel.y, colX, colY, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ); if( vel > m_bounceFactorHLVel ) m_vel.setAngle( outAngle, vel * m_bounceFactorHigh ); else m_vel.setAngle( outAngle, vel * m_bounceFactorLow ); } return true; } /**********************************************************/ void CollidableObject::serialize( Uint8*& bufferPointer ) const { // expands to a check of the buffer movement START_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer ); AttachableObject::serialize( bufferPointer ); Serialize::serialize( m_mass , bufferPointer ); Serialize::serialize( m_health , bufferPointer ); Serialize::serialize( m_damageSampleTimeout, bufferPointer ); Serialize::serialize( m_collRectSet , bufferPointer ); Serialize::serialize( m_firstUpdate , bufferPointer ); Serialize::serialize( m_wasHurt , bufferPointer ); // m_focusPriority is _not_ serialized // expands to tag serialization SERIALIZE_OBJECT_TAG( bufferPointer ); // expands to a check of the buffer movement END_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer, CollidableObject ); } /**********************************************************/ void CollidableObject::deserialize( Uint8*& bufferPointer ) { AttachableObject::deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, m_mass ); Serialize::deserialize( bufferPointer, m_health ); Serialize::deserialize( bufferPointer, m_damageSampleTimeout ); Serialize::deserialize( bufferPointer, m_collRectSet ); Serialize::deserialize( bufferPointer, m_firstUpdate ); Serialize::deserialize( bufferPointer, m_wasHurt ); // m_focusPriority is _not_ serialized // expands to tag deserialization DESERIALIZE_OBJECT_TAG( bufferPointer ); } /**********************************************************/ Uint32 CollidableObject::getSerializeBufferSize() const { return AttachableObject::getSerializeBufferSize() + Serialize::sizeOf( m_mass ) + Serialize::sizeOf( m_health ) + Serialize::sizeOf( m_damageSampleTimeout ) + Serialize::sizeOf( m_collRectSet ) + Serialize::sizeOf( m_firstUpdate ) + Serialize::sizeOf( m_wasHurt ) // m_focusPriority is _not_ serialized // adds additional size for debugging (see serialize.hpp), // but only, if the tags are used PLUS_TAG_SIZE( 1 ); } /**********************************************************/ void CollidableObject::dump( std::ostream& out ) const { using namespace std; Object::dump( out ); out << "CollidableObject components:" << endl; out << "\tMass: " << m_mass << endl; }