/*** * * Copyright (c) 1996-2002, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ #include "extdll.h" #include "util.h" #include "cbase.h" #include "monsters.h" #include "weapons.h" #include "nodes.h" #include "player.h" #include "gamerules.h" #include "soundent.h" #include "effects.h" #include "rpg.h" #include "shake.h" extern int gmsgRpgViseur; extern int gmsgRpgMenu; extern int gmsgClientDecal; extern int gEvilImpulse101; #if !CLIENT_DLL extern short g_sModelIndexBlastCircle; extern void EnvSmokeCreate( const Vector ¢er, int m_iScale, float m_fFrameRate, int m_iTime, int m_iEndTime ); //------------------------------------------ // // Sauvegardes // //------------------------------------------ // CRpg LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg ); TYPEDESCRIPTION CRpg::m_SaveData[] = { DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_pEntityTarget, FIELD_CLASSPTR ), DEFINE_FIELD( CRpg, m_pEntityLocked, FIELD_CLASSPTR ), DEFINE_FIELD( CRpg, m_flLockTime, FIELD_TIME ), DEFINE_FIELD( CRpg, m_iAmmoType, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_iAmmoRocket, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_iAmmoElectro, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_iAmmoNuclear, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_iMenuState, FIELD_INTEGER ), DEFINE_FIELD( CRpg, m_bLoaded, FIELD_BOOLEAN ), DEFINE_FIELD( CRpg, m_bRpgUpdate, FIELD_BOOLEAN ), DEFINE_FIELD( CRpg, m_flReloadTime, FIELD_FLOAT ), DEFINE_FIELD( CRpg, m_flLastBip, FIELD_TIME ), }; // CRpgRocket LINK_ENTITY_TO_CLASS( rpg_rocket, CRpgRocket ); TYPEDESCRIPTION CRpgRocket::m_SaveData[] = { DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ), DEFINE_FIELD( CRpgRocket, m_pTargetMonster, FIELD_CLASSPTR ), DEFINE_FIELD( CRpgRocket, m_iRocketType, FIELD_INTEGER ), DEFINE_FIELD( CRpgRocket, m_flDiskTime, FIELD_TIME ), DEFINE_FIELD( CRpgRocket, m_flLastRadius, FIELD_FLOAT ), }; IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade ); //------------------------------------------------- //------------------------------------------------ // // CRpgRocket // //------------------------------------------------- //------------------------------------------------- //------------------------------------------------ // // Creation de la roquette // //------------------------------------------------- CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ) { CRpgRocket *pRocket = GetClassPtr( (CRpgRocket *)NULL ); UTIL_SetOrigin( pRocket->pev, vecOrigin ); pRocket->pev->angles = vecAngles; if ( pLauncher != NULL ) //modif de Julien { pRocket->m_pLauncher = pLauncher;// remember what RPG fired me. pRocket->m_iRocketType = pLauncher->m_iAmmoType; } else pRocket->m_pLauncher = NULL; pRocket->Spawn(); pRocket->SetTouch( &CRpgRocket::RocketTouch ); if ( pOwner != NULL ) pRocket->pev->owner = pOwner->edict(); else pRocket->pev->owner = NULL; return pRocket; } //========================================================= //========================================================= void CRpgRocket::Spawn( void ) { Precache(); pev->movetype = MOVETYPE_BOUNCE; pev->solid = SOLID_BBOX; // model if ( m_iRocketType == AMMO_ELECTRO ) SET_MODEL(ENT(pev), "models/rpg_electrocket.mdl" ); else if ( m_iRocketType == AMMO_NUCLEAR ) SET_MODEL(ENT(pev), "models/rpg_nuclearrocket.mdl" ); else SET_MODEL(ENT(pev), "models/rpgrocket.mdl"); UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); UTIL_SetOrigin( pev, pev->origin ); pev->classname = MAKE_STRING( "rpg_rocket" ); SetThink( &CRpgRocket::IgniteThink ); pev->nextthink = gpGlobals->time + 0.1; //modif de Julien : 0.4 pev->angles.x -= 30.0f; UTIL_MakeVectors( pev->angles ); pev->angles.x = -( pev->angles.x + 30.0f ); pev->velocity = gpGlobals->v_forward * 250.0f; pev->gravity = 0.5f; pev->dmg = gSkillData.plrDmgRPG; pev->takedamage = DAMAGE_NO; // fix bug } void CRpgRocket :: Precache( void ) { PRECACHE_MODEL("models/rpgrocket.mdl"); PRECACHE_MODEL("models/rpg_electrocket.mdl"); PRECACHE_MODEL("models/rpg_nuclearrocket.mdl"); PRECACHE_SOUND ("weapons/rocket1.wav"); PRECACHE_SOUND ( "weapons/mortarhit.wav" ); PRECACHE_SOUND ( "weapons/rpg_electrocket.wav" ); m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); m_iDiskTexture = PRECACHE_MODEL("sprites/rpg_disk.spr"); m_sNuclearSprite = PRECACHE_MODEL("sprites/fexplo.spr"); } //------------------------------------------------ // // Explosion de la roquette // //------------------------------------------------- void CRpgRocket :: RocketTouch ( CBaseEntity *pOther ) //modif de Julien { //modif de Julien if ( m_pTargetMonster != NULL && m_pLauncher && m_pLauncher->m_cActiveRockets == 1 ) { if ( pOther == m_pTargetMonster ) m_pLauncher->UpdateCrosshair ( RPG_TEXT_TOUCHE ); else m_pLauncher->UpdateCrosshair ( RPG_TEXT_MANQUE ); m_pLauncher->m_cActiveRockets = 0; } // enflamme le gaz if ( IsInGaz() == TRUE ) { edict_t *pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "player" ); CBaseEntity *pPlayer = CBaseEntity::Instance ( pFind ); pPlayer->m_bFireInGaz = TRUE; } switch ( m_iRocketType ) { case AMMO_ROCKET: { pev->origin = pev->origin - pev->velocity * 0.05; // revient un peu en arriere pour ne pas exploser du sol ExplodeTouch( pOther ); break; } case AMMO_ELECTRO: { // truc affreux pour creer une particule a travers ce message pour les decals MESSAGE_BEGIN( MSG_ALL, gmsgClientDecal ); WRITE_COORD( pev->origin.x ); // xyz source WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); WRITE_COORD( 0 ); // xyz norme WRITE_COORD( 0 ); WRITE_COORD( 0 ); WRITE_CHAR ( 'a' ); // type de texture WRITE_BYTE ( 4 ); // 4 == electro-rocket MESSAGE_END(); SetThink ( &CRpgRocket::ElectroThink ); pev->nextthink = gpGlobals->time + 0.1; m_flDiskTime = gpGlobals->time; m_flLastRadius = 0.0; // pour coller avec la dll client pev->effects = 0; UTIL_ScreenFadeAll ( Vector ( 215, 225, 255 ), 0.3, 0, 80, FFADE_IN ); // fait du bruit pour r EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/rpg_electrocket.wav", 1, ATTN_NONE); break; } case AMMO_NUCLEAR: { // explosion MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z + 200 ); WRITE_SHORT( m_sNuclearSprite ); WRITE_BYTE( 100 ); // scale * 10 WRITE_BYTE( 10 ); // framerate WRITE_BYTE( TE_EXPLFLAG_NONE ); MESSAGE_END(); // cercles MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 600 * 5 ); // reach damage radius over .3 seconds WRITE_SHORT( g_sModelIndexBlastCircle ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( 3 ); // life WRITE_BYTE( 128 ); // width WRITE_BYTE( 0 ); // noise WRITE_BYTE( 255 ); // rgb WRITE_BYTE( 255 ); WRITE_BYTE( 200 ); WRITE_BYTE( 170 ); //brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 32 ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z - 600 * 5 / 2 ); // reach damage radius over .3 seconds WRITE_SHORT( g_sModelIndexBlastCircle ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( 4 ); // life WRITE_BYTE( 48 ); // width WRITE_BYTE( 0 ); // noise WRITE_BYTE( 255 ); // rgb WRITE_BYTE( 255 ); WRITE_BYTE( 200 ); WRITE_BYTE( 170 ); //brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/mortarhit.wav", 1, ATTN_NONE); UTIL_ScreenFadeAll ( Vector ( 220, 220, 220 ), 0.2, 0.3, 130, FFADE_IN ); UTIL_ScreenShakeAll ( pev->origin, 200, 100, 3 ); CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, BIG_EXPLOSION_VOLUME, 3.0 ); // dommages float flRadius = 1000; CBaseEntity *pEntity = NULL; float flDamage = 400; while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, flRadius )) != NULL) { if ( pEntity->pev->takedamage != DAMAGE_NO ) { Vector vecSpot = pEntity->Center(); TraceResult tr; UTIL_TraceLine ( pev->origin, vecSpot, dont_ignore_monsters, ENT(pev), &tr ); if ( tr.flFraction != 1.0 && tr.pHit != pEntity->edict() ) { flDamage = flDamage * (pev->origin - vecSpot).Length() / 3 * flRadius; } pEntity->TakeDamage ( pev, pev, flDamage, DMG_RADIATION ); } } SetThink ( &CBaseEntity::SUB_Remove ); pev->nextthink = gpGlobals->time + 0.1; // d CBaseEntity *pTrigger = UTIL_FindEntityByClassname ( NULL, "trigger_nuclear" ); while ( pTrigger != NULL ) { Vector org = pev->origin, min = pTrigger->pev->absmin, max = pTrigger->pev->absmax; if (( org.x > min.x && org.x < max.x ) && ( org.y > min.y && org.y < max.y ) && ( org.z > min.z && org.z < max.z ) ) { FireTargets ( STRING(pTrigger->pev->target), this, this, USE_ON, 0 ); } pTrigger = UTIL_FindEntityByClassname ( pTrigger, "trigger_nuclear" ); } break; } } pev->movetype = MOVETYPE_NOCLIP; // bug de la trainee de fumee rebondissante pev->solid = SOLID_NOT; pev->velocity = Vector ( 0,0,0 ); STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" ); } int CRpgRocket :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { return 1; } void CRpgRocket :: Killed( entvars_t *pevAttacker, int iGib ) { }; void CRpgRocket :: ElectroThink ( void ) { if ( m_flLastRadius > ELECTRO_DISK_MAX ) { SetThink ( &CBaseEntity::SUB_Remove ); pev->nextthink = gpGlobals->time + 0.1; return; } pev->nextthink = gpGlobals->time + 0.1; float flRadius = ( gpGlobals->time - m_flDiskTime ) * ELECTRO_DISK_SPEED + 1; // +1 pour coller avec la dll client CBaseEntity *pEntity = NULL; float flDamage = 100; while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, flRadius )) != NULL) { if ( pEntity->pev->takedamage != DAMAGE_NO ) { Vector2D vecSpot = ( pev->origin - pEntity->Center() ).Make2D(); if ( vecSpot.Length() > m_flLastRadius && ( ( pEntity->pev->solid == SOLID_BSP && pEntity->pev->mins.z <= pev->origin.z && pEntity->pev->maxs.z >= pev->origin.z ) || ( pEntity->pev->solid != SOLID_BSP && pEntity->pev->absmin.z <= pev->origin.z && pEntity->pev->absmax.z >= pev->origin.z ) ) ) { pEntity->TakeDamage ( pev, pev, flDamage, DMG_SHOCK ); if ( pEntity->IsPlayer () ) { short sens = RANDOM_LONG ( 0, 1 ); sens = sens == 0 ? -1 : 1; pEntity->pev->punchangle.y = 25 * sens; pEntity->pev->punchangle.x = 25 * sens; } } } } m_flLastRadius = flRadius; } //------------------------------------------------ // // Mouvement de la roquette // //------------------------------------------------- void CRpgRocket::IgniteThink( void ) { pev->movetype = MOVETYPE_FLY; if ( m_iRocketType == AMMO_ROCKET ) pev->effects |= EF_LIGHT; // make rocket sound EMIT_SOUND( ENT( pev ), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5f ); if ( m_iRocketType == AMMO_ROCKET || m_iRocketType == AMMO_ELECTRO ) { // rocket trail MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_BEAMFOLLOW ); WRITE_SHORT(entindex()); // entity WRITE_SHORT(m_iTrail ); // model WRITE_BYTE( 40 ); // life WRITE_BYTE( 5 ); // width WRITE_BYTE( 224 ); // r, g, b WRITE_BYTE( 224 ); // r, g, b WRITE_BYTE( 255 ); // r, g, b if ( m_iRocketType == AMMO_ROCKET ) WRITE_BYTE( 255 ); // brightness else WRITE_BYTE( 100 ); // brightness MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) } m_flIgniteTime = gpGlobals->time; // set to follow laser spot SetThink( &CRpgRocket::FollowThink ); pev->nextthink = gpGlobals->time + 0.1f; } void CRpgRocket::FollowThink( void ) { CBaseEntity *pOther = NULL; Vector vecTarget; Vector vecDir; Vector angDir; TraceResult tr; UTIL_MakeAimVectors( pev->angles ); vecTarget = gpGlobals->v_forward; if ( m_pTargetMonster != NULL ) { pOther = m_pTargetMonster; UTIL_TraceLine ( pev->origin, pOther->Center(), dont_ignore_monsters, ENT(pev), &tr ); if( tr.flFraction >= 0.9f ) { vecDir = ( pOther->Center() - pev->origin ).Normalize(); angDir = UTIL_VecToAngles( vecDir ); if ( (CBaseEntity::Instance(pev->owner)) ->IsPlayer() ) vecTarget = vecDir/* * 100*/; else { float flAngDistX = UTIL_AngleDiff ( angDir.x, pev->angles.x ); float flAngDistY = UTIL_AngleDiff ( angDir.y, pev->angles.y ); if ( fabs(flAngDistX) < 45 && fabs(flAngDistY) < 45 ) { if ( fabs(flAngDistX) <= 9 && fabs(flAngDistY) <= 9 ) { vecTarget = vecDir; } else { angDir = pev->angles; int iSens = flAngDistX > 0 ? 1 : -1; angDir.x = angDir.x + flAngDistX / 2 * iSens; iSens = flAngDistY > 0 ? 1 : -1; angDir.y = angDir.y + flAngDistY / 2 * iSens; UTIL_MakeVectors( angDir ); vecTarget = gpGlobals->v_forward; } } else { m_pTargetMonster = NULL; } } } } pev->angles = UTIL_VecToAngles( vecTarget ); // this acceleration and turning math is totally wrong, but it seems to respond well so don't change it. float flSpeed = pev->velocity.Length(); if( gpGlobals->time - m_flIgniteTime < 1.0f ) { pev->velocity = pev->velocity * 0.2f + vecTarget * ( flSpeed * 0.8f + 400.0f ); if( pev->waterlevel == 3 ) { // go slow underwater if( pev->velocity.Length() > 300.0f ) { pev->velocity = pev->velocity.Normalize() * 300.0f; } UTIL_BubbleTrail( pev->origin - pev->velocity * 0.1f, pev->origin, 4 ); } else { if( pev->velocity.Length() > 2000.0f ) { pev->velocity = pev->velocity.Normalize() * 2000.0f; } } } else { if( pev->effects & EF_LIGHT ) { pev->effects = 0; STOP_SOUND( ENT( pev ), CHAN_VOICE, "weapons/rocket1.wav" ); } pev->velocity = pev->velocity * 0.2f + vecTarget * flSpeed * 0.798f; if( pev->waterlevel == 0 && pev->velocity.Length() < 1500.0f ) { //if( CRpg *pLauncher = (CRpg*)( (CBaseEntity*)( m_hLauncher ) ) ) if ( m_pLauncher ) //modif de Julien o para Julien, LOL :D { // my launcher is still around, tell it I'm dead. m_pLauncher->m_cActiveRockets--; //lacked m_ before pLauncher } Detonate(); } } //modif de JUlien if ( CBaseEntity::Instance( pev->owner )->IsPlayer() ) CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin, pev->velocity.Length( ), 0.1 ); pev->nextthink = gpGlobals->time + 0.1f; } #endif //------------------------------------------------- //------------------------------------------------- //------------------------------------------------ // // CRpg // //------------------------------------------------- //------------------------------------------------- //------------------------------------------------- void CRpg::Spawn( ) { Precache( ); m_iId = WEAPON_RPG; SET_MODEL(ENT(pev), "models/w_rpg.mdl"); m_iClip = 1; // empeche le declenchement du rechargement par la classe mere m_iAmmoType = AMMO_ROCKET; m_bLoaded = TRUE; //pas de rechargement la premiere fois m_flReloadTime = -1; // -1 = pas en cours de rechargement pev->body = RPG_WEAPON_ROCKET; m_iMenuState = 0; m_iMenuState |= ( RPG_MENU_ROCKET_SELECTED | RPG_MENU_ROCKET_EMPTY | RPG_MENU_ELECTRO_EMPTY | RPG_MENU_NUCLEAR_EMPTY ); m_iDefaultAmmo = RPG_DEFAULT_GIVE; m_flLastBip = 0; FallInit();// get ready to fall down. } void CRpg::Precache( void ) { PRECACHE_MODEL( "models/w_rpg.mdl" ); PRECACHE_MODEL( "models/v_rpg.mdl" ); PRECACHE_MODEL( "models/p_rpg.mdl" ); PRECACHE_SOUND( "items/9mmclip1.wav" ); PRECACHE_SOUND("sentences/hev_aim_on.wav"); PRECACHE_SOUND("sentences/hev_aim_off.wav"); PRECACHE_SOUND( "weapons/rocketfire1.wav" ); PRECACHE_SOUND( "weapons/glauncher.wav" ); // alternative fire sound PRECACHE_SOUND("weapons/rpg_lock03.wav"); UTIL_PrecacheOther( "rpg_rocket" ); // m_usRpg = PRECACHE_EVENT( 1, "events/rpg.sc" ); //modif de Julien, wasn't in Julien's code, commented out (never understood those sc events anyway) // This event was used in primary attack code, but it got removed anyway. } // /give weapon_rpg int CRpg::GetItemInfo( ItemInfo *p ) { p->pszName = STRING( pev->classname ); p->pszAmmo1 = NULL; //modif de Julien p->iMaxAmmo1 = ROCKET_MAX_CARRY; p->pszAmmo2 = NULL; p->iMaxAmmo2 = -1; p->iMaxClip = RPG_MAX_CLIP; p->iSlot = 3; p->iPosition = 0; p->iId = m_iId = WEAPON_RPG; p->iFlags = 0; p->iWeight = RPG_WEIGHT; return 1; } int CRpg::AddToPlayer( CBasePlayer *pPlayer ) { if( CBasePlayerWeapon::AddToPlayer( pPlayer ) ) { MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev ); WRITE_BYTE( m_iId ); MESSAGE_END(); m_pPlayer->TextAmmo( TA_RPG ); // ajoute la premi AddAmmo ( this, AMMO_ROCKET, 1 ); return TRUE; } return FALSE; } //------------------------------------------ // // Draw / Holster // //------------------------------------------ BOOL CRpg::Deploy() { // d UpdateMenu (); PlayStateSound (); if ( m_iAmmoType == AMMO_ROCKET ) UpdateCrosshair ( RPG_CROSSHAIR_EMPTY ); else UpdateCrosshair ( RPG_CROSSHAIR_NORMAL ); m_flReloadTime = -1; // -1 = pas en cours de rechargement // animation BOOL bResult = DefaultDeploy( "models/v_rpg.mdl", "models/p_rpg.mdl", RPG_DRAW, "rpg" ); m_flNextPrimaryAttack = gpGlobals->time + 8 / 5.0; m_flTimeWeaponIdle = gpGlobals->time + 8 / 5.0; return bResult; } BOOL CRpg::CanHolster( void ) { if ( m_cActiveRockets ) return FALSE; return TRUE; } void CRpg::Holster( int skiplocal ) { m_fInReload = FALSE;// cancel any reload in progress. m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5f; m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 ); // fermeture du hud rpg m_iMenuState |= RPG_CLOSE; UpdateMenu (); m_iMenuState &= ~RPG_CLOSE; } //------------------------------------------ // // Tirs principal et secondaire // //------------------------------------------ void CRpg::PrimaryAttack() { // confirmation de la selection if ( m_iMenuState & RPG_MENU_ACTIVE ) { m_iMenuState &= ~RPG_MENU_ACTIVE; UpdateMenu(); if ( m_iMenuState & RPG_MENU_ROCKET_SELECTED ) { if ( m_iAmmoType != AMMO_ROCKET ) { m_bLoaded = FALSE; m_flTimeWeaponIdle = gpGlobals->time - 0.1; } m_iAmmoType = AMMO_ROCKET; } else if ( m_iMenuState & RPG_MENU_ELECTRO_SELECTED ) { if ( m_iAmmoType != AMMO_ELECTRO ) { m_bLoaded = FALSE; m_flTimeWeaponIdle = gpGlobals->time - 0.1; } m_iAmmoType = AMMO_ELECTRO; } else if ( m_iMenuState & RPG_MENU_NUCLEAR_SELECTED ) { if ( m_iAmmoType != AMMO_NUCLEAR ) { m_bLoaded = FALSE; m_flTimeWeaponIdle = gpGlobals->time - 0.1; } m_iAmmoType = AMMO_NUCLEAR; } // bon viseur ! if ( m_iAmmoType == AMMO_ROCKET ) UpdateCrosshair ( RPG_CROSSHAIR_EMPTY ); else UpdateCrosshair ( RPG_CROSSHAIR_NORMAL ); m_flNextPrimaryAttack = gpGlobals->time + 0.3; } // tir else if ( m_bLoaded == TRUE && ((m_iAmmoType == AMMO_ROCKET && m_iAmmoRocket != 0) || (m_iAmmoType == AMMO_ELECTRO && m_iAmmoElectro != 0) || (m_iAmmoType == AMMO_NUCLEAR && m_iAmmoNuclear != 0) ) ) { // flash et son m_pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME; m_pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH; m_pPlayer->pev->punchangle.x -= 5; EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/rocketfire1.wav", 0.9, ATTN_NORM ); EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/glauncher.wav", 0.7, ATTN_NORM ); // bodygroup pev->body = RPG_WEAPON_EMPTY; #if !CLIENT_DLL // animations // player "shoot" animation SendWeaponAnim( RPG_FIRE ); m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); // roquette UTIL_MakeVectors( m_pPlayer->pev->v_angle ); Vector vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 16.0f + gpGlobals->v_right * 8.0f + gpGlobals->v_up * -8.0f; pRocket = CRpgRocket::CreateRpgRocket( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this ); if ( (m_iAmmoType == AMMO_ROCKET) && ( m_pEntityLocked != NULL ) ) { pRocket->m_pTargetMonster = m_pEntityLocked; m_cActiveRockets = 1; } else { pRocket->m_pTargetMonster = NULL; m_cActiveRockets = 0; // m_cActiveRockets empeche le rechargement } UTIL_MakeVectors( m_pPlayer->pev->v_angle );// RpgRocket::Create stomps on globals, so remake. pRocket->pev->velocity = pRocket->pev->velocity + gpGlobals->v_forward * DotProduct( m_pPlayer->pev->velocity, gpGlobals->v_forward ); #endif // munitions m_bLoaded = FALSE; if ( m_iAmmoType == AMMO_ROCKET ) // A FAIRE : continuer de remplacer les masks par iammotype { m_iAmmoRocket --; if ( m_iAmmoRocket == 0 ) m_iMenuState |= RPG_MENU_ROCKET_EMPTY; } else if ( m_iAmmoType == AMMO_ELECTRO ) { m_iAmmoElectro --; if ( m_iAmmoElectro == 0 ) m_iMenuState |= RPG_MENU_ELECTRO_EMPTY; } else if ( m_iAmmoType == AMMO_NUCLEAR ) { m_iAmmoNuclear --; if ( m_iAmmoNuclear == 0 ) m_iMenuState |= RPG_MENU_NUCLEAR_EMPTY; } // mise a jour du hud UpdateMenu(); m_pEntityTarget = m_pEntityLocked = NULL; m_flLockTime = 0; // prochaine attaque m_flNextPrimaryAttack = GetNextAttackDelay( 1.5f ); m_flTimeWeaponIdle = gpGlobals->time + 1.5f; // weaponidle () declenche le rechargement ResetEmptySound(); } else { PlayEmptySound(); m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.2f; } } void CRpg::SecondaryAttack() { // menu if ( !(m_iMenuState & RPG_MENU_ACTIVE) ) m_iMenuState |= RPG_MENU_ACTIVE; else if ( m_iMenuState & RPG_MENU_ROCKET_SELECTED ) { m_iMenuState &= ~RPG_MENU_ROCKET_SELECTED; if ( !(m_iMenuState & RPG_MENU_ELECTRO_EMPTY) ) m_iMenuState |= RPG_MENU_ELECTRO_SELECTED; else if ( !(m_iMenuState & RPG_MENU_NUCLEAR_EMPTY) ) m_iMenuState |= RPG_MENU_NUCLEAR_SELECTED; else m_iMenuState |= RPG_MENU_ROCKET_SELECTED; } else if ( m_iMenuState & RPG_MENU_ELECTRO_SELECTED ) { m_iMenuState &= ~RPG_MENU_ELECTRO_SELECTED; if ( !(m_iMenuState & RPG_MENU_NUCLEAR_EMPTY) ) m_iMenuState |= RPG_MENU_NUCLEAR_SELECTED; else if ( !(m_iMenuState & RPG_MENU_ROCKET_EMPTY) ) m_iMenuState |= RPG_MENU_ROCKET_SELECTED; else m_iMenuState |= RPG_MENU_ELECTRO_SELECTED; } else if ( m_iMenuState & RPG_MENU_NUCLEAR_SELECTED ) { m_iMenuState &= ~RPG_MENU_NUCLEAR_SELECTED; if ( !(m_iMenuState & RPG_MENU_ROCKET_EMPTY) ) m_iMenuState |= RPG_MENU_ROCKET_SELECTED; else if ( !(m_iMenuState & RPG_MENU_ELECTRO_EMPTY) ) m_iMenuState |= RPG_MENU_ELECTRO_SELECTED; else m_iMenuState |= RPG_MENU_NUCLEAR_SELECTED; } UpdateMenu (); PlayStateSound (); m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.2f; } //------------------------------------------ // // Rechargement // //------------------------------------------ void CRpg::Reload( void ) { if ( m_cActiveRockets < 0 || m_flTimeWeaponIdle > gpGlobals->time ) // weaponidle () declenche le rechargement return; if ( (m_iAmmoType == AMMO_ROCKET && m_iAmmoRocket == 0) || (m_iAmmoType == AMMO_ELECTRO && m_iAmmoElectro == 0) || (m_iAmmoType == AMMO_NUCLEAR && m_iAmmoNuclear == 0) ) return; // bodygroup // en cas de changement de munition, le bodygroup n'a pas pev->body = RPG_WEAPON_EMPTY; // animations int iAnim; switch ( m_iAmmoType ) { case AMMO_ROCKET: iAnim = RPG_RELOAD_ROCKET; m_flReloadTime = gpGlobals->time + 2; break; case AMMO_ELECTRO: iAnim = RPG_RELOAD_ELECTRO; m_flReloadTime = gpGlobals->time + 4; break; case AMMO_NUCLEAR: iAnim = RPG_RELOAD_NUCLEAR; m_flReloadTime = gpGlobals->time + 6; break; } SendWeaponAnim( iAnim ); // validation du rechargement m_flTimeWeaponIdle = gpGlobals->time + 10; m_flNextPrimaryAttack = gpGlobals->time + 10; } void CRpg::WeaponIdle( void ) { // rafraichissement des donnees client if ( m_bRpgUpdate == 1 ) { UpdateMenu (); if ( m_iAmmoType == AMMO_ROCKET ) UpdateCrosshair ( RPG_CROSSHAIR_EMPTY ); else UpdateCrosshair ( RPG_CROSSHAIR_NORMAL ); m_flTimeWeaponIdle = gpGlobals->time - 0.1; // pour charger une anim et changer le bodygroup m_bRpgUpdate = 0; // weaponidle, ca rafraichit, et c'est d } // viseur if ( (m_iAmmoType == AMMO_ROCKET) && ( m_cActiveRockets == 0 ) ) UpdateEntityTarget (); //ALARM! DIFFERENT CODE DETECTED! ResetEmptySound( ); // rechargement if ( m_flTimeWeaponIdle < gpGlobals->time && m_bLoaded == FALSE && m_flReloadTime == -1 ) // -1 = pas en cours de rechargement { Reload (); return; // pour ne pas lancer d anim idle } else if ( m_bLoaded == FALSE && m_flReloadTime != -1 && m_flReloadTime < gpGlobals->time ) { m_bLoaded = TRUE; m_flReloadTime = -1; m_flNextPrimaryAttack = gpGlobals->time; m_flTimeWeaponIdle = gpGlobals->time; } // ajustement du bodygroup if ( m_flTimeWeaponIdle <= gpGlobals->time ) { int ibody; switch ( m_iAmmoType ) { case AMMO_ROCKET: ibody = RPG_WEAPON_ROCKET; break; case AMMO_ELECTRO: ibody = RPG_WEAPON_ELECTRO; break; case AMMO_NUCLEAR: ibody = RPG_WEAPON_NUCLEAR; break; } if ( m_bLoaded == FALSE ) ibody = RPG_WEAPON_EMPTY; pev->body = ibody; } // animations idle if (m_flTimeWeaponIdle > gpGlobals->time) return; int iAnim; switch ( RANDOM_LONG( 0, 6 ) ) { case 0: iAnim = RPG_FIDGET; m_flTimeWeaponIdle = gpGlobals->time + 15 / 4.0; break; default: iAnim = RPG_IDLE; m_flTimeWeaponIdle = gpGlobals->time + 15 / 3.0; break; } SendWeaponAnim( iAnim ); } void CRpg::UpdateEntityTarget( void ) { if ( m_iMenuState & RPG_MENU_ACTIVE ) return; // le texte s'affiche et disparait selon la selection UTIL_MakeVectors( m_pPlayer->pev->v_angle ); Vector vecSrc = m_pPlayer->GetGunPosition( ); Vector vecAiming = gpGlobals->v_forward; TraceResult tr; UTIL_TraceLine ( vecSrc, vecSrc + vecAiming * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr ); //tracage du vecteur if ( tr.pHit && ( CBaseEntity :: Instance ( tr.pHit )) -> MyMonsterPointer() != NULL ) { // si une entite est touchee et //que l'entit if ( m_pEntityTarget == NULL || ( m_pEntityTarget != NULL && ( m_pEntityTarget != (CBaseEntity::Instance(tr.pHit)) ) ) ) { // si il n'y avait pas de cible, on en met une . On met en memoire l heure de reperage de la cible // une cible, mais pas la meme qu avant, ... m_pEntityTarget = CBaseEntity::Instance ( tr.pHit ); m_pEntityLocked = NULL; m_flLockTime = gpGlobals->time; UpdateCrosshair ( RPG_CROSSHAIR_PROCESS ); // son EMIT_SOUND ( ENT(pev), CHAN_BODY, "weapons/rpg_lock03.wav", 0.5, ATTN_NORM ); } else if ( ( m_pEntityTarget != NULL ) && ( m_pEntityTarget == (CBaseEntity::Instance(tr.pHit))) && ( gpGlobals->time - m_flLockTime > 1 ) ) { // la meme cible pendant 1 sec , on verrouille m_pEntityLocked = m_pEntityTarget; UpdateCrosshair ( RPG_CROSSHAIR_LOCKED ); // son if ( m_flLastBip <= 0 || gpGlobals->time - m_flLastBip >= 0.1 ) { EMIT_SOUND ( ENT(pev), CHAN_BODY, "weapons/rpg_lock03.wav", 0.5, ATTN_NORM ); m_flLastBip = gpGlobals->time; } } } else { //si rien n est touche UpdateCrosshair ( RPG_CROSSHAIR_EMPTY ); m_pEntityTarget = m_pEntityLocked = NULL; m_flLockTime = 0; } } void CRpg :: PlayStateSound ( void ) { /* if ( m_iAmmoType == AMMO_ROCKET ) EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "sentences/hev_aim_on.wav", 0.9, ATTN_NORM ); else EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "sentences/hev_aim_off.wav", 0.9, ATTN_NORM ); */ } //------------------------------------------ // // messages au client // //------------------------------------------ void CRpg :: UpdateCrosshair ( int crosshair ) { MESSAGE_BEGIN( MSG_ONE, gmsgRpgViseur, NULL, m_pPlayer->pev ); WRITE_BYTE( crosshair ); MESSAGE_END(); } void CRpg :: UpdateMenu ( void ) { MESSAGE_BEGIN( MSG_ONE, gmsgRpgMenu, NULL, m_pPlayer->pev ); WRITE_BYTE( m_iMenuState ); WRITE_BYTE( m_iAmmoRocket ); WRITE_BYTE( m_iAmmoElectro ); WRITE_BYTE( m_iAmmoNuclear ); MESSAGE_END(); } //------------------------------------------ // // munitions // //------------------------------------------ void CRpg :: ItemTouch( CBaseEntity *pOther ) { if ( pOther->IsPlayer() == FALSE ) { return; } CBasePlayer *pPlayer = (CBasePlayer*)pOther; CBasePlayerItem *pItem; CRpg *pRpg = NULL; for ( int i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pItem = pPlayer->m_rgpPlayerItems[ i ]; while (pItem) { if ( !strcmp( "weapon_rpg", STRING( pItem->pev->classname ) ) ) { pRpg = (CRpg*)pItem->GetWeaponPtr(); break; } pItem = pItem->m_pNext; } } if ( pRpg == NULL ) return; if ( pRpg->AddAmmo ( (CBasePlayerWeapon *) pRpg, AMMO_ROCKET, 1 ) ) { EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); UTIL_Remove( this ); } } int CRpg::ExtractAmmo( CBasePlayerWeapon *pWeapon ) { // AddAmmo ( pWeapon, m_iAmmoType, 1 ); return 1; } int CRpg::ExtractClipAmmo( CBasePlayerWeapon *pWeapon ) { // AddAmmo ( pWeapon, m_iAmmoType, 1 ); return 1; } BOOL CRpg ::AddAmmo ( CBasePlayerWeapon *pWeapon, int iAmmotype, int iNombre ) { int *pType; CRpg *pRpg = (CRpg*) pWeapon; switch ( iAmmotype ) { default: case AMMO_ROCKET: pType = &pRpg->m_iAmmoRocket; pRpg->m_iMenuState &= ~RPG_MENU_ROCKET_EMPTY; break; case AMMO_ELECTRO: pType = &pRpg->m_iAmmoElectro; pRpg->m_iMenuState &= ~RPG_MENU_ELECTRO_EMPTY; break; case AMMO_NUCLEAR: pType = &pRpg->m_iAmmoNuclear; pRpg->m_iMenuState &= ~RPG_MENU_NUCLEAR_EMPTY; break; } if ( pType[0] == RPG_MAX_AMMO ) return FALSE; pType[0] += iNombre; if ( m_pPlayer->m_pActiveItem == pWeapon ) { pRpg->UpdateMenu(); } return TRUE; } int CRpg :: GiveAmmo( int iAmount, char *szName, int iMax ) { // AddAmmo ( this, m_iAmmoType, 1 ); return 1; } class CRpgAmmo : public CBasePlayerAmmo { void Spawn( void ) { Precache(); if ( FClassnameIs( pev, "ammo_rpgclip" ) ) SET_MODEL(ENT(pev), "models/w_rpgclip.mdl"); else if ( FClassnameIs( pev, "ammo_rpgelectroclip" ) ) SET_MODEL(ENT(pev), "models/w_rpgelectroclip.mdl"); else if ( FClassnameIs( pev, "ammo_rpgnuclearclip" ) ) SET_MODEL(ENT(pev), "models/w_rpgnuclearclip.mdl"); CBasePlayerAmmo::Spawn(); } void Precache( void ) { PRECACHE_MODEL ("models/w_rpgclip.mdl"); PRECACHE_MODEL ("models/w_rpgelectroclip.mdl"); PRECACHE_MODEL ("models/w_rpgnuclearclip.mdl"); PRECACHE_SOUND( "items/9mmclip1.wav" ); } BOOL AddAmmo( CBaseEntity *pOther ) { if ( pOther->IsPlayer() == 0 ) return FALSE; CBasePlayer *pPlayer = (CBasePlayer *) pOther; CBasePlayerItem *pItem; CRpg *pRpg = NULL; int i; int fin = 0; for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ ) { pItem = pPlayer->m_rgpPlayerItems[ i ]; while (pItem) { if ( !strcmp( "weapon_rpg"/*&pszItemName*/, STRING( pItem->pev->classname ) ) ) { fin = 1; pRpg = (CRpg*)pItem->GetWeaponPtr(); } pItem = pItem->m_pNext; if ( fin ) break; } if ( fin ) break; } if ( pRpg == NULL ) return FALSE; int iAmmotype = 0; if ( FClassnameIs( pev, "ammo_rpgclip" ) ) iAmmotype = AMMO_ROCKET; else if ( FClassnameIs( pev, "ammo_rpgelectroclip" ) ) iAmmotype = AMMO_ELECTRO; else if ( FClassnameIs( pev, "ammo_rpgnuclearclip" ) ) iAmmotype = AMMO_NUCLEAR; else return FALSE; if ( pRpg->AddAmmo ( (CBasePlayerWeapon *) pRpg, iAmmotype, 1 ) == TRUE ) { // accept switch ( iAmmotype ) { default: case AMMO_ROCKET: break; case AMMO_ELECTRO: pRpg->m_pPlayer->TextAmmo( TA_ELECTROROCKET ); break; case AMMO_NUCLEAR: pRpg->m_pPlayer->TextAmmo( TA_NUCLEARROCKET ); break; } EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM); return TRUE; } else return FALSE; } }; LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo ) LINK_ENTITY_TO_CLASS( ammo_rpgelectroclip, CRpgAmmo ); LINK_ENTITY_TO_CLASS( ammo_rpgnuclearclip, CRpgAmmo ); //------------------------------------------ // // sauvegarde et restauration // //------------------------------------------ #if !CLIENT_DLL int CRpg::Save( CSave &save ) { if ( !CBasePlayerWeapon::Save(save) ) return 0; return save.WriteFields( "CRpg", this, m_SaveData, ARRAYSIZE(m_SaveData) ); } int CRpg::Restore( CRestore &restore ) { if ( !CBasePlayerWeapon::Restore(restore) ) return 0; int status = restore.ReadFields( "CRpg", this, m_SaveData, ARRAYSIZE(m_SaveData) ); //----------------------- m_bRpgUpdate = 1; // force le rafraichissement des donnees client //---------------------- return status; } #endif