/* $Id: stationarygun.hpp,v 1.24.2.1 2006/01/20 11:33:53 chfreund Exp $ */ #ifndef _STATIONARYGUN_HPP_ #define _STATIONARYGUN_HPP_ /**********************************************************/ #include #include "collidableobject.hpp" #include "map.hpp" #include "weapon.hpp" #include "world.hpp" #include "weapongrenade.hpp" /**********************************************************/ class Avatar; /**********************************************************/ //! base class for stationary weapons class StationaryGun : public CollidableObject { public: StationaryGun(); virtual ~StationaryGun(); ////////////////////////////////////////// //! \name methods concerned with the pilot's entering or leaving //@{ //! place the passed avatar in this gun /*! Attaches the passed avatar to the gun object and sets * the gun's state to OCCUPIED. This method is called from * StationaryGun::update, if the gun detects an avatar at its * check point, and changes only data in the gun (besides the * changes due to the symmetry of the attaching process). * Changes in the passed avatar are bussiness of * Avatar::sitDownInGun, that is called in update, too. * \note The corresponding */ virtual void registerPilot( Avatar *const avatar ); //! release the avatar currently sitting in this gun virtual void releasePilot(); //! get the current pilot of the gun Avatar* getPilot() const { return m_Pilot; } //@} ////////////////////////////////////////// //! \name used weapon, munition /*! A stationary weapon can shoot every projectile provided by * a class derived from class Weapon. It keeps a pointer to an * object of type class Weapon and calls Weapon::shoot (\b Note * that Weapon::shoot ??? Himmel, was wollte ich denn hier noch * schreiben ???). * \note * - the object at the Weapon pointer will be deleted in the * destructor of this class and whenever a new weapon is set * by method StationaryGun::setWeapon. */ //!@{ //! get pointer to the current Weapon object const Weapon* getWeapon() const { return m_Weapon; } //! set a new weapon /*! This method sets the passed weapon \p weapon as current * weapon in the stationary gun. A possibly already present * weapon is removed and deleted. In particular a passed * NULL pointer removes the current weapon, which is exploited * in StationaryGun::removeWeapon. * \note With this method the control of the passed weapon * object is transfered to the gun, i.e. the gun will delete * the weapon. */ void setWeapon( Weapon* const weapon = 0x0 ) { delete m_Weapon; m_Weapon = weapon; // if the weapon launches grenades, randomize grenade types WeaponGrenade* weaponGrenade = dynamic_cast( m_Weapon ); if( weaponGrenade ) { Uint32 type = 0; type |= (m_worldPointer->getRandom().getUint32() & 1 ? Grenade::TIME_TRIGGERED : Grenade::CONTACT_TRIGGERED); type |= (m_worldPointer->getRandom().getUint32() & 1 ? Grenade::SHRAPNELS : Grenade::DIRT ); LOG( 3) INFO( "StationaryGun::setWeapon: set grenade type to %i\n", type ); weaponGrenade->setType( type ); } } //! set a new weapon by weapon ID /*! This method uses Weapon::newWeapon to create a new weapon * object and inserts the created object in the stationary * gun using StationaryGun::setWeapon( Weapon *const ). */ void setWeapon( const Sint32 weaponID ) { setWeapon( Weapon::newWeapon(weaponID) ); } //! set fixed weapon /*! Sets a weapon as fixed weapon. This means that possibly * present probability weights for the selection of random * weapons are removed and it is memorized that the weapon * selection type is FIXED (StationaryGun::OPMode). */ void setFixedWeapon( Weapon* const weapon = 0x0 ) { setWeaponList( 0x0, 0x0 ); setWeapon( weapon ); } //! removes the current gun void removeWeapon() { setWeapon((Weapon*)0x0); } //! set a list of weapons /*! Instead of keeping just one fixed weapon the gun can also * select a weapon from a list of possible weapons. This method * inserts such a list of weapons in the stationary gun. * \param list points to an array containing the possible * weapon types \b and must be terminated with the * weapon type INVALID_WEAPON. So if the list contains * \c n valid weapon IDs, \c list[n] == INVALID_WEAPON. * The method fails, if this list contains more than * NUMBER_OF_WEAPON_IDs valid weapon IDs. * \param weights probability weights for the selection of a * weapon in the list. This array must contain \c n-1 * integers weights. It can also be passed as NULL * pointer, which will cause a uniformly distributed * selection (see also documentation of class * WeightedRandom). * \param mode fixes, \b how a new weapon is selected from the * list passed at \c list. * - StationaryGun::CYCLIC : a call of changeWeapon * will choose the next weapon in the list,cyclicly. * - StationaryGun::RANDOM : each call of changeWeapon * will select a weapon randomly. */ bool setWeaponList( const Sint32 *list, const Sint32 *weights = 0x0, const Sint32 mode = RANDOM ); //! changes Weapon according to the current operation mode virtual void changeWeapon( Weapon* const weapon = 0x0 ); //! //!@} ////////////////////////////////////////// //! \name usage, shooting, aiming angle ... //!@{ //! returns the x offset of the shooting point real getAimingDX() const { return m_Direction * m_aimingLength * SIN_REAL(m_aimingAngle * (M_PI/180.0)); } //! returns the y offset of the shooting point real getAimingDY() const { return m_aimingLength * COS_REAL(m_aimingAngle * (M_PI/180.0)); } //! returns the x coordinate of the shooting point real getShootingPointX() const { return m_shootingPointX; } //! returns the y coordinate of the shooting point real getShootingPointY() const { return m_shootingPointY; } //! Vector getShootingPoint() const { return Vector( getShootingPointX(), getShootingPointY() ); } //! updates the internal shooting point void updateShootingPoint() { m_shootingPointX = getPosX() + getAimingDX(); m_shootingPointY = getPosY() + getAimingDY(); } //! returns a randomly perturbated aiming vector Vector getRandomizedAimingVector( const real maxTremble ) const { const real tremble = (M_PI/180.0) * maxTremble * (0.5 - m_worldPointer->getRandom().getNormedReal()); return Vector( m_Direction * SIN_REAL( m_aimingAngle*(M_PI/180.0)+tremble ), COS_REAL( m_aimingAngle*(M_PI/180.0)+tremble ) ); } //! returns the normalized aiming vector Vector getAimingVector() const { return Vector( m_Direction * SIN_REAL( m_aimingAngle*(M_PI/180.0)), COS_REAL( m_aimingAngle*(M_PI/180.0)) ); } //!@} ////////////////////////////////////////// //! \name special initialisations for stationary guns //@{ //! initializes the gun from the passed item virtual bool initFromItem( const ObjectItem &item ); //! initializes the gun in the color and the flag map /*! This method is responsible for initial entries in both * the color and the flag map (see Object::initInMaps). * This default implementation for all stationary guns * assumes the passes sprite set to have at least one sprite * in sequence [0] for the color map, and the \b corresponding * (in particular this means "... the same number of ...") * sprites in sequence [1] to be drawn as indestructible * flags on the flag map. Therefor a random frame index is * chosen in sequence [0] and the also used to aquire the * flag frame from sequence [1]. Overwrite this method in * derived classes, if you want a different behaviour. */ virtual bool initInMaps( SpriteSet *gfx = NULL ); //! returns the object ID of the chassis /*! The sprite, that is draw on the color map in method * StationaryGun::initInMaps might be different from the * one, that should be drawn during the whole game, e.g. * after all collidable objects to cover them. Therefore * an additional object must be created in the world. This * method returns the object ID of the object, that is * created additionally. Its base version (in this class) * returns INVALID_OBJECT, what will prohibit the creation * of an additional objet. \br * The additionally object is created in method initInMaps * and positioned at the coordinates of the gun itself. */ virtual Sint32 getChassisID() const { return INVALID_OBJECT; } //@} //! update method (reimplementation from class Object) virtual void update(); //////////////////////////////////////////////////// //! \name (de)serialization //@{ virtual Uint32 getSerializeBufferSize() const; virtual void serialize( Uint8*& bufferPointer ) const; virtual void deserialize( Uint8*& bufferPointer ); //@} virtual void dump( std::ostream& out ) const; protected: //////////////////////////////////////////////////// //! \name positioning and setup //@{ //! find initial position starting at \c (X,Y) /*! A stationary gun could be placed by simply fixing its * coordinates to some predefined values. But there might * also be the case that the gun searches an ideal place * starting from a predefined point, for example by "falling * down" until it is grounded at certain points. This method * is called exclusively by initFromItem, which is called at * every object, that is loaded and placed initially in the * world. The starting point is taken from the item (class * ObjectItem) passed to initFromItem. * \note Reimplement this method in your gun, if you want to * place the gun differently from just accepting the passed * coordinates. */ virtual bool findInitialPos( const Sint32 X, const Sint32 Y ); //! returns the relative coordinates of the check point /*! returns the relative (to the gun's coordinates) * coordinates of the point, that is checked to detect the * occupation of the gun. This point is checked in the flag * map in each update. If an avatar is detected here, the * weapon will get occupied. */ virtual const IntVector& getCheckPoint() const = 0; //! adapts the check coordinates to the gun's coordinates void updateCheckCoordinates() { m_checkPointX = getIntPosX() + getCheckPoint().x; m_checkPointY = getIntPosY() + getCheckPoint().y; } //@} ////////////////////////////////////////// //! \name usage, shooting, aiming angle ... //!@{ //! super virile men scoring 'round the cluck virtual void steerByPilot(); //! one shot virtual void doShot(); //!@} //! states of the gun enum State { FREE = 0, OCCUPIED, NUM_STATES }; //! direction enum Direction { LEFT = -1, UNDEFINED = 0, RIGHT = 1 }; //! operation mode enum OPMode { FIXED, CYCLIC, RANDOM }; Sint32 m_State; //!< state of the gun Sint32 m_Direction; //!< direction Sint32 m_checkPointX, //!< X coordinate of check point m_checkPointY; //!< Y coordinate of check point Avatar *m_Pilot; //!< pointer to the current pilot Weapon *m_Weapon; //!< pointer to the currently used weapon //! \name aiming angle, and shooting point /*! \brief Each instance of a stationary gun keeps its own * minimal and maximal aiming angle. This makes sense, because * these value may depend from the aiming direction of the gun * and therefore change with the aiming direction of the gun. * For example, if a gun's range covers the sector from -30 * to +50 in the mathematical plane, this range would change * with a turn to the right (mirroring on the y-axis) +130 to * +210. No matter how you organize and lable the aiming angles * and therefore also the behaviour of the inherited method * updateSpriteSelection, these variables support a dynamical * adaption of the aiming angle range. \br * \b Convention: The aiming angle starts at 0, the barrel * facing directly down and moves up to 180 \b in \b both \b * directions. * \note Of course, in each derived class these three variables * should be initialized with sensible values as soon as * possible. */ //@{ real m_minAimingAngle; //!< minimal aiming angle real m_maxAimingAngle; //!< maximal aiming angle real m_aimingAngle; //!< current aiming angle real m_aimingAngleInc; //!< aiming angle increment / event real m_aimingLength; //!< virtual barrel length real m_shootingPointX; real m_shootingPointY; //@} WeightedRandom m_rnd; Sint32 m_opMode; Sint32 m_listSize; Sint32 m_currWeaponIndex; Sint32 m_weaponList[NUMBER_OF_WEAPON_IDs+1]; }; /**********************************************************/ #endif // _STATIONARYGUN_HPP_