/* $Id: stationarygun.cpp,v 1.25.2.1 2006/01/20 11:33:53 chfreund Exp $ */ #include "avatar.hpp" #include "map.hpp" #include "world.hpp" #include "mapstuffset.hpp" #include "stationarygun.hpp" #include "weightedrandom.hpp" #include "weaponmissile.hpp" #include "weaponhomingmissile.hpp" #include "weaponhook.hpp" /**********************************************************/ StationaryGun::StationaryGun() : m_State(FREE), m_Direction(UNDEFINED), m_checkPointX(0), m_checkPointY(0), m_Pilot(0x0), m_Weapon(0x0), m_minAimingAngle(0), m_maxAimingAngle(0), m_aimingAngle(m_minAimingAngle), m_aimingAngleInc(0), m_aimingLength(0), m_shootingPointX(0), m_shootingPointY(0), m_rnd( localRnd.getUint32() & 0xffff ), m_opMode(FIXED), m_listSize(0), m_currWeaponIndex(-1) { // initialize the first entry in the weapon list to // a non-valid value m_weaponList[0] = INVALID_WEAPON; } /**********************************************************/ StationaryGun::~StationaryGun() { removeWeapon(); } /**********************************************************/ bool StationaryGun::initFromItem( const ObjectItem &item ) { if( ! CollidableObject::initFromItem(item) || ! findInitialPos(getIntPosX(), getIntPosY() ) ) return false; return true; } /**********************************************************/ void StationaryGun::registerPilot( Avatar *const avatar ) { DBG(3) ASSERT( avatar->getPlayer(), "StationaryGun::registerPilot: avatar is not " "attached to its player\n" ); LOG(2) INFO( "StationaryGun::registerPilot: %d:\"%s\" entered " "\"%s\"\n", avatar->getPlayer()->getPlayerID(), avatar->getPlayer()->getName().getString(), getIDString() ); attachTo( avatar ); m_Pilot = avatar; m_State = OCCUPIED; } /**********************************************************/ void StationaryGun::releasePilot() { // developing debug check DBG(3) ASSERT( m_Pilot, "StationaryGun::releasePilot: " "pilot pointer == 0x0\n" ); // info about function call LOG(2) INFO( "StationaryGun::releasePilot: %d:\"%s\" left " "\"%s\"\n", m_Pilot->getPlayer() ? m_Pilot->getPlayer()->getPlayerID() : -1, m_Pilot->getPlayer() ? m_Pilot->getPlayer()->getName().getString() : "undefined (might leave the world)", getIDString() ); detachFrom( m_Pilot ); m_Pilot = 0x0; m_State = FREE; } /**********************************************************/ bool StationaryGun::setWeaponList( const Sint32 *list, const Sint32 *weights, const Sint32 mode ) { const char fn[] = "StationaryGun::setWeaponList: "; // get number of weapons in the list int length = 0; while( (m_weaponList[length] = list[length]) != INVALID_WEAPON ) length++; ASSERT( length <= NUMBER_OF_WEAPON_IDs, "%s weapon list contains more that NUMBER_OF_WEAPON_IDs " "= %d weapon IDs\n", fn, NUMBER_OF_WEAPON_IDs ); const int *intWeights = reinterpret_cast( weights ); // convert Sint32 to int, if necessary if( weights && sizeof(Sint32) != sizeof(int) ) { intWeights = new int [length]; for( int i = 0; i < length; i++ ) { ((int*)intWeights)[i] = static_cast(weights[i]); } } LOG(3) { INFO( "%s new weapon list:\n", fn ); for( int i = 0; i < length; i++ ) { INFO( " -> %15s : %d\n", Weapon::getIDString(list[i]), weights ? weights[i] : 1 ); } } // set random weights in random number generator ASSERT( m_rnd.setWeights(length, intWeights), "%s could not insert weights\n", fn ); if( intWeights != weights ) delete [] intWeights; // set the operation mode m_opMode = mode; if( !CHECK(mode == CYCLIC || mode == RANDOM, "StationaryGun::setWeaponList: the operation mode can " "be specified in this method only as CYCLIC (%d) or " "RANDOM (%d), but specified %d. Using default: RANDOM\n", CYCLIC, RANDOM, mode) ) { m_opMode = RANDOM; } return true; } /**********************************************************/ void StationaryGun::changeWeapon( Weapon* const weapon ) { // If a certain weapon is specified or we do not have a // valid weapon list, set the passed weapon. NOTE that // the weapon will be removed in the latter case. Also NOTE // that the FIXED operation mode should be covered by // this case, since there should not exist a valid weapon // list in FIXED mode. if( weapon || m_weaponList[0] == INVALID_WEAPON ) { setWeapon( weapon ); // set weapon according to the operating mode of the gun } else { // In CYCLIC operation mode simply select the next weapon // in the list. if( m_opMode == CYCLIC ) { if( ++m_currWeaponIndex >= m_currWeaponIndex ) { m_currWeaponIndex = 0; } // In RANDOM operation mode select a weapon from the list // randomly. } else if( m_opMode == RANDOM ) { m_currWeaponIndex = m_rnd.getWeightedUint32(); } setWeapon( m_weaponList[m_currWeaponIndex] ); } LOG(3) INFO( "StationaryGun::changeWeapon: new weapon \"%s\"\n", m_Weapon ? m_Weapon->getIDString() : "INVALID_WEAPON" ); } /**********************************************************/ bool StationaryGun::initInMaps( SpriteSet *gfx ) { const char fn[] = "StationaryGun::initInMaps:"; // check the pointer ASSERT( gfx, "%s passed pointer to graphics is NULL\n", fn ); // check convention (see documentation of this method) ASSERT( gfx->getSize() >= 2, "%s per convention StationaryGun expects at least " "two sequences\n", fn ); // check the convention that ??? ASSERT( (*gfx)[0]->getSize() == (*gfx)[1]->getSize(), "%s the number of frames in the first two sequences " "must match (here s[0]:%d, s[1]:%d)\n", fn, (*gfx)[0]->getSize(), (*gfx)[1]->getSize() ); // select a random frame from the first sequence const int iFrame = localRnd.getUint32() % (*gfx)[0]->getSize(); // draw this frame as map stuff Map *const map = m_worldPointer->getMap(); ASSERT( map, "%s called on client\n", fn ); map->placeMapStuffItem( (*(*gfx)[0])[iFrame], (*(*gfx)[1])[iFrame], getIntPosX(), getIntPosY(), MapStuffSet::INDESTRUCTIBLE, false ); // create a noncollidable object for the chassis to be drawn // after all collidable objects if( getChassisID() != INVALID_OBJECT ) { Object *chassis = m_worldPointer->newObject( getChassisID() ); chassis->setPos( m_pos ); chassis->setFrame( iFrame ); } return true; } /**********************************************************/ void StationaryGun::update() { switch( m_State ) { case FREE: { // check, whether there is an object at the check point if( m_worldPointer->getMap()->getFlags() ->getValueAt( m_checkPointX, m_checkPointY ) & Flags::OBSTACLE_OBJECT ) { // get a pointer to the object at the check point CollidableObject* obj = m_worldPointer->getCollidableAt( m_checkPointX, m_checkPointY ); // test, if the object is an avatar Avatar *const avatar = dynamic_cast(obj); if( avatar ) { if( !avatar->isJumping() ) { avatar->sitDownInGun( this ); } } } break; } case OCCUPIED: { // No pilot, but gun is OCCUPIED? This could happen after // a deserialisation. In this case we have got to find the // occupying avatar in the list of attached objects. if( ! m_Pilot ) { for( int i = 0; i < getNAttachedObjects(); i++ ) { if( 0x0 != dynamic_cast (getAttachedObject(i)) ) { m_Pilot = dynamic_cast (getAttachedObject(i)); break; } } } // Check, whether the check point is still occupied. // We have to do this check, because the avatar could // have left the gun involuntarily :) if( !(m_worldPointer->getMap()->getFlags() ->getValueAt( m_checkPointX, m_checkPointY ) & Flags::OBSTACLE_OBJECT) ) { // Let the avatar leave the gun actively. This will // also call this->releasePilot() m_Pilot->leaveCurrentGun(); break; } // steerByPilot(); break; } default: break; } updateSpriteSelection(); } /**********************************************************/ void StationaryGun::serialize( Uint8*& bufferPointer ) const { // expands to a check of the buffer movement START_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer ); CollidableObject::serialize( bufferPointer ); Serialize::serialize( m_State, bufferPointer ); Serialize::serialize( m_Direction, bufferPointer ); Serialize::serialize( m_checkPointX, bufferPointer ); Serialize::serialize( m_checkPointY, bufferPointer ); Serialize::serialize( m_minAimingAngle, bufferPointer ); Serialize::serialize( m_maxAimingAngle, bufferPointer ); Serialize::serialize( m_aimingAngle, bufferPointer ); Serialize::serialize( m_aimingAngleInc, bufferPointer ); Serialize::serialize( m_aimingLength, bufferPointer ); Serialize::serialize( m_shootingPointX, bufferPointer ); Serialize::serialize( m_shootingPointY, bufferPointer ); m_rnd.serialize( bufferPointer ); Serialize::serialize( m_opMode, bufferPointer ); Serialize::serialize( m_listSize, bufferPointer ); Serialize::serialize( m_currWeaponIndex, bufferPointer ); Serialize::serialize( m_listSize, (Sint32*)m_weaponList, bufferPointer ); Sint32 currWeaponID = m_Weapon ? m_Weapon->getID() : INVALID_WEAPON; Serialize::serialize( currWeaponID, bufferPointer ); if( m_Weapon ) m_Weapon->serialize( bufferPointer ); // expands to tag serialization SERIALIZE_OBJECT_TAG( bufferPointer ); // expands to a check of the buffer movement END_OBJECT_SERIALIZED_SIZE_CHECK( bufferPointer, StationaryGun ); } /**********************************************************/ void StationaryGun::deserialize( Uint8*& bufferPointer ) { CollidableObject::deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, m_State ); Serialize::deserialize( bufferPointer, m_Direction ); Serialize::deserialize( bufferPointer, m_checkPointX ); Serialize::deserialize( bufferPointer, m_checkPointY ); Serialize::deserialize( bufferPointer, m_minAimingAngle ); Serialize::deserialize( bufferPointer, m_maxAimingAngle ); Serialize::deserialize( bufferPointer, m_aimingAngle ); Serialize::deserialize( bufferPointer, m_aimingAngleInc ); Serialize::deserialize( bufferPointer, m_aimingLength ); Serialize::deserialize( bufferPointer, m_shootingPointX ); Serialize::deserialize( bufferPointer, m_shootingPointY ); m_rnd.deserialize( bufferPointer ); Serialize::deserialize( bufferPointer, m_opMode ); Serialize::deserialize( bufferPointer, m_listSize ); Serialize::deserialize( bufferPointer, m_currWeaponIndex ); Serialize::deserialize( m_listSize, bufferPointer, m_weaponList ); Sint32 currWeaponID; Serialize::deserialize( bufferPointer, currWeaponID ); if( currWeaponID != INVALID_WEAPON ) { setWeapon( currWeaponID ); m_Weapon->deserialize( bufferPointer ); } // expands to tag deserialization DESERIALIZE_OBJECT_TAG( bufferPointer ); } /**********************************************************/ Uint32 StationaryGun::getSerializeBufferSize() const { return CollidableObject::getSerializeBufferSize() + Serialize::sizeOf( m_State ) + Serialize::sizeOf( m_Direction ) + Serialize::sizeOf( m_checkPointX ) + Serialize::sizeOf( m_checkPointY ) + Serialize::sizeOf( m_minAimingAngle ) + Serialize::sizeOf( m_maxAimingAngle ) + Serialize::sizeOf( m_aimingAngle ) + Serialize::sizeOf( m_aimingAngleInc ) + Serialize::sizeOf( m_aimingLength ) + Serialize::sizeOf( m_shootingPointX ) + Serialize::sizeOf( m_shootingPointY ) + m_rnd.getSerializeBufferSize() + Serialize::sizeOf( m_opMode ) + Serialize::sizeOf( m_listSize ) + Serialize::sizeOf( m_currWeaponIndex ) + m_listSize * Serialize::sizeOf() + Serialize::sizeOf() // current weapon ID + (m_Weapon ? m_Weapon->getSerializeBufferSize() : 0) // adds additional size for debugging (see serialize.hpp), // but only, if the tags are used PLUS_TAG_SIZE( 1 ); } /**********************************************************/ bool StationaryGun::findInitialPos( const Sint32 X, const Sint32 Y ) { setPos( X, Y ); updateCheckCoordinates(); updateShootingPoint(); return true; } /**********************************************************/ void StationaryGun::steerByPilot() { DBG(3) ASSERT( m_Pilot, "StationaryGun::steerByAvatar: " "no avatar in gun \"%s\", state: %d\n", getIDString(), m_State ); // enable required states; should be save to enable all m_Pilot->setStateMask( ~0 ); if( m_Pilot->isShooting() ) { doShot(); } // the pilot is aiming up if( m_Pilot->isAimingUp() ) { if( (m_aimingAngle += m_aimingAngleInc) > m_maxAimingAngle ) { m_aimingAngle = m_maxAimingAngle; } updateShootingPoint(); // the pilot is aiming down } else if( m_Pilot->isAimingDown() ) { if( (m_aimingAngle -= m_aimingAngleInc) < m_minAimingAngle ) { m_aimingAngle = m_minAimingAngle; } updateShootingPoint(); } } /**********************************************************/ void StationaryGun::doShot() { DBG(3) if( !CHECK(m_Weapon, "StationaryGun::doShot(): " "no weapon present\n") ) { return; } m_Weapon->rechargeNow(); m_Weapon->shoot( m_worldPointer, m_Pilot, this ); } /**********************************************************/ void StationaryGun::dump( std::ostream& out ) const { using namespace std; out << "### StationaryGun object ###" << endl; CollidableObject::dump( out ); out << "StationaryGun components:" << endl; out << "\tAiming angle: " << m_aimingAngle << endl; out << "\tAiming angle increment: " << m_aimingAngleInc << endl; out << "\tAiming length: " << m_aimingLength << endl; out << "\tShooting point x: " << m_shootingPointX << endl; out << "\tShooting point y: " << m_shootingPointY << endl; }