//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason ) { m_hPhysicsAttacker = pPhysGunUser; m_flLastPhysicsInfluenceTime = gpGlobals->curtime; m_flTimeGrabbed = FLT_MAX; m_bHeldByPhysgun = false; if( m_iMineState == MINE_STATE_ARMED ) { // Put the mine back to searching. Wake( false ); return; } if( Reason == DROPPED_BY_CANNON ) { // Set to lock down to ground again. m_bPlacedByPlayer = true; OpenHooks( true ); SetMineState( MINE_STATE_DEPLOY ); } else if ( Reason == LAUNCHED_BY_CANNON ) { SetMineState( MINE_STATE_LAUNCHED ); } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::Spawn() { Precache(); Wake( false ); SetModel("models/props_combine/combine_mine01.mdl"); SetSolid( SOLID_VPHYSICS ); m_hSprite.Set( NULL ); m_takedamage = DAMAGE_EVENTS_ONLY; // Find my feet! m_iHookN = LookupPoseParameter( "blendnorth" ); m_iHookE = LookupPoseParameter( "blendeast" ); m_iHookS = LookupPoseParameter( "blendsouth" ); m_iAllHooks = LookupPoseParameter( "blendstates" ); m_flHookPositions = 0; SetHealth( 100 ); m_bBounce = true; SetSequence( SelectWeightedSequence( ACT_IDLE ) ); OpenHooks( true ); m_bHeldByPhysgun = false; m_iFlipAttempts = 0; if( !GetParent() ) { // Create vphysics now if I'm not being carried. CreateVPhysics(); } m_flTimeGrabbed = FLT_MAX; if( m_bDisarmed ) { SetMineState( MINE_STATE_DORMANT ); } else { SetMineState( MINE_STATE_DEPLOY ); } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::InputDisarm( inputdata_t &inputdata ) { // Only affect a mine that's armed and not placed by player. if( !m_bPlacedByPlayer && m_iMineState == MINE_STATE_ARMED ) { if( m_pConstraint ) { physenv->DestroyConstraint( m_pConstraint ); m_pConstraint = NULL; } m_bDisarmed = true; OpenHooks(false); SetMineState(MINE_STATE_DORMANT); } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::SearchThink() { if( !UTIL_FindClientInPVS(edict()) ) { // Sleep! SetNextThink( gpGlobals->curtime + 0.5 ); return; } if( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) ) { if( IsAwake() ) { Wake(false); } SetNextThink( gpGlobals->curtime + 0.5 ); return; } SetNextThink( gpGlobals->curtime + 0.1 ); StudioFrameAdvance(); if( m_pConstraint && gpGlobals->curtime - m_flTimeGrabbed >= 1.0f ) { m_OnPulledUp.FireOutput( this, this ); SetMineState( MINE_STATE_CAPTIVE ); return; } float flNearestNPCDist = FindNearestNPC(); if( flNearestNPCDist <= BOUNCEBOMB_WARN_RADIUS ) { if( !IsAwake() ) { Wake( true ); } } else { if( IsAwake() ) { Wake( false ); } return; } if( flNearestNPCDist <= BOUNCEBOMB_DETONATE_RADIUS && !IsFriend( m_hNearestNPC ) ) { if( m_bBounce ) { SetMineState( MINE_STATE_TRIGGERED ); } else { // Don't pop up in the air, just explode if the NPC gets closer than explode radius. SetThink( &CBounceBomb::ExplodeThink ); SetNextThink( gpGlobals->curtime + m_flExplosionDelay ); } } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::SettleThink() { SetNextThink( gpGlobals->curtime + 0.05 ); StudioFrameAdvance(); if( GetParent() ) { // A scanner or something is carrying me. Just keep checking back. return; } // Not being carried. if( !VPhysicsGetObject() ) { // Probably was just dropped. Get physics going. CreateVPhysics(); if( !VPhysicsGetObject() ) { Msg("**** Can't create vphysics for combine_mine!\n" ); UTIL_Remove( this ); return; } VPhysicsGetObject()->Wake(); return; } if( !m_bDisarmed ) { if( VPhysicsGetObject()->IsAsleep() && !(VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) ) { // If i'm not resting on the world, jump randomly. trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1024 ), MASK_SHOT|CONTENTS_GRATE, this, COLLISION_GROUP_NONE, &tr ); bool bHop = false; if( tr.m_pEnt ) { IPhysicsObject *pPhysics = tr.m_pEnt->VPhysicsGetObject(); if( pPhysics && pPhysics->GetMass() <= 1000 ) { // Light physics objects can be moved out from under the mine. bHop = true; } else if( tr.m_pEnt->m_takedamage != DAMAGE_NO ) { // Things that can be harmed can likely be broken. bHop = true; } if( bHop ) { Vector vecForce; vecForce.x = random->RandomFloat( -1000, 1000 ); vecForce.y = random->RandomFloat( -1000, 1000 ); vecForce.z = 2500; AngularImpulse torque( 160, 0, 160 ); Flip( vecForce, torque ); return; } // Check for upside-down Vector vecUp; GetVectors( NULL, NULL, &vecUp ); if( vecUp.z <= 0.8 ) { // Landed upside down. Right self Vector vecForce( 0, 0, 2500 ); Flip( vecForce, AngularImpulse( 60, 0, 0 ) ); return; } } // Check to make sure I'm not in a forbidden location if( !IsValidLocation() ) { return; } // Lock to what I'm resting on constraint_ballsocketparams_t ballsocket; ballsocket.Defaults(); ballsocket.constraint.Defaults(); ballsocket.constraint.forceLimit = lbs2kg(1000); ballsocket.constraint.torqueLimit = lbs2kg(1000); ballsocket.InitWithCurrentObjectState( g_PhysWorldObject, VPhysicsGetObject(), GetAbsOrigin() ); m_pConstraint = physenv->CreateBallsocketConstraint( g_PhysWorldObject, VPhysicsGetObject(), NULL, ballsocket ); CloseHooks(); SetMineState( MINE_STATE_ARMED ); } } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::Spawn() { Precache(); Wake( false ); SetModel("models/props_combine/combine_mine01.mdl"); SetSolid( SOLID_VPHYSICS ); m_hSprite.Set( NULL ); m_takedamage = DAMAGE_EVENTS_ONLY; // Find my feet! m_iHookN = LookupPoseParameter( "blendnorth" ); m_iHookE = LookupPoseParameter( "blendeast" ); m_iHookS = LookupPoseParameter( "blendsouth" ); m_iAllHooks = LookupPoseParameter( "blendstates" ); m_flHookPositions = 0; SetHealth( 100 ); m_bBounce = true; SetSequence( SelectWeightedSequence( ACT_IDLE ) ); OpenHooks( true ); m_bHeldByPhysgun = false; m_iFlipAttempts = 0; if( !GetParent() ) { // Create vphysics now if I'm not being carried. CreateVPhysics(); } m_flTimeGrabbed = FLT_MAX; if( m_bDisarmed ) { SetMineState( MINE_STATE_DORMANT ); } else { SetMineState( MINE_STATE_DEPLOY ); } // default to a different skin for cavern turrets (unless explicitly overridden) if ( m_iModification == MINE_MODIFICATION_CAVERN ) { // look for this value in the first datamap // loop through the data description list, restoring each data desc block datamap_t *dmap = GetDataDescMap(); bool bFoundSkin = false; // search through all the readable fields in the data description, looking for a match for ( int i = 0; i < dmap->dataNumFields; ++i ) { if ( dmap->dataDesc[i].flags & (FTYPEDESC_OUTPUT | FTYPEDESC_KEY) ) { if ( !Q_stricmp(dmap->dataDesc[i].externalName, "Skin") ) { bFoundSkin = true; break; } } } if (!bFoundSkin) { // select a random skin for the mine. Actually, we'll cycle through the available skins // using a static variable to provide better distribution. The static isn't saved but // really it's only cosmetic. static unsigned int nextSkin = MINE_CITIZEN_SKIN_MIN; m_nSkin = nextSkin; // increment the skin for next time nextSkin = (nextSkin >= MINE_CITIZEN_SKIN_MAX) ? MINE_CITIZEN_SKIN_MIN : nextSkin + 1; } // pretend like the player set me down. m_bPlacedByPlayer = true; } }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) { m_hPhysicsAttacker = pPhysGunUser; m_flLastPhysicsInfluenceTime = gpGlobals->curtime; m_iFlipAttempts = 0; if( reason != PUNTED_BY_CANNON ) { if( m_iMineState == MINE_STATE_ARMED ) { // Yanking on a mine that is locked down, trying to rip it loose. UpdateLight( true, 255, 255, 0, 190 ); m_flTimeGrabbed = gpGlobals->curtime; m_bHeldByPhysgun = true; VPhysicsGetObject()->EnableMotion( true ); // Try to scatter NPCs without panicking them. Make a move away sound up around their // ear level. CSoundEnt::InsertSound( SOUND_MOVE_AWAY, GetAbsOrigin() + Vector( 0, 0, 60), 32.0f, 0.2f ); return; } else { // Picked up a mine that was not locked down. m_bHeldByPhysgun = true; if( m_iMineState == MINE_STATE_TRIGGERED ) { // This mine's already set to blow. Player can't place it. return; } else { m_bDisarmed = false; SetMineState( MINE_STATE_DEPLOY ); } } } else { m_bHeldByPhysgun = false; } if( reason == PUNTED_BY_CANNON ) { if( m_iMineState == MINE_STATE_TRIGGERED || m_iMineState == MINE_STATE_ARMED ) { // Already set to blow return; } m_bDisarmed = false; m_bPlacedByPlayer = true; SetTouch( NULL ); SetThink( &CBounceBomb::SettleThink ); SetNextThink( gpGlobals->curtime + 0.1); // Since being punted causes the mine to flip, sometimes it 'catches an edge' // and ends up touching the ground from whence it came, exploding instantly. // This little stunt prevents that by ignoring world collisions for a very short time. m_flIgnoreWorldTime = gpGlobals->curtime + 0.1; } }