/* ================ FireBullets Go to the trouble of combining multiple pellets into a single damage call. This version is used by Monsters. ================ */ void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int inDamageType) { static int tracerCount; int tracer; TraceResult tr; Vector vecRight = gpGlobals->v_right; Vector vecUp = gpGlobals->v_up; if ( pevAttacker == NULL ) pevAttacker = pev; // the default attacker is ourselves ClearMultiDamage(); gMultiDamage.type = inDamageType; for (ULONG iShot = 1; iShot <= cShots; iShot++) { // get circular gaussian spread float x, y, z; do { x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5); z = x*x+y*y; } while (z > 1); Vector vecDir = vecDirShooting + x * vecSpread.x * vecRight + y * vecSpread.y * vecUp; Vector vecEnd; vecEnd = vecSrc + vecDir * flDistance; //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); bool theProtected = false; AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); if(theProtected && theEntityHit) { // joev: experiment EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); // :joev } else { tracer = 0; if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) { Vector vecTracerSrc; if ( IsPlayer() ) {// adjust tracer position for player vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; } else { vecTracerSrc = vecSrc; } if ( iTracerFreq != 1 ) // guns that always trace also always decal tracer = 1; switch( iBulletType ) { case BULLET_MONSTER_MP5: case BULLET_MONSTER_9MM: case BULLET_MONSTER_12MM: default: MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); WRITE_BYTE( TE_TRACER ); WRITE_COORD( vecTracerSrc.x ); WRITE_COORD( vecTracerSrc.y ); WRITE_COORD( vecTracerSrc.z ); WRITE_COORD( tr.vecEndPos.x ); WRITE_COORD( tr.vecEndPos.y ); WRITE_COORD( tr.vecEndPos.z ); MESSAGE_END(); break; } } // do damage, paint decals if (tr.flFraction != 1.0) { float theScalar = 1.0f; if(theEntityHit && GetGameRules()->CanEntityDoDamageTo(this, theEntityHit, &theScalar)) { // Multiply damage by scalar for tourny mode, etc. iDamage *= theScalar; if ( iDamage ) { theEntityHit->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); //DecalGunshot( &tr, 0 ); } else switch(iBulletType) { default: case BULLET_MONSTER_9MM: theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); break; case BULLET_MONSTER_MP5: theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); break; case BULLET_MONSTER_12MM: theEntityHit->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); if ( !tracer ) { TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); DecalGunshot( &tr, iBulletType ); } break; case BULLET_NONE: // FIX theEntityHit->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); // only decal glass if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) { UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); } break; } } } // make bullet trails UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); } } ApplyMultiDamage(pev, pevAttacker); }
/* ================ FireBullets Go to the trouble of combining multiple pellets into a single damage call. This version is used by Players, uses the random seed generator to sync client and server side shots. ================ */ Vector CBaseEntity::FireBulletsPlayer ( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker, int shared_rand ) { static int tracerCount; TraceResult tr; Vector vecRight = gpGlobals->v_right; Vector vecUp = gpGlobals->v_up; entvars_t* theInflictor = this->pev; //float x, y, z; if ( pevAttacker == NULL ) pevAttacker = pev; // the default attacker is ourselves int theDamageType = DMG_BULLET; bool isShotgun=false; AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this); if(thePlayer && thePlayer->m_pActiveItem) { AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast<AvHBasePlayerWeapon*>(thePlayer->m_pActiveItem); if(theBasePlayerWeapon) { theDamageType = theBasePlayerWeapon->GetDamageType(); theInflictor = theBasePlayerWeapon->pev; } AvHSonicGun* theSonicGun = dynamic_cast<AvHSonicGun*>(thePlayer->m_pActiveItem); if ( theSonicGun ) { isShotgun=true; } } else { AvHTurret* theTurret = dynamic_cast<AvHTurret*>(this); if(theTurret) { theDamageType = theTurret->GetDamageType(); } } ClearMultiDamage(); gMultiDamage.type = theDamageType; for ( ULONG iShot = 1; iShot <= cShots; iShot++ ) { //Use player's random seed. // get circular gaussian spread //x = UTIL_SharedRandomFloat( shared_rand + iShot, -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 1 + iShot ) , -0.5, 0.5 ); //y = UTIL_SharedRandomFloat( shared_rand + ( 2 + iShot ), -0.5, 0.5 ) + UTIL_SharedRandomFloat( shared_rand + ( 3 + iShot ), -0.5, 0.5 ); //z = x * x + y * y; // //Vector vecDir = vecDirShooting + // x * vecSpread.x * vecRight + // y * vecSpread.y * vecUp; Vector vecDir = UTIL_GetRandomSpreadDir(shared_rand, iShot, vecDirShooting, vecRight, vecUp, vecSpread); Vector vecEnd; vecEnd = vecSrc + vecDir * flDistance; //UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); bool theProtected = false; AvHSUServerTraceBullets(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev), tr, theProtected); CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit); if(theProtected) { // joev: experiment EMIT_SOUND(theEntityHit->edict(), CHAN_AUTO, kUmbraBlockedSound, 1.0f, ATTN_NORM); // :joev } else { // do damage, paint decals if (tr.flFraction != 1.0) { float theScalar = 1.0f; if(GetGameRules()->CanEntityDoDamageTo(thePlayer, theEntityHit, &theScalar)) { int theAdjustedDamage = iDamage*theScalar; if(theAdjustedDamage) { theEntityHit->TraceAttack(pevAttacker, theAdjustedDamage, vecDir, &tr, theDamageType | ((theAdjustedDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); } } } // make bullet trails UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); } } ApplyMultiDamage(theInflictor, pevAttacker); //return Vector( (float)(x * vecSpread.x), (float)(y * vecSpread.y), 0.0f ); return vecSpread; }
//========================================================= // CheckTraceHullAttack - expects a length to trace, amount // of damage to do, and damage type. Returns a pointer to // the damaged entity in case the monster wishes to do // other stuff to the victim (punchangle, etc) // // Used for many contact-range melee attacks. Bites, claws, etc. //========================================================= CBaseEntity* CBaseMonster :: CheckTraceHullAttack( float flDist, float& ioDamage, int iDmgType ) { Vector vecStart = pev->origin; if(this->IsPlayer()) { // Melee attacks always originate where the view is vecStart.x += this->pev->view_ofs.x; vecStart.y += this->pev->view_ofs.y; vecStart.z += this->pev->view_ofs.z; } if (IsPlayer()) UTIL_MakeVectors( pev->v_angle ); else UTIL_MakeAimVectors( pev->angles ); //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecStart); // First do a tracehull. If that misses, try three tracelines (dead-on center, then randomly left and randomly right). bool theHitTarget = false; for(int i = 0; (i < 4) && !theHitTarget; i++) { const float kAmount = 0.4f; float theXAmount = 0.0f; if(i == 2) { theXAmount = kAmount; } else if(i == 3) { theXAmount = -kAmount; } Vector vecDir = gpGlobals->v_forward + theXAmount*gpGlobals->v_right; vecDir.Normalize(); Vector vecEnd = vecStart + (vecDir * flDist ); TraceResult tr; if(i == 0) { int theOurHull = this->GetHull(); int theHull = AvHSUGetValveHull(theOurHull); UTIL_TraceHull(vecStart, vecEnd, dont_ignore_monsters, theHull, this->edict(), &tr); } else { //AvHSUPlayParticleEvent("JetpackEffect", this->edict(), vecEnd); UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, dont_ignore_glass, ENT(this->pev), &tr); } if ( tr.pHit ) { CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit ); if ( ioDamage > 0 ) { float theScalar = 1.0f; if(GetGameRules()->CanEntityDoDamageTo(this, pEntity, &theScalar)) { theHitTarget = true; // Multiply damage by scalar for tourny mode, etc. ioDamage *= theScalar; entvars_t* theInflictor = this->pev; AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this); if(thePlayer) { AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast<AvHBasePlayerWeapon*>(thePlayer->m_pActiveItem); if(theBasePlayerWeapon) { theInflictor = theBasePlayerWeapon->pev; } } pEntity->TakeDamage(theInflictor, pev, ioDamage, iDmgType ); // Spawn blood if(ioDamage > 0.0f) { // a little surface blood. SpawnBlood(tr.vecEndPos, pEntity->BloodColor(), ioDamage); // on the wall/floor (don't play because blood decal is green) //TraceBleed(ioDamage, vecDir, &tr, iDmgType); } return pEntity; } } } } return NULL; }
void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float inDamage, float flRadius, int iClassIgnore, int bitsDamageType ) { CBaseEntity *pEntity = NULL; TraceResult tr; float flAdjustedDamage, falloff; Vector vecSpot; if ( flRadius ) falloff = inDamage / flRadius; else falloff = 1.0; int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER); vecSrc.z += 1;// in case grenade is lying on the ground if ( !pevAttacker ) pevAttacker = pevInflictor; // iterate on all entities in the vicinity. while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) { // NOTE: Should this be inflictor or attacker? CBaseEntity* theInflictingEntity = CBaseEntity::Instance(pevInflictor); CBaseEntity* theAttackingEntity = CBaseEntity::Instance(pevAttacker); float theScalar = 1.0f; bool aCanDamage=GetGameRules()->CanEntityDoDamageTo(theAttackingEntity, pEntity, &theScalar) || theInflictingEntity->pev->classname == MAKE_STRING(kwsDeployedMine);; bool iCanDamage=GetGameRules()->CanEntityDoDamageTo(theInflictingEntity, pEntity, &theScalar); if(pEntity && ( aCanDamage && iCanDamage )) { // Multiply damage by scalar for tourny mode, etc. float theDamage = inDamage*theScalar; if ( pEntity->pev->takedamage != DAMAGE_NO ) { // UNDONE: this should check a damage mask, not an ignore if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) {// houndeyes don't hurt other houndeyes with their attack continue; } // blast's don't tavel into or out of water if (bInWater && pEntity->pev->waterlevel == 0) continue; if (!bInWater && pEntity->pev->waterlevel == 3) continue; vecSpot = pEntity->BodyTarget( vecSrc ); // Clear pevInflictor's owner temporarily so it can apply damage to it edict_t* theInflictorOwner = pevInflictor->owner; pevInflictor->owner = NULL; UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); // Restore owner pevInflictor->owner = theInflictorOwner; if (tr.flFraction == 1.0 || tr.pHit == pEntity->edict() ) {// the explosion can 'see' this entity, so hurt them! if (tr.fStartSolid) { // if we're stuck inside them, fixup the position and distance tr.vecEndPos = vecSrc; tr.flFraction = 0.0; } // decrease damage for an ent that's farther from the bomb. flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; flAdjustedDamage = theDamage - flAdjustedDamage; if ( flAdjustedDamage > 0 ) { pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); } } } } } }
//========================================================= //========================================================= void CBasePlayer::CheatImpulseCommands( int iImpulse ) { if( !UTIL_CheatsAllowed() ) { return; } switch( iImpulse ) { case 76: { if( !g_bPrecacheGrunt ) { g_bPrecacheGrunt = true; ALERT( at_console, "You must now restart to use Grunt-o-matic.\n" ); } else { UTIL_MakeVectors( Vector( 0, GetViewAngle().y, 0 ) ); Create( "monster_human_grunt", GetAbsOrigin() + gpGlobals->v_forward * 128, GetAbsAngles() ); } break; } case 101: { gEvilImpulse101 = true; GiveNamedItem( "item_suit" ); GiveNamedItem( "item_battery" ); GiveNamedItem( "weapon_crowbar" ); GiveNamedItem( "weapon_9mmhandgun" ); GiveNamedItem( "ammo_9mmclip" ); GiveNamedItem( "weapon_shotgun" ); GiveNamedItem( "ammo_buckshot" ); GiveNamedItem( "weapon_9mmAR" ); GiveNamedItem( "ammo_9mmAR" ); GiveNamedItem( "ammo_ARgrenades" ); GiveNamedItem( "weapon_handgrenade" ); GiveNamedItem( "weapon_tripmine" ); GiveNamedItem( "weapon_357" ); GiveNamedItem( "ammo_357" ); GiveNamedItem( "weapon_crossbow" ); GiveNamedItem( "ammo_crossbow" ); GiveNamedItem( "weapon_egon" ); GiveNamedItem( "weapon_gauss" ); GiveNamedItem( "ammo_gaussclip" ); GiveNamedItem( "weapon_rpg" ); GiveNamedItem( "ammo_rpgclip" ); GiveNamedItem( "weapon_satchel" ); GiveNamedItem( "weapon_snark" ); GiveNamedItem( "weapon_hornetgun" ); #if USE_OPFOR GiveNamedItem( "weapon_knife" ); GiveNamedItem( "weapon_pipewrench" ); GiveNamedItem( "weapon_grapple" ); GiveNamedItem( "weapon_eagle" ); GiveNamedItem( "weapon_m249" ); GiveNamedItem( "weapon_displacer" ); GiveNamedItem( "weapon_sniperrifle" ); GiveNamedItem( "weapon_sporelauncher" ); GiveNamedItem( "weapon_shockrifle" ); GiveNamedItem( "ammo_556" ); GiveNamedItem( "ammo_762" ); #endif gEvilImpulse101 = false; break; } case 102: { // Gibbage!!! CGib::SpawnRandomGibs( this, 1, 1 ); break; } case 103: { // What the hell are you doing? auto pEntity = UTIL_FindEntityForward( this ); if( pEntity ) { CBaseMonster *pMonster = pEntity->MyMonsterPointer(); if( pMonster ) pMonster->ReportAIState(); } break; } case 104: { // Dump all of the global state varaibles (and global entity names) gGlobalState.DumpGlobals(); break; } case 105:// player makes no sound for monsters to hear. { if( m_fNoPlayerSound ) { ALERT( at_console, "Player is audible\n" ); m_fNoPlayerSound = false; } else { ALERT( at_console, "Player is silent\n" ); m_fNoPlayerSound = true; } break; } case 106: { // Give me the classname and targetname of this entity. auto pEntity = UTIL_FindEntityForward( this ); if( pEntity ) { ALERT( at_console, "Classname: %s", pEntity->GetClassname() ); if( pEntity->HasTargetname() ) { ALERT( at_console, " - Targetname: %s\n", pEntity->GetTargetname() ); } else { ALERT( at_console, " - TargetName: No Targetname\n" ); } ALERT( at_console, "Model: %s\n", pEntity->GetModelName() ); if( HasGlobalName() ) ALERT( at_console, "Globalname: %s\n", pEntity->GetGlobalName() ); } break; } case 107: { TraceResult tr; CBaseEntity* pWorld = CWorld::GetInstance(); Vector start = GetAbsOrigin() + GetViewOffset(); Vector end = start + gpGlobals->v_forward * 1024; UTIL_TraceLine( start, end, ignore_monsters, edict(), &tr ); if( tr.pHit ) pWorld = CBaseEntity::Instance( tr.pHit ); const texture_t* pTexture = UTIL_TraceTexture( pWorld, start, end ); if( pTexture ) ALERT( at_console, "Texture: %s\n", pTexture->name ); break; } case 195:// show shortest paths for entire level to nearest node { Create( "node_viewer_fly", GetAbsOrigin(), GetAbsAngles() ); break; } case 196:// show shortest paths for entire level to nearest node { Create( "node_viewer_large", GetAbsOrigin(), GetAbsAngles() ); break; } case 197:// show shortest paths for entire level to nearest node { Create( "node_viewer_human", GetAbsOrigin(), GetAbsAngles() ); break; } case 199:// show nearest node and all connections { ALERT( at_console, "%d\n", WorldGraph.FindNearestNode( GetAbsOrigin(), bits_NODE_GROUP_REALM ) ); WorldGraph.ShowNodeConnections( WorldGraph.FindNearestNode( GetAbsOrigin(), bits_NODE_GROUP_REALM ) ); break; } case 202:// Random blood splatter { UTIL_MakeVectors( GetViewAngle() ); TraceResult tr; UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), GetAbsOrigin() + GetViewOffset() + gpGlobals->v_forward * 128, ignore_monsters, ENT( pev ), &tr ); if( tr.flFraction != 1.0 ) {// line hit something, so paint a decal auto pBlood = static_cast< CBloodSplat* >( UTIL_CreateNamedEntity( "blood_splat" ) ); pBlood->CreateSplat( this ); } break; } case 203:// remove creature. { auto pEntity = UTIL_FindEntityForward( this ); if( pEntity ) { if( pEntity->GetTakeDamageMode() != DAMAGE_NO ) pEntity->SetThink( &CBaseEntity::SUB_Remove ); } break; } } }
/* ============ ImpulseCommands ============ */ void CBasePlayer::ImpulseCommands() { TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs // Handle use events PlayerUse(); int iImpulse = GetImpulse(); switch( iImpulse ) { case 99: { bool bOn; if( !gmsgLogo ) { bOn = true; //TODO: figure out why this is done here. - Solokiller gmsgLogo = REG_USER_MSG( "Logo", 1 ); } else { bOn = false; } ASSERT( gmsgLogo > 0 ); // send "health" update message MESSAGE_BEGIN( MSG_ONE, gmsgLogo, NULL, this ); WRITE_BYTE( bOn ); MESSAGE_END(); if( !bOn ) gmsgLogo = 0; break; } case 100: // temporary flashlight for level designers if( FlashlightIsOn() ) { FlashlightTurnOff(); } else { FlashlightTurnOn(); } break; case 201:// paint decal if( gpGlobals->time < m_flNextDecalTime ) { // too early! break; } UTIL_MakeVectors( GetViewAngle() ); UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), GetAbsOrigin() + GetViewOffset() + gpGlobals->v_forward * 128, ignore_monsters, ENT( pev ), &tr ); if( tr.flFraction != 1.0 ) {// line hit something, so paint a decal m_flNextDecalTime = gpGlobals->time + decalfrequency.value; auto pCan = static_cast<CSprayCan*>( UTIL_CreateNamedEntity( "spray_can" ) ); pCan->CreateSprayCan( this ); } break; default: // check all of the cheat impulse commands now CheatImpulseCommands( iImpulse ); break; } SetImpulse( 0 ); }
void CBasePlayer::PreThink() { const int buttonsChanged = ( m_afButtonLast ^ GetButtons().Get() ); // These buttons have changed this frame // Debounced button codes for pressed/released // UNDONE: Do we need auto-repeat? m_afButtonPressed = buttonsChanged & GetButtons().Get(); // The changed ones still down are "pressed" m_afButtonReleased = buttonsChanged & ( ~GetButtons().Get() ); // The ones not down are "released" g_pGameRules->PlayerThink( this ); bool bCheckVehicles = true; #if USE_ANGELSCRIPT uint32_t uiFlags = PreThinkFlag::NONE; CallGlobalEvent( g_PlayerPreThinkEvent, CallFlag::NONE, this, &uiFlags ); bCheckVehicles = !( uiFlags & PreThinkFlag::SKIP_VEHICLES ); #endif if( g_fGameOver ) return; // intermission or finale UTIL_MakeVectors( GetViewAngle() ); // is this still used? ItemPreFrame(); WaterMove(); if( g_pGameRules && g_pGameRules->FAllowFlashlight() ) m_iHideHUD &= ~HIDEHUD_FLASHLIGHT; else m_iHideHUD |= HIDEHUD_FLASHLIGHT; // JOHN: checks if new client data (for HUD and view control) needs to be sent to the client UpdateClientData(); CheckTimeBasedDamage(); CheckSuitUpdate(); // Observer Button Handling if( IsObserver() ) { Observer_HandleButtons(); Observer_CheckTarget(); Observer_CheckProperties(); SetImpulse( 0 ); return; } if( GetDeadFlag() >= DEAD_DYING ) { PlayerDeathThink(); return; } // So the correct flags get sent to client asap. // if( m_afPhysicsFlags & PFLAG_ONTRAIN ) GetFlags() |= FL_ONTRAIN; else GetFlags().ClearFlags( FL_ONTRAIN ); if( bCheckVehicles ) { #if USE_OPFOR //We're on a rope. - Solokiller if( m_afPhysicsFlags & PFLAG_ONROPE && m_pRope ) { SetAbsVelocity( g_vecZero ); const Vector vecAttachPos = m_pRope->GetAttachedObjectsPosition(); SetAbsOrigin( vecAttachPos ); Vector vecForce; /* //TODO: This causes sideways acceleration that doesn't occur in Op4. - Solokiller //TODO: should be IN_MOVERIGHT and IN_MOVELEFT - Solokiller if( GetButtons().Any( IN_DUCK ) ) { vecForce.x = gpGlobals->v_right.x; vecForce.y = gpGlobals->v_right.y; vecForce.z = 0; m_pRope->ApplyForceFromPlayer( vecForce ); } if( GetButtons().Any( IN_JUMP ) ) { vecForce.x = -gpGlobals->v_right.x; vecForce.y = -gpGlobals->v_right.y; vecForce.z = 0; m_pRope->ApplyForceFromPlayer( vecForce ); } */ //Determine if any force should be applied to the rope, or if we should move around. - Solokiller if( GetButtons().Any( IN_BACK | IN_FORWARD ) ) { if( ( gpGlobals->v_forward.x * gpGlobals->v_forward.x + gpGlobals->v_forward.y * gpGlobals->v_forward.y - gpGlobals->v_forward.z * gpGlobals->v_forward.z ) <= 0.0 ) { if( m_bIsClimbing ) { const float flDelta = gpGlobals->time - m_flLastClimbTime; m_flLastClimbTime = gpGlobals->time; if( GetButtons().Any( IN_FORWARD ) ) { if( gpGlobals->v_forward.z < 0.0 ) { if( !m_pRope->MoveDown( flDelta ) ) { //Let go of the rope, detach. - Solokiller SetMoveType( MOVETYPE_WALK ); SetSolidType( SOLID_SLIDEBOX ); m_afPhysicsFlags &= ~PFLAG_ONROPE; m_pRope->DetachObject(); m_pRope = nullptr; m_bIsClimbing = false; } } else { m_pRope->MoveUp( flDelta ); } } if( GetButtons().Any( IN_BACK ) ) { if( gpGlobals->v_forward.z < 0.0 ) { m_pRope->MoveUp( flDelta ); } else if( !m_pRope->MoveDown( flDelta ) ) { //Let go of the rope, detach. - Solokiller SetMoveType( MOVETYPE_WALK ); SetSolidType( SOLID_SLIDEBOX ); m_afPhysicsFlags &= ~PFLAG_ONROPE; m_pRope->DetachObject(); m_pRope = nullptr; m_bIsClimbing = false; } } } else { m_bIsClimbing = true; m_flLastClimbTime = gpGlobals->time; } } else { vecForce.x = gpGlobals->v_forward.x; vecForce.y = gpGlobals->v_forward.y; vecForce.z = 0.0; if( GetButtons().Any( IN_BACK ) ) { vecForce.x = -gpGlobals->v_forward.x; vecForce.y = -gpGlobals->v_forward.y; vecForce.z = 0; } m_pRope->ApplyForceFromPlayer( vecForce ); m_bIsClimbing = false; } } else { m_bIsClimbing = false; } if( m_afButtonPressed & IN_JUMP ) { //We've jumped off the rope, give us some momentum - Solokiller SetMoveType( MOVETYPE_WALK ); SetSolidType( SOLID_SLIDEBOX ); this->m_afPhysicsFlags &= ~PFLAG_ONROPE; Vector vecDir = gpGlobals->v_up * 165.0 + gpGlobals->v_forward * 150.0; Vector vecVelocity = m_pRope->GetAttachedObjectsVelocity() * 2; vecVelocity.NormalizeInPlace(); vecVelocity = vecVelocity * 200; SetAbsVelocity( vecVelocity + vecDir ); m_pRope->DetachObject(); m_pRope = nullptr; m_bIsClimbing = false; } return; } #endif // Train speed control if( m_afPhysicsFlags & PFLAG_ONTRAIN ) { CBaseEntity *pTrain = GetGroundEntity(); //To match original behavior, Instance returns the world if entity is null - Solokiller if( !pTrain ) pTrain = CWorld::GetInstance(); float vel; if( !pTrain ) { TraceResult trainTrace; // Maybe this is on the other side of a level transition UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -38 ), ignore_monsters, ENT( pev ), &trainTrace ); // HACKHACK - Just look for the func_tracktrain classname if( trainTrace.flFraction != 1.0 && trainTrace.pHit ) pTrain = CBaseEntity::Instance( trainTrace.pHit ); if( !pTrain || !( pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) || !pTrain->OnControls( this ) ) { //ALERT( at_error, "In train mode with no train!\n" ); m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW | TRAIN_OFF; return; } } else if( !GetFlags().Any( FL_ONGROUND ) || pTrain->GetSpawnFlags().Any( SF_TRACKTRAIN_NOCONTROL ) || ( GetButtons().Any( IN_MOVELEFT | IN_MOVERIGHT ) ) ) { // Turn off the train if you jump, strafe, or the train controls go dead m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = TRAIN_NEW | TRAIN_OFF; return; } SetAbsVelocity( g_vecZero ); vel = 0; if( m_afButtonPressed & IN_FORWARD ) { vel = 1; pTrain->Use( this, this, USE_SET, ( float ) vel ); } else if( m_afButtonPressed & IN_BACK ) { vel = -1; pTrain->Use( this, this, USE_SET, ( float ) vel ); } if( vel ) { m_iTrain = TrainSpeed( pTrain->GetSpeed(), pTrain->GetImpulse() ); m_iTrain |= TRAIN_ACTIVE | TRAIN_NEW; } } else if( m_iTrain & TRAIN_ACTIVE ) m_iTrain = TRAIN_NEW; // turn off train } if( GetButtons().Any( IN_JUMP ) ) { // If on a ladder, jump off the ladder // else Jump Jump(); } // If trying to duck, already ducked, or in the process of ducking if( GetButtons().Any( IN_DUCK ) || GetFlags().Any( FL_DUCKING ) || ( m_afPhysicsFlags & PFLAG_DUCKING ) ) Duck(); if( !GetFlags().Any( FL_ONGROUND ) ) { m_flFallVelocity = -GetAbsVelocity().z; } // StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating? // Clear out ladder pointer m_hEnemy = NULL; if( m_afPhysicsFlags & PFLAG_ONBARNACLE ) { SetAbsVelocity( g_vecZero ); } }