/* $Id: avatarworm.cpp,v 1.74.2.1 2006/03/14 14:45:52 chfreund Exp $ */ #include "avatarworm.hpp" #include "audio.hpp" #include "random.hpp" /**********************************************************/ // some graphic settings we cannot extract from the sprites #define MIN_AIMING_ANGLE 0 #define AIMING_ANGLE_STEP 5 /**********************************************************/ // derived constants // number of aiming angles #define NUM_AIMING_ANGLES (( ((Sint32)MAX_AIMING_ANGLE - MIN_AIMING_ANGLE) / \ AIMING_ANGLE_STEP ) + 1) // number of sequences for one direction (two directions: 0,1) #define NUM_SEQUENCES_PER_DIRECTION NUM_AIMING_ANGLES // number of walking sequences #define NUM_WALKING_SEQUENCES (2 * NUM_SEQUENCES_PER_DIRECTION) ////////////////////////////////////////////////// // more settings concerning the graphics sequence indices // // first sequence belonging to gun occupation #define FIRST_GUN_SEQUENCE NUM_WALKING_SEQUENCES #define NUM_GUN_SEQUENCES 1 #define LAST_GUN_SEQUENCE (FIRST_GUN_SEQUENCE + NUM_GUN_SEQUENCES - 1) // first sequence showing the worm operating a remote control #define FIRST_RC_SEQUENCE (FIRST_GUN_SEQUENCE + NUM_GUN_SEQUENCES) #define NUM_RC_SEQUENCES 2 /**********************************************************/ AvatarWorm::AvatarWorm() : Avatar() { String sampleFilename; m_maxHealth = 1000000; m_health = m_maxHealth; m_mass = 250.0; m_collRectX = -4; m_collRectY = -15; m_collRectWidth = 9; m_collRectHeight = 21; m_weaponPivotDY = 0; // set the initial colors m_Colors[SKIN_COLOR] = TPLCOL_RGB( 255, 255, 255 ); m_Colors[EYE_COLOR] = TPLCOL_DONT_COL; m_Colors[GUN_COLOR] = TPLCOL_RGB( 0, 100, 230 ); // initialize the player and team color per default // for an individual fight m_Colors[PLAYER_COLOR] = m_Colors[SKIN_COLOR]; m_Colors[TEAM_COLOR] = m_Colors[GUN_COLOR]; for( int s = 0; s < N_INJURED_SAMPLES; s++ ) { sampleFilename.format( "sound/avatar/worm/injured%01i.wav", s+1 ); m_injuredSample[s] = Audio::getInstance()->loadSound( sampleFilename ); } for( int s = 0; s < N_DIE_SAMPLES; s++ ) { sampleFilename.format( "sound/avatar/worm/die%02i.wav", s+1 ); m_dieSample[s] = Audio::getInstance()->loadSound( sampleFilename ); } } /**********************************************************/ int AvatarWorm::applyDamage( const Particles::ParticleData& p ) { const int damage = Avatar::applyDamage( p ); // \todo UGLY HACK!!! parts of this function should move to class Avatar if( damage <= 0 ) return 0; LOG( 5 ) INFO( "Avatar::applyDamage: player '%s' was hit\n", static_cast( getPlayer()->getName() )); Uint8 r, g, b; real brightness; LOG( 5 ) INFO( "Avatar::applyDamage: damage: %i => new health: %i\n", damage, m_health ); Player* const shooter = m_worldPointer->getPlayerByID( p.owner ); if( shooter ) { m_worldPointer->getScorekeeper() ->playerDamagedPlayer( shooter->getPlayerID(), getPlayer()->getPlayerID(), damage, m_health <= 0 ); } if( m_health > 0 ) { // play sound if( m_damageSampleTimeout == 0 ) { Audio::getInstance()->playSound( m_injuredSample[m_worldPointer->getRandom().getUint32() % N_INJURED_SAMPLES], ROUND( m_pos.x ), ROUND( m_pos.y ) ); m_damageSampleTimeout = 5; } // generate blood for( int i = 0; i < 3; i++ ) { brightness = 0.7 + 0.3*m_worldPointer->getRandom().getNormedReal(); r = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 16 ) * brightness ); g = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 8 ) * brightness ); b = ROUND( static_cast( m_Colors[SKIN_COLOR] ) * brightness ); Vector vel = m_vel; real velFactor = m_worldPointer->getRandom().getNormedReal(); velFactor *= 0.20; velFactor += 0.10; vel += p.vel * velFactor; real length = m_worldPointer->getRandom().getNormedReal(); length *= 2.0; vel += m_worldPointer->getRandom().getVector( length ); m_worldPointer->getParticles()->addParticle( Particles::ParticleData( p.pos, vel, Particles::BOUNCE_EARTH | Particles::STAIN | Particles::REDUNDANT, 0, 0, 75, 1, (((r << 8) | g) << 8) | b )); } } else { // remove avatar stencil removeCollRect(); setState( VISIBLE | WEAPON_VISIBLE | LIVING | SHOOTING | DIGGING, false ); // leave gun properly leaveCurrentGun(); // detach from everything detachFromAll(); // disable low-pass filter Audio::getInstance()->setLowPassFilter( -1.0 ); // play sound Audio::getInstance()->playSound( m_dieSample[m_worldPointer->getRandom().getUint32() % N_DIE_SAMPLES], ROUND( m_pos.x ), ROUND( m_pos.y ) ); // add twice the current force to velocity real factor = m_worldPointer->getDT( m_pos ); factor *= m_mass; factor *= 2.0; Vector velAdd = m_force; velAdd /= factor; m_vel += velAdd; // generate a lot of blood for( int i = 0; i < 1000; i++ ) { brightness = 0.7 + 0.3*m_worldPointer->getRandom().getNormedReal(); r = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 16 ) * brightness ); g = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 8 ) * brightness ); b = ROUND( static_cast( m_Colors[SKIN_COLOR] ) * brightness ); Vector vel = m_vel; real length = m_worldPointer->getRandom().getNormedReal(); length *= 4.0; length += 2.0; vel += m_worldPointer->getRandom().getVector( length ); m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos + elementProd( m_worldPointer->getRandom().getVector( 1.0 ), Vector( 3.0, 8.0 )), vel, Particles::BOUNCE_EARTH | Particles::BOUNCE_OBJECT | Particles::STAIN | Particles::REDUNDANT, 0, 0, 75, 1, (((r << 8) | g) << 8) | b )); } // initialize eyes if( (m_worldPointer->getRandom().getUint32() & 15) == 0 ) { for( int eye = 0; eye < 2; eye++ ) { const Vector eyeVel = m_vel + m_worldPointer->getRandom().getVector( 2.0 ); for( double y = m_pos.y-3.0; y <= m_pos.y+3.0; y++ ) { for( double x = m_pos.x-3.0; x <= m_pos.x+3.0; x++ ) { const double dist = (x-m_pos.x)*(x-m_pos.x) + (y-m_pos.y)*(y-m_pos.y); if( dist < 0.9*0.9 ) r = g = b = 0; else if( dist < 2.5*2.5 ) r = g = b = 230; else continue; m_worldPointer->getParticles()->addParticle( Particles::ParticleData( Vector( x, y ), eyeVel, Particles::BOUNCE_EARTH | Particles::BOUNCE_OBJECT | Particles::STAIN | Particles::REDUNDANT, 0, 0, 200, 1, (((r << 8) | g) << 8) | b )); } } } } // reset the bonus pack for the "sleeping state" of the avatar m_bonusPack.resetForRespawn(); // send message if ( shooter ) { if ( getPlayer() == shooter ) { m_messageSink->addMessage( MessageGenerator::createSuicideMessage( getPlayer()->getName() ) ); } else { m_messageSink->addMessage( MessageGenerator::createFragMessage( shooter->getName(), getPlayer()->getName() ) ); } } } return damage; } /**********************************************************/ int AvatarWorm::applyDamage( const int damage, Player* shooter ) { const int realDamage = Avatar::applyDamage( damage, shooter ); if( realDamage <= 0 ) return 0; LOG( 5 ) INFO( "Avatar::applyDamage: player '%s' was hit\n", static_cast( getPlayer()->getName() )); Uint8 r, g, b; real brightness; LOG( 5 ) INFO( "Avatar::applyDamage: damage: %i => new health: %i\n", realDamage, m_health ); m_worldPointer->getScorekeeper() ->playerDamagedPlayer( shooter->getPlayerID(), getPlayer()->getPlayerID(), realDamage, m_health <= 0 ); if( m_health > 0 ) { // play sound if( m_damageSampleTimeout == 0 ) { Audio::getInstance()->playSound( m_injuredSample[m_worldPointer->getRandom().getUint32() % N_INJURED_SAMPLES], ROUND( m_pos.x ), ROUND( m_pos.y ) ); m_damageSampleTimeout = 5; } // generate blood for( int i = 0; i < 3; i++ ) { brightness = 0.7 + 0.3*m_worldPointer->getRandom().getNormedReal(); r = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 16 ) * brightness ); g = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 8 ) * brightness ); b = ROUND( static_cast( m_Colors[SKIN_COLOR] ) * brightness ); Vector vel = m_vel; vel += m_vel; real length = m_worldPointer->getRandom().getNormedReal(); length *= 4.0; length += 3.0; vel += m_worldPointer->getRandom().getVector( length ); m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos, vel, Particles::BOUNCE_EARTH | Particles::STAIN | Particles::REDUNDANT, 0, 0, 75, 1, (((r << 8) | g) << 8) | b )); } } else { // remove avatar stencil removeCollRect(); setState( VISIBLE | WEAPON_VISIBLE | LIVING | SHOOTING | DIGGING, false ); // leave gun properly leaveCurrentGun(); // detach from everything detachFromAll(); // disable low-pass filter Audio::getInstance()->setLowPassFilter( -1.0 ); // play sound Audio::getInstance()->playSound( m_dieSample[m_worldPointer->getRandom().getUint32() % N_DIE_SAMPLES], ROUND( m_pos.x ), ROUND( m_pos.y) ); // generate a lot of blood for( int i = 0; i < 1000; i++ ) { brightness = 0.7 + 0.3*m_worldPointer->getRandom().getNormedReal(); r = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 16 ) * brightness ); g = ROUND( static_cast( m_Colors[SKIN_COLOR] >> 8 ) * brightness ); b = ROUND( static_cast( m_Colors[SKIN_COLOR] ) * brightness ); Vector vel = m_vel; real length = m_worldPointer->getRandom().getNormedReal(); length *= 6.0; length += 2.0; vel += m_worldPointer->getRandom().getVector( length ); m_worldPointer->getParticles()->addParticle( Particles::ParticleData( m_pos, vel, Particles::BOUNCE_EARTH | Particles::BOUNCE_OBJECT | Particles::STAIN | Particles::REDUNDANT, 0, 0, 75, 1, (((r << 8) | g) << 8) | b )); } // remove avatar stencil removeCollRect(); setState( VISIBLE | WEAPON_VISIBLE | LIVING | SHOOTING, false ); // reset the bonus pack for the "sleeping state" of the avatar m_bonusPack.resetForRespawn(); } return realDamage; } /**********************************************************/ void AvatarWorm::updateSpriteSelection() { if( m_state & Avatar::MODE_GUN ) { // developing check DBG(3) ASSERT( FIRST_STATIONARY_GUN <= m_gunID && LAST_STATIONARY_GUN >= m_gunID, "AvatarWorm::updateSpriteSelection: %d is not a " "valid gun ID in range [%d,%d]\n", m_gunID, FIRST_STATIONARY_GUN, LAST_STATIONARY_GUN ); // set sequence setSequence( m_gunID + (FIRST_GUN_SEQUENCE - FIRST_STATIONARY_GUN) ); setFrame( 0 ); } else if( m_state & Avatar::MODE_GM ) { setSequence( FIRST_RC_SEQUENCE + getDirection() ); setFrame( 0 ); } else { // set the sequence index. // NOTE: setSequence also updates m_SequenceLength. setSequence( NUM_SEQUENCES_PER_DIRECTION * getDirection() + (ROUND( getAimingAngle()) / AIMING_ANGLE_STEP) ); // only reset the frame index, if we cannot continue // this movement in a different aiming angle if( getFrame() >= m_SequenceLength ) setFrame( 0 ); } } /**********************************************************/ AvatarWorm::~AvatarWorm() { } /**********************************************************/