//------------------------------------------------------------------------ void CVehicleDamageBehaviorCameraShake::ShakeClient(float angle, float shift, float duration, float frequency) { IActorSystem *pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); EntityId clientId = g_pGame->GetIGameFramework()->GetClientActorId(); for (TVehicleSeatId seatId = InvalidVehicleSeatId + 1; seatId != m_pVehicle->GetLastSeatId(); seatId++) { if (IVehicleSeat *pSeat = m_pVehicle->GetSeatById(seatId)) { EntityId passengerId = pSeat->GetPassenger(); if (passengerId == clientId) { CActor *pActor = static_cast<CActor *>(pActorSystem->GetActor(passengerId)); if(pActor) { pActor->CameraShake(angle, shift, duration, frequency, Vec3(0.0f, 0.0f, 0.0f), 5, "VehicleDamageBehaviorCameraShake"); } } } } }
//------------------------------------------------------------------------ void CVehicleActionDeployRope::OnVehicleEvent(EVehicleEvent event, const SVehicleEventParams& params) { if (event == eVE_PassengerExit && params.iParam == m_seatId) { IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(params.entityId); if (!pActor) { assert(pActor); return; } m_actorId = pActor->GetEntityId(); DeployRope(); AttachOnRope(pActor->GetEntity()); } else if (event == eVE_Destroyed) { if (m_ropeUpperId) { gEnv->pEntitySystem->RemoveEntity(m_ropeUpperId); m_ropeUpperId = 0; } if (m_ropeLowerId) { gEnv->pEntitySystem->RemoveEntity(m_ropeLowerId); m_ropeLowerId = 0; } } }
//------------------------------------------------------------------------ void CVehicleActionDeployRope::Update(const float deltaTime) { if (!m_ropeUpperId && !m_ropeLowerId && !m_actorId) return; IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(m_actorId); if (!pActor) return; Vec3 worldPos = pActor->GetEntity()->GetWorldTM().GetTranslation(); if (IRopeRenderNode* pRopeUpper = GetRopeRenderNode(m_ropeUpperId)) { Vec3 points[2]; points[0] = m_pRopeHelper->GetWorldTM().GetTranslation(); points[1] = worldPos; pRopeUpper->SetPoints(points, 2); float lenghtLeft = max(0.0f, g_ropeLenght - (points[0].z - points[1].z)); if (IRopeRenderNode* pRopeLower = GetRopeRenderNode(m_ropeLowerId)) { Vec3 points[2]; points[0] = worldPos; points[1] = Vec3(worldPos.x, worldPos.y, worldPos.z - lenghtLeft); pRopeLower->SetPoints(points, 2); } } }
EEntityType GetEntityType(EntityId id) { int type = eET_Unknown; IEntitySystem *pEntitySystem = gEnv->pEntitySystem; if (pEntitySystem) { IEntity *pEntity = pEntitySystem->GetEntity(id); if (pEntity) { type = eET_Valid; IEntityClass *pClass = pEntity->GetClass(); if (pClass) { const char* className = pClass->GetName(); // Check AI if (pEntity->GetAI()) { type |= eET_AI; } // Check actor IActorSystem *pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); if (pActorSystem) { IActor *pActor = pActorSystem->GetActor(id); if (pActor) { type |= eET_Actor; } } // Check vehicle IVehicleSystem *pVehicleSystem = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem(); if (pVehicleSystem) { if (pVehicleSystem->IsVehicleClass(className)) { type |= eET_Vehicle; } } // Check item IItemSystem *pItemSystem = gEnv->pGame->GetIGameFramework()->GetIItemSystem(); if (pItemSystem) { if (pItemSystem->IsItemClass(className)) { type |= eET_Item; } } } } } return (EEntityType)type; }
void CAnimatedCharacter::RefreshAnimTarget() { if (m_pMannequinAGState) { IActorSystem* pActorSystem = CCryAction::GetCryAction()->GetIActorSystem(); assert(pActorSystem != NULL); IActor* pActor = pActorSystem->GetActor(GetEntity()->GetId()); IMovementController* pMovementController = pActor->GetMovementController(); m_pAnimTarget = pMovementController->GetExactPositioningTarget(); } }
//------------------------------------------------------------------------ void CVehicleSeatActionMovement::StopUsing() { IActorSystem* pActorSystem = CCryAction::GetCryAction()->GetIActorSystem(); CRY_ASSERT(pActorSystem); IVehicleMovement* pMovement = m_pVehicle->GetMovement(); if (!pMovement) return; CRY_ASSERT(m_pSeat); // default to continuing for a bit m_delayedStop = 0.8f; IActor* pActor = pActorSystem->GetActor(m_pSeat->GetPassenger()); if (pActor && pActor->IsPlayer()) { // if stopped already don't go anywhere IPhysicalEntity* pPhys = m_pVehicle->GetEntity()->GetPhysics(); pe_status_dynamics status; if (pPhys && pPhys->GetStatus(&status)) { if (status.v.GetLengthSquared() < 25.0f) m_delayedStop = 0.0f; } if (m_actionForward > 0.0f) m_delayedStop = 1.5f; if (pMovement->GetMovementType() == IVehicleMovement::eVMT_Air) m_delayedStop *= 2.0f; m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_AlwaysUpdate); // prevent full pedal being kept pressed, but give it a bit pMovement->OnAction(eVAI_MoveForward, eAAM_OnPress, 0.1f); } else { if (pMovement->GetMovementType() == IVehicleMovement::eVMT_Air) { m_delayedStop = 0.0f; m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_AlwaysUpdate); } else { pMovement->StopDriving(); } } }
//--------------------------------------- TAudioSignalID CAreaAnnouncer::BuildAnnouncement(const EntityId clientId) { const int k_areaCount = m_areaList.size(); if (k_areaCount > 0) { IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); if (CActor* pClientActor = static_cast<CActor*>(pActorSystem->GetActor(clientId))) { int actorCount[k_maxAnnouncementAreas]; memset(&actorCount, 0, sizeof(actorCount)); CActorManager * pActorManager = CActorManager::GetActorManager(); pActorManager->PrepareForIteration(); const int kNumActors = pActorManager->GetNumActors(); const int kPlayerTeamId = g_pGame->GetGameRules()->GetTeam(clientId); for (int i = 0; i < kNumActors; i++) { SActorData actorData; pActorManager->GetNthActorData(i, actorData); if(actorData.teamId != kPlayerTeamId && actorData.health > 0 && actorData.spectatorMode == CActor::eASM_None) { for (int areaIndex = 0; areaIndex < k_areaCount; areaIndex++) { IEntity* pEntity = gEnv->pEntitySystem->GetEntity(m_areaList[areaIndex].m_areaProxyId); if(pEntity) { IEntityAreaProxy *pArea = (IEntityAreaProxy*)pEntity->GetProxy(ENTITY_PROXY_AREA); if(pArea && pArea->CalcPointWithin(INVALID_ENTITYID, actorData.position, true)) { actorCount[areaIndex]++; break; } } } } } return GenerateAnnouncement(&actorCount[0], k_areaCount, clientId); } } return INVALID_AUDIOSIGNAL_ID; }
//------------------------------------------------------------------------ void CVehicleClient::OnEnterVehicleSeat(IVehicleSeat* pSeat) { m_bMovementFlagRight=m_bMovementFlagLeft=m_bMovementFlagForward=m_bMovementFlagBack=false; m_fLeftRight = m_fForwardBackward = 0.f; IVehicle* pVehicle = pSeat->GetVehicle(); assert(pVehicle); IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(pSeat->GetPassenger()); if (pActor) { bool isThirdPerson = pActor->IsThirdPerson() || m_tp; TVehicleViewId viewId = InvalidVehicleViewId; TVehicleViewId firstViewId = InvalidVehicleViewId; while (viewId = pSeat->GetNextView(viewId)) { if (viewId == firstViewId) break; if (firstViewId == InvalidVehicleViewId) firstViewId = viewId; if (IVehicleView* pView = pSeat->GetView(viewId)) { if (pView->IsThirdPerson() == isThirdPerson) break; } } if (viewId != InvalidVehicleViewId) pSeat->SetView(viewId); if(IActor *pPassengerActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pSeat->GetPassenger())) { if(pPassengerActor->IsPlayer()) { EnableActionMaps(pSeat, true); } } } }
//------------------------------------------------------------------------- void CGameRulesStandardState::CleanUpAbortedIntro() { CGameRules::TPlayers players; m_pGameRules->GetPlayers(players); IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); CGameRules::TPlayers::const_iterator iter = players.begin(); CGameRules::TPlayers::const_iterator end = players.end(); while(iter != end) { CPlayer* pPlayer = static_cast<CPlayer*>( pActorSystem->GetActor( *iter ) ); if(pPlayer) { pPlayer->OnIntroSequenceFinished(); } ++iter; } }
//------------------------------------------------------------------------ void CVehicleClient::OnEnterVehicleSeat(IVehicleSeat* pSeat) { m_bMovementFlagRight=m_bMovementFlagLeft=m_bMovementFlagForward=m_bMovementFlagBack=false; m_fLeftRight = m_fForwardBackward = 0.f; IVehicle* pVehicle = pSeat->GetVehicle(); assert(pVehicle); IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(pSeat->GetPassenger()); bool isThirdPerson = pActor->IsThirdPerson() || m_tp; TVehicleViewId viewId = InvalidVehicleViewId; TVehicleViewId firstViewId = InvalidVehicleViewId; while (viewId = pSeat->GetNextView(viewId)) { if (viewId == firstViewId) break; if (firstViewId == InvalidVehicleViewId) firstViewId = viewId; if (IVehicleView* pView = pSeat->GetView(viewId)) { if (pView->IsThirdPerson() == isThirdPerson) break; } } if (viewId != InvalidVehicleViewId) pSeat->SetView(viewId); IActionMapManager* pMapManager = gEnv->pGame->GetIGameFramework()->GetIActionMapManager(); assert(pMapManager); pMapManager->EnableActionMap("landvehicle", false); pMapManager->EnableActionMap("seavehicle", false); pMapManager->EnableActionMap("helicopter", false); pMapManager->EnableActionMap("vtol", false); pMapManager->EnableFilter ( "vehicle_no_seat_change_and_exit", true ); }
//------------------------------------------------------------------------ bool CVehicleActionDeployRope::DeployRope() { IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(m_actorId); if (!pActor) return false; Vec3 upperPos = m_pRopeHelper->GetWorldTM().GetTranslation(); Vec3 lowerPos(upperPos.x, upperPos.y, upperPos.z - g_ropeLenght); m_ropeUpperId = CreateRope(m_pVehicle->GetEntity()->GetPhysics(), upperPos, upperPos); m_ropeLowerId = CreateRope(pActor->GetEntity()->GetPhysics(), upperPos, lowerPos); m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_AlwaysUpdate); return true; }
void CLocalPlayerComponent::Revive() { if(gEnv->bMultiplayer) { // Reset NotYetSpawned filter. IActionFilter* pNYSFilter = g_pGameActions->FilterNotYetSpawned(); if(pNYSFilter && pNYSFilter->Enabled()) { pNYSFilter->Enable(false); } // For Modes where we can swap teams per round, refresh everyone else's cloak colour on revive. CGameRules *pGameRules = g_pGame->GetGameRules(); if( pGameRules->GetGameMode()==eGM_Gladiator ) { IActorSystem* pActorSys = g_pGame->GetIGameFramework()->GetIActorSystem(); CActorManager* pActorManager = CActorManager::GetActorManager(); pActorManager->PrepareForIteration(); const int kNumActors = pActorManager->GetNumActors(); for(int i=0; i<kNumActors; i++) { SActorData actorData; pActorManager->GetNthActorData(i, actorData); if(CActor* pActor = static_cast<CActor*>(pActorSys->GetActor(actorData.entityId))) { if(pActor->IsCloaked()) { pActor->SetCloakLayer(false); pActor->SetCloakLayer(true); } } } } } m_bIsInFreeFallDeath = false; m_playedMidHealthSound = false; }
//------------------------------------------------------------------------ void CVehicleSeatActionMovement::StartUsing(EntityId passengerId) { IActorSystem* pActorSystem = CCryAction::GetCryAction()->GetIActorSystem(); CRY_ASSERT(pActorSystem); IActor* pActor = pActorSystem->GetActor(passengerId); CRY_ASSERT(pActor); IVehicleMovement* pMovement = m_pVehicle->GetMovement(); CRY_ASSERT(pMovement); if (!pMovement) return; if (m_delayedStop >= 0.0f) { m_pVehicle->SetObjectUpdate(this, IVehicle::eVOU_NoUpdate); m_delayedStop = 0.0f; pMovement->StopDriving(); } pMovement->StartDriving(passengerId); }
//------------------------------------------------------------------------ void CVehicleViewFirstPerson::HideEntitySlots(IEntity* pEnt, bool hide) { IActorSystem* pActorSystem = CCryAction::GetCryAction()->GetIActorSystem(); CRY_ASSERT(pActorSystem); if (hide) { for (int i=0; i<pEnt->GetSlotCount(); ++i) { if (pEnt->IsSlotValid(i) && pEnt->GetSlotFlags(i) & ENTITY_SLOT_RENDER) { if (pEnt->GetId() == m_pVehicle->GetEntity()->GetId()) { // set character to always update if (ICharacterInstance* pCharInstance = pEnt->GetCharacter(i)) { pCharInstance->SetFlags(pCharInstance->GetFlags() | CS_FLAG_UPDATE_ALWAYS); if (ISkeletonPose* pSkeletonPose = pCharInstance->GetISkeletonPose()) pSkeletonPose->SetForceSkeletonUpdate(10); } } pEnt->SetSlotFlags(i, pEnt->GetSlotFlags(i) & ~ENTITY_SLOT_RENDER); if (IActor* pActor = pActorSystem->GetActor(pEnt->GetId())) { pActor->HideAllAttachments(true); } // store slot; we must not reveal previously hidden slots later m_slotFlags.insert( std::pair<EntityId,int>(pEnt->GetId(), i) ); } } // hide all children for (int i=0; i<pEnt->GetChildCount(); ++i) HideEntitySlots(pEnt->GetChild(i), hide); } else { // unhide all stored slots for (TSlots::iterator it=m_slotFlags.begin(); it!=m_slotFlags.end(); ++it) { IEntity* pEntity = gEnv->pEntitySystem->GetEntity(it->first); if (pEntity && pEntity->IsSlotValid(it->second)) { pEntity->SetSlotFlags(it->second, pEntity->GetSlotFlags(it->second)|(ENTITY_SLOT_RENDER)); if (IActor* pActor = pActorSystem->GetActor(pEnt->GetId())) { pActor->HideAllAttachments(false); } if (pEntity->GetId() == m_pVehicle->GetEntity()->GetId()) { // reset character flags if (ICharacterInstance* pCharInstance = pEntity->GetCharacter(it->second)) { pCharInstance->SetFlags(pCharInstance->GetFlags() & ~CS_FLAG_UPDATE_ALWAYS); if (ISkeletonPose* pSkeletonPose = pCharInstance->GetISkeletonPose()) pSkeletonPose->SetForceSkeletonUpdate(0); } } } } m_slotFlags.clear(); } }
////////////////////////////////////////////////////////////////////////// //this respawns the active AI at their spawn locations void CCheckpointSystem::RespawnAI(XmlNodeRef data) { if(!data) return; XmlNodeRef actorData = data->findChild(ACTOR_FLAGS_SECTION); if(!actorData) { CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "Failed reading actor data from checkpoint, actors won't be respawned"); return; } IActorSystem *pActorSystem = CCryAction::GetCryAction()->GetIActorSystem(); //first run through all actors and hide/deactivate them IActorIteratorPtr it = pActorSystem->CreateActorIterator(); while (IActor *pActor = it->Next()) { IEntity *pEntity = pActor->GetEntity(); //deactivate all actors pEntity->Hide(true); pEntity->Activate(false); } //load actorflags for active actors XmlNodeRef activatedActors = actorData->findChild(ACTIVATED_ACTORS_SECTION); if(activatedActors) { int actorFlags = activatedActors->getNumAttributes(); const char *key; const char *value; for(int i = 0; i < actorFlags; ++i) { activatedActors->getAttributeByIndex(i, &key, &value); //format is "idXXX" CRY_ASSERT(strlen(key)>2); EntityId id = (EntityId)(atoi(&key[2])); bool foundEntity = RepairEntityId(id, value); if(foundEntity) { IActor* pActor = pActorSystem->GetActor(id); if(pActor) { pActor->GetEntity()->Hide(false); pActor->GetEntity()->Activate(true); } else CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "Failed finding actor %i from checkpoint.", (int)id); } else CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "Failed finding actor %s from checkpoint, actor is not setup correctly.", value); } } else CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "Deactivated actors section was missing in checkpoint."); it = pActorSystem->CreateActorIterator(); //iterate all actors and respawn if active while (IActor *pActor = it->Next()) { IEntity *pEntity = pActor->GetEntity(); if(pEntity->GetId() == LOCAL_PLAYER_ENTITY_ID) //don't respawn player continue; //we don't respawn deactivated actors if(!pEntity->IsHidden() && pEntity->IsActive()) { pActor->SetHealth(0); pActor->Respawn(); } else //but we still reset their position { pActor->ResetToSpawnLocation(); } } }
//------------------------------------------------------------------------ void CVehicleMovementVTOL::Update(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); CVehicleMovementHelicopter::Update(deltaTime); CryAutoCriticalSection lk(m_lock); if (m_pWingsAnimation) { m_pWingsAnimation->SetTime(m_wingsAnimTime); } IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); assert(pActorSystem); IActor* pActor = pActorSystem->GetActor(m_actorId); if (pActor && pActor->IsClient()) { float turbulence = m_turbulence; if (m_pAltitudeLimitVar) { float altitudeLimit = m_pAltitudeLimitVar->GetFVal(); float currentHeight = m_pEntity->GetWorldPos().z; if (!iszero(altitudeLimit)) { float altitudeLowerOffset; if (m_pAltitudeLimitLowerOffsetVar) { float r = 1.0f - min(1.0f, max(0.0f, m_pAltitudeLimitLowerOffsetVar->GetFVal())); altitudeLowerOffset = r * altitudeLimit; if (currentHeight >= altitudeLowerOffset) { if (currentHeight > altitudeLowerOffset) { float zone = altitudeLimit - altitudeLowerOffset; turbulence += (currentHeight - altitudeLowerOffset) / (zone); } } } } } if (turbulence > 0.0f) { static_cast<CActor*>(pActor)->CameraShake(0.50f * turbulence, 0.0f, 0.05f, 0.04f, Vec3(0.0f, 0.0f, 0.0f), 10, "VTOL_Update_Turbulence"); } float enginePowerRatio = m_enginePower / m_enginePowerMax; if (enginePowerRatio > 0.0f) { float rpmScaleDesired = 0.2f; rpmScaleDesired += abs(m_forwardAction) * 0.8f; rpmScaleDesired += abs(m_strafeAction) * 0.4f; rpmScaleDesired += abs(m_turnAction) * 0.25f; rpmScaleDesired = min(1.0f, rpmScaleDesired); Interpolate(m_rpmScale, rpmScaleDesired, 1.0f, deltaTime); } float turnParamGoal = min(1.0f, abs(m_turnAction)) * 0.6f; turnParamGoal *= (min(1.0f, max(0.0f, m_speedRatio)) + 1.0f) * 0.50f; turnParamGoal += turnParamGoal * m_boost * 0.25f; Interpolate(m_soundParamTurn, turnParamGoal, 0.5f, deltaTime); SetSoundParam(eSID_Run, "turn", m_soundParamTurn); float damage = GetSoundDamage(); if (damage > 0.1f) { //if (ISound* pSound = GetOrPlaySound(eSID_Damage, 5.f, m_enginePos)) //SetSoundParam(pSound, "damage", damage); } } }
//------------------------------------------------------------------------ void CGameRulesMPDamageHandling::SvOnExplosion(const ExplosionInfo &explosionInfo, const CGameRules::TExplosionAffectedEntities& affectedEntities) { // SinglePlayer.lua 1721 IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); float totalDamage = 0.f; // calculate damage for each entity CGameRules::TExplosionAffectedEntities::const_iterator it = affectedEntities.begin(); for (; it != affectedEntities.end(); ++it) { bool success = false; IEntity* entity = it->first; float obstruction = 1.0f - it->second; bool incone=true; if (explosionInfo.angle > 0 && explosionInfo.angle < 2*3.1415f) { Vec3 explosionEntityPos = entity->GetWorldPos(); Vec3 entitypos = explosionEntityPos; float ha = explosionInfo.angle * 0.5f; Vec3 edir = entitypos - explosionInfo.pos; edir.normalize(); float dot = 1; if (edir != Vec3(ZERO)) { dot = edir.dot(explosionInfo.dir); } float angle = abs(acos_tpl(dot)); if (angle > ha) { incone = false; } } if (incone && gEnv->bServer) { float damage = explosionInfo.damage; damage = floor_tpl(0.5f + CalcExplosionDamage(entity, explosionInfo, obstruction)); bool dead = false; HitInfo explHit; Vec3 dir = entity->GetWorldPos() - explosionInfo.pos; explHit.pos = explosionInfo.pos; explHit.radius = explosionInfo.radius; explHit.partId = -1; dir.Normalize(); explHit.targetId = entity->GetId(); explHit.weaponId = explosionInfo.weaponId; explHit.shooterId = explosionInfo.shooterId; explHit.projectileId = explosionInfo.projectileId; explHit.projectileClassId = explosionInfo.projectileClassId; uint16 weaponClass = ~uint16(0); const IEntity* pWeaponEntity = gEnv->pEntitySystem->GetEntity(explosionInfo.weaponId); if (pWeaponEntity) { g_pGame->GetIGameFramework()->GetNetworkSafeClassId(weaponClass, pWeaponEntity->GetClass()->GetName()); } explHit.weaponClassId = weaponClass; explHit.material = 0; explHit.damage = damage; explHit.type = explosionInfo.type; explHit.hitViaProxy = explosionInfo.explosionViaProxy; explHit.dir = dir; explHit.normal = -dir; //set normal to reverse of direction to avoid backface cull of damage if (explHit.damage > 0.0f || explosionInfo.damage == 0.f) { CActor* pActor = static_cast<CActor*>(pActorSystem->GetActor(entity->GetId())); if (pActor) { const float damageMultiplier = pActor->GetBodyExplosionDamageMultiplier(explHit); explHit.damage *= damageMultiplier; // Protect players who are currently shielded if(pActor->IsPlayer() && static_cast<CPlayer*>(pActor)->ShouldFilterOutExplosion(explHit)) { explHit.damage = 0.0f; } } else { CInteractiveObjectEx* pInteractiveObject = static_cast<CInteractiveObjectEx*>(g_pGame->GetIGameFramework()->QueryGameObjectExtension(entity->GetId(), "InteractiveObjectEx")); if(pInteractiveObject) { pInteractiveObject->OnExploded(explHit.pos); } } if(!explosionInfo.explosionViaProxy) { if(pActor) { if(!pActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } else { IVehicle* pVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(entity->GetId()); if(pVehicle) { IActor* pDriver = pVehicle->GetDriver(); int seatNum = 0; int numSeats = pVehicle->GetSeatCount(); while(!pDriver && seatNum < numSeats) { IVehicleSeat* pSeat = pVehicle->GetSeatById(seatNum++); EntityId passengerId = pSeat ? pSeat->GetPassenger(true) : 0; pDriver = pActorSystem->GetActor(passengerId); } if(pDriver) { CActor* pDriverActor = static_cast<CActor*>(pDriver); if(!pDriverActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } } } } m_pGameRules->ServerHit(explHit); } } } if (totalDamage > 0.f) { IGameRulesPlayerStatsModule* pPlayStatsMod = m_pGameRules->GetPlayerStatsModule(); if(pPlayStatsMod) { pPlayStatsMod->ProcessSuccessfulExplosion(explosionInfo.shooterId, totalDamage, explosionInfo.projectileClassId); } } }
//------------------------------------------------------------------------ // 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; }
// //----------------------------------------------------------------------------------------------------------- // (MATT) Moved here from Scriptbind_AI when that was moved to the AI system {2008/02/15:15:23:16} int CScriptBind_Action::RegisterWithAI(IFunctionHandler *pH) { if (gEnv->bMultiplayer && !gEnv->bServer) return pH->EndFunction(); int type; ScriptHandle hdl; if (!pH->GetParams(hdl, type)) return pH->EndFunction(); EntityId entityID = (EntityId)hdl.n; IEntity *pEntity = gEnv->pEntitySystem->GetEntity(entityID); if(!pEntity) { GameWarning("RegisterWithAI: Tried to set register with AI nonExisting entity with id [%d]. ", entityID); return pH->EndFunction(); } // Apparently we can't assume that there is just one IGameObject to an entity, because we choose between (at least) Actor and Vehicle objects. // (MATT) Do we really need to check on the actor system here? {2008/02/15:18:38:34} IGameFramework *pGameFramework = gEnv->pGame->GetIGameFramework(); IVehicleSystem* pVSystem = pGameFramework->GetIVehicleSystem(); IActorSystem* pASystem = pGameFramework->GetIActorSystem(); if(!pASystem) { GameWarning("RegisterWithAI: no ActorSystem for %s.", pEntity->GetName()); return pH->EndFunction(); } AIObjectParams params(type, 0, entityID); bool autoDisable(true); // For most types, we need to parse the tables // For others we leave them blank switch (type) { case AIOBJECT_ACTOR: case AIOBJECT_2D_FLY: case AIOBJECT_BOAT: case AIOBJECT_CAR: case AIOBJECT_HELICOPTER: case AIOBJECT_INFECTED: case AIOBJECT_ALIENTICK: case AIOBJECT_HELICOPTERCRYSIS2: if(gEnv->pAISystem && ! gEnv->pAISystem->ParseTables(3, true, pH, params, autoDisable)) return pH->EndFunction(); default:; } // Most types check these, so just get them in advance IActor* pActor = pASystem->GetActor( pEntity->GetId() ); IVehicle* pVehicle = NULL; if( pVSystem ) pVehicle = pVSystem->GetVehicle( pEntity->GetId() ); // Set this if we've found something to create a proxy from IGameObject* pGameObject = NULL; switch(type) { case AIOBJECT_ACTOR: case AIOBJECT_2D_FLY: case AIOBJECT_INFECTED: case AIOBJECT_ALIENTICK: { // (MATT) The pActor/pVehicle test below - is it basically trying to distiguish between the two cases above? If so, separate them! {2008/02/15:19:38:08} if(!pActor) { GameWarning("RegisterWithAI: no Actor for %s.", pEntity->GetName()); return pH->EndFunction(); } pGameObject = pActor->GetGameObject(); } break; case AIOBJECT_BOAT: case AIOBJECT_CAR: { if(!pVehicle) { GameWarning("RegisterWithAI: no Vehicle for %s (Id %i).", pEntity->GetName(), pEntity->GetId()); return pH->EndFunction(); } pGameObject = pVehicle->GetGameObject(); } break; case AIOBJECT_HELICOPTER: case AIOBJECT_HELICOPTERCRYSIS2: { if(!pVehicle) { GameWarning("RegisterWithAI: no Vehicle for %s (Id %i).", pEntity->GetName(), pEntity->GetId()); return pH->EndFunction(); } pGameObject = pVehicle->GetGameObject(); params.m_moveAbility.b3DMove = true; } break; case AIOBJECT_PLAYER: { if(IsDemoPlayback()) return pH->EndFunction(); SmartScriptTable pTable; if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); pGameObject = pActor->GetGameObject(); pTable->GetValue("groupid",params.m_sParamStruct.m_nGroup); const char* faction = 0; if (pTable->GetValue("esFaction", faction) && gEnv->pAISystem) { params.m_sParamStruct.factionID = gEnv->pAISystem->GetFactionMap().GetFactionID(faction); if (faction && *faction && (params.m_sParamStruct.factionID == IFactionMap::InvalidFactionID)) { GameWarning("Unknown faction '%s' being set...", faction); } } else { // Márcio: backwards compatibility int species = -1; if (!pTable->GetValue("eiSpecies", species)) pTable->GetValue("species", species); if (species > -1) params.m_sParamStruct.factionID = species; } pTable->GetValue("commrange",params.m_sParamStruct.m_fCommRange); //Luciano - added to use GROUPONLY signals SmartScriptTable pPerceptionTable; if(pTable->GetValue("Perception",pPerceptionTable)) { pPerceptionTable->GetValue( "sightrange", params.m_sParamStruct.m_PerceptionParams.sightRange); } } break; case AIOBJECT_SNDSUPRESSOR: { // (MATT) This doesn't need a proxy? {2008/02/15:19:45:58} SmartScriptTable pTable; // Properties table if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); if (!pTable->GetValue("radius",params.m_moveAbility.pathRadius)) params.m_moveAbility.pathRadius = 10.f; break; } case AIOBJECT_WAYPOINT: break; /* // this block is commented out since params.m_sParamStruct is currently ignored in pEntity->RegisterInAISystem() // instead of setting the group id here, it will be set from the script right after registering default: // try to get groupid settings for anchors params.m_sParamStruct.m_nGroup = -1; params.m_sParamStruct.m_nSpecies = -1; { SmartScriptTable pTable; if ( pH->GetParamCount() > 2 ) pH->GetParam( 3, pTable ); if ( *pTable ) pTable->GetValue( "groupid", params.m_sParamStruct.m_nGroup ); } break; */ } // Remove any existing AI object pEntity->RegisterInAISystem(AIObjectParams(0)); // Register in AI to get a new AI object, deregistering the old one in the process pEntity->RegisterInAISystem(params); // (MATT) ? {2008/02/15:19:46:29} // AI object was not created (possibly AI System is disabled) if (IAIObject* aiObject = pEntity->GetAI()) { if(type==AIOBJECT_SNDSUPRESSOR) aiObject->SetRadius(params.m_moveAbility.pathRadius); else if(type>=AIANCHOR_FIRST) // if anchor - set radius { SmartScriptTable pTable; // Properties table if (pH->GetParamCount() > 2) pH->GetParam(3,pTable); else return pH->EndFunction(); float radius(0.f); pTable->GetValue("radius",radius); int groupId = -1; pTable->GetValue("groupid", groupId); aiObject->SetGroupId(groupId); aiObject->SetRadius(radius); } if (IAIActorProxy* proxy = aiObject->GetProxy()) proxy->UpdateMeAlways(!autoDisable); } return pH->EndFunction(); }
//------------------------------------------------------------------------ void CDebugGun::Update( SEntityUpdateContext& ctx, int update) { if (!IsSelected()) return; static float drawColor[4] = {1,1,1,1}; static const int dx = 5; static const int dy = 15; static const float font = 1.2f; static const float fontLarge = 1.4f; IRenderer* pRenderer = gEnv->pRenderer; IRenderAuxGeom* pAuxGeom = pRenderer->GetIRenderAuxGeom(); pAuxGeom->SetRenderFlags(e_Def3DPublicRenderflags); pRenderer->Draw2dLabel(pRenderer->GetWidth()/5.f, pRenderer->GetHeight()-35, fontLarge, drawColor, false, "Firemode: %s (%.1f)", m_fireModes[m_fireMode].first.c_str(), m_fireModes[m_fireMode].second); ray_hit rayhit; int hits = 0; unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any; if (m_fireModes[m_fireMode].first == "pierceability") { flags = (unsigned int)m_fireModes[m_fireMode].second & rwi_pierceability_mask; } // use cam, no need for firing pos/dir CCamera& cam = GetISystem()->GetViewCamera(); if (hits = gEnv->pPhysicalWorld->RayWorldIntersection(cam.GetPosition()+cam.GetViewdir(), cam.GetViewdir()*HIT_RANGE, ent_all, flags, &rayhit, 1)) { IMaterialManager* pMatMan = gEnv->p3DEngine->GetMaterialManager(); IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); IVehicleSystem* pVehicleSystem = g_pGame->GetIGameFramework()->GetIVehicleSystem(); int x = (int)(pRenderer->GetWidth() *0.5f) + dx; int y = (int)(pRenderer->GetHeight()*0.5f) + dx - dy; // draw normal ColorB colNormal(200,0,0,128); Vec3 end = rayhit.pt + 0.75f*rayhit.n; pAuxGeom->DrawLine(rayhit.pt, colNormal, end, colNormal); pAuxGeom->DrawCone(end, rayhit.n, 0.1f, 0.2f, colNormal); IEntity * pEntity = (IEntity*)rayhit.pCollider->GetForeignData(PHYS_FOREIGN_ID_ENTITY); if(pEntity) { pRenderer->Draw2dLabel(x, y+=dy, fontLarge, drawColor, false, pEntity->GetName()); } // material const char* matName = pMatMan->GetSurfaceType(rayhit.surface_idx)->GetName(); if (matName[0]) pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%s (%i)", matName, rayhit.surface_idx); pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.1f m", rayhit.dist); if (pEntity) { IScriptTable* pScriptTable = pEntity->GetScriptTable(); // physics if (IPhysicalEntity* pPhysEnt = pEntity->GetPhysics()) { pe_status_dynamics status; if (pPhysEnt->GetStatus(&status)) { if (status.mass > 0.f) pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.1f kg", status.mass); pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "pe_type: %i", pPhysEnt->GetType()); if (status.submergedFraction > 0.f) pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.2f submerged", status.submergedFraction); if (status.v.len2() > 0.0001f) pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.2f m/s", status.v.len()); } } if (pScriptTable) { HSCRIPTFUNCTION func = 0; if (pScriptTable->GetValue("GetFrozenAmount", func) && func) { float frozen = 0.f; Script::CallReturn(gEnv->pScriptSystem, func, pScriptTable, frozen); gEnv->pScriptSystem->ReleaseFunc(func); if (frozen > 0.f) pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "Frozen: %.2f", frozen); } } // class-specific stuff if (IActor* pActor = pActorSystem->GetActor(pEntity->GetId())) { pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%i health", pActor->GetHealth()); } else if (IVehicle* pVehicle = pVehicleSystem->GetVehicle(pEntity->GetId())) { const SVehicleStatus& status = pVehicle->GetStatus(); pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.0f%% health", 100.f*status.health); pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%i passengers", status.passengerCount); if (pVehicle->GetMovement() && pVehicle->GetMovement()->IsPowered()) { pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "Running"); } } else { if (pScriptTable) { HSCRIPTFUNCTION func = 0; if (pScriptTable->GetValue("GetHealth", func) && func) { float health = 0.f; if (Script::CallReturn(gEnv->pScriptSystem, func, pScriptTable, health)) { pRenderer->Draw2dLabel(x, y+=dy, font, drawColor, false, "%.0f health", health); } gEnv->pScriptSystem->ReleaseFunc(func); } } } } } }