//------------------------------------------------------------------------ int CScriptBind_Actor::GetChannel(IFunctionHandler *pH) { CActor *pActor = GetActor(pH); if (!pActor) return pH->EndFunction(); return pH->EndFunction( (int)pActor->GetChannelId() ); }
void CLTagSingle::NetShootEx(const Vec3 &pos, const Vec3 &dir, const Vec3 &vel, const Vec3 &hit, float extra, int predictionHandle) { IEntityClass* ammo = m_fireParams->fireparams.ammo_type_class; int weaponAmmoCount = m_pWeapon->GetAmmoCount(ammo); int ammoCount = weaponAmmoCount; CActor* pOwnerActor = m_pWeapon->GetOwnerActor(); bool playerIsShooter = pOwnerActor ? pOwnerActor->IsPlayer() : false; if (IsProceduralRecoilEnabled() && pOwnerActor) { pOwnerActor->ProceduralRecoil(m_fireParams->proceduralRecoilParams.duration, m_fireParams->proceduralRecoilParams.strength, m_fireParams->proceduralRecoilParams.kickIn, m_fireParams->proceduralRecoilParams.arms); } m_pWeapon->PlayAction(GetFragmentIds().fire, 0, false, CItem::eIPAF_Default); CProjectile *pAmmo = m_pWeapon->SpawnAmmo(m_fireParams->fireparams.spawn_ammo_class, true); CLTAGGrenade* pGrenade = static_cast<CLTAGGrenade*>(pAmmo); if (pGrenade) { CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId, string().Format("Invalid hit type '%s' in fire params for '%s'", m_fireParams->fireparams.hit_type.c_str(), m_pWeapon->GetEntity()->GetName())); CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId == g_pGame->GetGameRules()->GetHitTypeId(m_fireParams->fireparams.hit_type.c_str()), "Sanity Check Failed: Stored hit type id does not match the type string, possibly CacheResources wasn't called on this weapon type"); pGrenade->SetParams(CProjectile::SProjectileDesc( m_pWeapon->GetOwnerId(), m_pWeapon->GetHostId(), m_pWeapon->GetEntityId(), m_fireParams->fireparams.damage, 0.f, 0.f, 0.f, m_fireParams->fireparams.hitTypeId, m_fireParams->fireparams.bullet_pierceability_modifier, m_pWeapon->IsZoomed())); // this must be done after owner is set pGrenade->InitWithAI(); pGrenade->SetDestination(m_pWeapon->GetDestination()); pGrenade->SetGrenadeType(m_grenadeType); pGrenade->Launch(pos, dir, vel, m_speed_scale); m_projectileId = pGrenade->GetEntity()->GetId(); } if (m_pWeapon->IsServer()) { const char *ammoName = ammo != NULL ? ammo->GetName() : NULL; g_pGame->GetIGameFramework()->GetIGameplayRecorder()->Event(m_pWeapon->GetOwner(), GameplayEvent(eGE_WeaponShot, ammoName, 1, (void *)(EXPAND_PTR)m_pWeapon->GetEntityId())); #ifdef SERVER_CHECKS if (pOwnerActor && m_pWeapon->IsServerSpawn(ammo)) { int inventoryAmmoCount = m_pWeapon->GetInventoryAmmoCount(ammo); int totalAmmoCount=weaponAmmoCount+inventoryAmmoCount; // this needed seeing as how this seems to use weaponAmmo or inventoryAmmo but NOT both? if (totalAmmoCount <= 0) { g_pGame->GetAntiCheatManager()->FlagActivity(eCT_ServerSpawnedAmmoUsed, pOwnerActor->GetChannelId()); } } #endif } m_muzzleEffect.Shoot(this, hit, m_barrelId); RecoilImpulse(pos, dir); m_fired = true; m_next_shot = 0.0f; ammoCount--; if(ammoCount<0) ammoCount = 0; if (m_pWeapon->IsServer()) { int clipSize = GetClipSize(); if (clipSize != -1) { if (clipSize != 0) m_pWeapon->SetAmmoCount(ammo, ammoCount); else m_pWeapon->SetInventoryAmmoCount(ammo, ammoCount); } } OnShoot(m_pWeapon->GetOwnerId(), pAmmo?pAmmo->GetEntity()->GetId():0, ammo, pos, dir, vel); if(playerIsShooter) { const SThermalVisionParams& thermalParams = m_fireParams->thermalVisionParams; m_pWeapon->AddShootHeatPulse(pOwnerActor, thermalParams.weapon_shootHeatPulse, thermalParams.weapon_shootHeatPulseTime, thermalParams.owner_shootHeatPulse, thermalParams.owner_shootHeatPulseTime); } if (OutOfAmmo()) m_pWeapon->OnOutOfAmmo(ammo); if (pAmmo && predictionHandle && pOwnerActor) { pAmmo->GetGameObject()->RegisterAsValidated(pOwnerActor->GetGameObject(), predictionHandle); pAmmo->GetGameObject()->BindToNetwork(); } else if (pAmmo && pAmmo->IsPredicted() && gEnv->IsClient() && gEnv->bServer && pOwnerActor && pOwnerActor->IsClient()) { pAmmo->GetGameObject()->BindToNetwork(); } m_pWeapon->RequireUpdate(eIUS_FireMode); }
//------------------------------------------------------------------------ // returns true if entity is killed, false if it is not bool CGameRulesMPDamageHandling::SvOnHit( const HitInfo &hitInfo ) { const HitTypeInfo * pHitTypeInfo = m_pGameRules->GetHitTypeInfo(hitInfo.type); #if !defined(_RELEASE) if(!pHitTypeInfo) CryFatalError("By ::SvOnHit() all hit info should have a hit type that is valid and registered in the GameRules. This isn't true of type %d!", hitInfo.type); #endif SDamageHandling damageHandling(&hitInfo, 1.0f); float damage = hitInfo.damage; IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); CActor *pTargetActor = static_cast<CActor*>(pActorSystem->GetActor(hitInfo.targetId)); CActor *pShooterActor = static_cast<CActor*>(pActorSystem->GetActor(hitInfo.shooterId)); CPlayer* pShooterPlayer = (pShooterActor && pShooterActor->IsPlayer()) ? static_cast<CPlayer*>(pShooterActor) : NULL ; bool isPlayer = pTargetActor != NULL && pTargetActor->IsPlayer(); #ifndef _RELEASE //--- Fix to allow the damage handling to work for these entity classes in the same way as for Players static IEntityClass* sDamEntClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass("DamageTestEnt"); isPlayer |= pTargetActor != NULL && pTargetActor->GetEntity()->GetClass() == sDamEntClass; #endif CPlayer* pPlayer = isPlayer ? static_cast<CPlayer*>(pTargetActor) : NULL; const bool isMelee = ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::IsMeleeAttack) != 0); const bool checkHeadshots = ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::IgnoreHeadshots) == 0); bool bIsHeadShot = false; if(pPlayer && hitInfo.partId >= 0 && checkHeadshots) { bIsHeadShot = pPlayer->IsHeadShot(hitInfo); if (!bIsHeadShot && g_pGameCVars->g_mpHeadshotsOnly) { damage = 0.f; } } //If the player has died more than kTimeToAllowKillsAfterDeath seconds ago, we disallow any damage they apply, unless the hit type is flagged as allowing it. static const float kTimeToAllowKillsAfterDeath = 0.05f; if(pTargetActor && pShooterPlayer && !hitInfo.hitViaProxy && ((pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::AllowPostDeathDamage) == 0) && pShooterActor->IsDead() && (gEnv->pTimer->GetFrameStartTime().GetSeconds() - pShooterPlayer->GetDeathTime()) > kTimeToAllowKillsAfterDeath) { damage = 0.0f; } IGameRulesStateModule *stateModule = m_pGameRules->GetStateModule(); IGameRulesRoundsModule* pRoundsModule = m_pGameRules->GetRoundsModule(); if ( (stateModule != NULL && (stateModule->GetGameState() == IGameRulesStateModule::EGRS_PostGame)) || (pRoundsModule!= NULL && !pRoundsModule->IsInProgress() )) { // No damage allowed once the game has ended, except in cases where it would cause graphical glitches if (hitInfo.type != CGameRules::EHitType::PunishFall) { damage = 0.0f; } } IEntity *pTarget = gEnv->pEntitySystem->GetEntity(hitInfo.targetId); #if defined(SERVER_CHECKS) if(damage != 0.0f) { int nNewCheckCounter = m_checkCounter + 1; if (CItem *pItem = static_cast<CItem *>(g_pGame->GetIGameFramework()->GetIItemSystem()->GetItem(hitInfo.weaponId))) { if(CWeapon * pWeapon = static_cast<CWeapon*>(pItem->GetIWeapon())) { int nFireModeFromShotId = GetFireModeFromShotId(hitInfo.shotId); static const int kCheckFreq = 7; if(pShooterActor && nNewCheckCounter == kCheckFreq) { float fDamageAtXMeters = 0.0f; float fDistance2D, fDistanceMax, fNetLagSeconds; CServerCheatMonitor::GetHitValidationInfo(*pShooterActor, hitInfo, fDistance2D, fDistanceMax, fNetLagSeconds); bool bDoDamageValidation = false; if(isMelee) { if(CMelee * pMelee = pWeapon->GetMelee()) { //This check can't be used for everything because the default firemode returns '0.f' for range and only CMelee extends it // the horizontal player speed is x 2.0f as the players could have potentially immediately turned and run away from each other float fMeleeRangeError = fDistance2D - (pMelee->GetRange() + (CServerCheatMonitor::kMaxHorizontalPlayerSpeed * fNetLagSeconds * 2.0f)); if(fMeleeRangeError > 0.1f) { g_pGame->GetAntiCheatManager()->FlagActivity(eCT_MeleeRange, pShooterActor->GetChannelId(), fMeleeRangeError); } fDamageAtXMeters = pMelee->GetDamageAmountAtXMeters(fDistance2D); bDoDamageValidation = true; } } else { ////////////////////////////////////////////////////////////////////// // Verify that the hit is from a valid shot DoShotValidation(hitInfo, pHitTypeInfo, pShooterActor); ////////////////////////////////////////////////////////////////////// CFireMode * pFireMode = static_cast<CFireMode*>(pWeapon->GetFireMode(nFireModeFromShotId)); if(pFireMode) { const SFireModeParams * pParams = pFireMode->GetShared(); char projectileClassName[128]; g_pGame->GetIGameFramework()->GetNetworkSafeClassName(projectileClassName, sizeof(projectileClassName), hitInfo.projectileClassId); IEntityClass * pProjectileClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(projectileClassName); if((pProjectileClass && (pProjectileClass == pParams->fireparams.ammo_type_class || pProjectileClass == pParams->plantparams.ammo_type_class))) { float fBulletSpeed = 100.f; const SAmmoParams * pAmmoParams = g_pGame->GetWeaponSystem()->GetAmmoParams(pFireMode->GetAmmoType()); if(pAmmoParams) { fBulletSpeed = pAmmoParams->speed; } //Might have been closer when the shot was fired const float fMaxTimeSinceShot = ((fDistanceMax / fBulletSpeed) * 2.0f) + fNetLagSeconds; //Should be less than this. Laaaaarge fudge factor float fDistance_Conservative = fDistance2D - (fMaxTimeSinceShot * CServerCheatMonitor::kMaxHorizontalPlayerSpeed); fDamageAtXMeters = pFireMode->GetDamageAmountAtXMeters(fDistance_Conservative); } } else if(pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::ValidationRequired) { CryStackStringT<char, 256> invalidFireModeMessage; invalidFireModeMessage.Format("Invalid fire mode, weapon: '%s', firemode: %d", pWeapon->GetEntity()->GetName(), nFireModeFromShotId); g_pGame->GetAntiCheatManager()->FlagActivity(eCT_ValidHitInfo, pShooterActor->GetChannelId(), invalidFireModeMessage.c_str()); } } float fDamageFromWeapon = hitInfo.damage; if(fDamageFromWeapon > fDamageAtXMeters && fDamageAtXMeters > 0.0f) { //Log the ratio of actual damage to expected damage, e.g. 1.2 x expected CryStackStringT<char, 256> excessiveDamageMessage; excessiveDamageMessage.Format("Weapon '%s', firemode %d", pWeapon->GetEntity()->GetName(), nFireModeFromShotId); g_pGame->GetAntiCheatManager()->FlagActivity(eCT_WeaponDamage, pShooterActor->GetChannelId(), fDamageFromWeapon / fDamageAtXMeters, excessiveDamageMessage.c_str()); } if(pTargetActor) { CServerCheatMonitor::ValidateTargetActorPositionAgainstHit(*pTargetActor, hitInfo, fNetLagSeconds); } nNewCheckCounter = 0; } else { nNewCheckCounter = kCheckFreq - 1; } } } m_checkCounter = nNewCheckCounter; } // Update the shotcounter for tracking headshots and traversal times if(pShooterPlayer && (pHitTypeInfo->m_flags & CGameRules::EHitTypeFlag::ValidationRequired)) { char netName[128]; g_pGame->GetIGameFramework()->GetNetworkSafeClassName(netName, sizeof(netName), hitInfo.projectileClassId); IEntityClass * pProjectileClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(netName); if (pProjectileClass) { CShotCounter* pShotCounter = pShooterPlayer->GetShotCounter(); pShotCounter->RecordHit(hitInfo, bIsHeadShot); } } #endif // SERVER_CHECKS IEntityClass* pTargetClass = pTarget ? pTarget->GetClass() : NULL; // Check for friendly fire if( bool bCheckFriendlyFire = ((hitInfo.targetId!=hitInfo.shooterId) && (hitInfo.type!=CGameRules::EHitType::EventDamage)) ) { if(IVehicle* pTargetVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(hitInfo.targetId)) { if(IActor* pDriverTargetVehicle = pTargetVehicle->GetDriver()) { // Vehicle driver shot own vehicle (same as shooting yourself), don't do friendly fire. bCheckFriendlyFire = pDriverTargetVehicle->GetEntityId()!=hitInfo.shooterId; } } if(bCheckFriendlyFire) { if (m_pGameRules->GetTeamCount() > 1) { int shooterTeamId = m_pGameRules->GetTeam(hitInfo.shooterId); int targetTeamId = m_pGameRules->GetTeam(hitInfo.targetId); if (shooterTeamId && (shooterTeamId == targetTeamId)) { damage = GetFriendlyFireDamage(damage, hitInfo, pTargetActor); } } } } if (damage <= 0.f) { // If the hit isn't doing anything bail, this means any hit that gets past here has damage associated with it and thus wants to // display a hit indicator return false; } if (pPlayer) { if(hitInfo.partId >= 0 && !isMelee) { damageHandling.damageMultiplier *= pPlayer->GetBodyDamageMultiplier(hitInfo); } if (isMelee) { damageHandling.damageMultiplier *= g_pGameCVars->pl_melee.damage_multiplier_mp; } } damage *= damageHandling.damageMultiplier; HitInfo hitInfoWithDamage = hitInfo; hitInfoWithDamage.damage = damage; if(pPlayer) { SActorStats* pStats = pPlayer->GetActorStats(); float actorHealth = pPlayer->GetHealth(); if(pStats) { #ifndef _RELEASE if (g_pGameCVars->g_LogDamage) { char weaponClassName[64], projectileClassName[64]; CryLog ("[DAMAGE] %s '%s' took %.3f '%s' damage (%.3f x %.3f) weapon=%s projectile=%s", pPlayer->GetEntity()->GetClass()->GetName(), pPlayer->GetEntity()->GetName(), damage, m_pGameRules->GetHitType(hitInfo.type), hitInfo.damage, damageHandling.damageMultiplier, g_pGame->GetIGameFramework()->GetNetworkSafeClassName(weaponClassName, sizeof(weaponClassName), hitInfo.weaponClassId) ? weaponClassName : "none", g_pGame->GetIGameFramework()->GetNetworkSafeClassName(projectileClassName, sizeof(projectileClassName), hitInfo.projectileClassId) ? projectileClassName : "none"); } #endif if(pStats->bStealthKilling && actorHealth <= damage) { if(pPlayer->GetStealthKill().GetTargetId() != hitInfoWithDamage.shooterId) { pPlayer->StoreDelayedKillingHitInfo(hitInfoWithDamage); } hitInfoWithDamage.damage = 0; } else if (pStats->bStealthKilled && hitInfoWithDamage.type != CGameRules::EHitType::StealthKill) { hitInfoWithDamage.damage = 0; } } } bool bKilled = SvOnHitScaled(hitInfoWithDamage); return bKilled; }