void Player::Update() { // animate sprite m_Sprite->Animate(); // move the sprite with the hitbox glm::vec2 pos; pos.x = m_Hitbox.GetCenterPosition().x - (m_Sprite->GetSize().x / 2.f); pos.y = m_Hitbox.m_Position.y + m_Hitbox.m_Size.y - m_Sprite->GetSize().y; m_Sprite->SetPosition(pos); // update the weapon... if( IsAttacking() ) { Weapon::WEAPON_STATE w_state; switch( GetCurrentState() ) { case ATTACKING : w_state = Weapon::ATTACKING; break; case ATTACKING_UP : w_state = Weapon::ATTACKING_UP; break; case ATTACKING_DOWN : w_state = Weapon::ATTACKING_DOWN; break; } m_Weapons[m_CurrentWeaponIndex]->SetState(w_state); m_Weapons[m_CurrentWeaponIndex]->UseFrame(m_Sprite); } }
DWORD IOrderModule::Communicate (CShip *pShip, CAIBehaviorCtx &Ctx, CSpaceObject *pSender, MessageTypes iMessage, CSpaceObject *pParam1, DWORD dwParam2) // Communicate // // Handle communications from another ship { switch (iMessage) { case msgEscortAttacked: { // Treat this as an attack on ourselves DamageDesc Dummy; pShip->GetController()->OnAttacked(pParam1, Dummy); return resAck; } case msgEscortReportingIn: Ctx.SetHasEscorts(true); return resAck; case msgQueryAttackStatus: return (IsAttacking() ? resAck : resNoAnswer); default: return OnCommunicate(pShip, Ctx, pSender, iMessage, pParam1, dwParam2); } }
void Player::Draw() { if( m_CurrentState != DYING ) { GLESRenderer::Instance()->AddSprite2D(m_Sprite); GLESRenderer::Instance()->FlushSprites(); if( IsAttacking() ) { GLESRenderer::Instance()->AddSprite2D(m_Weapons[m_CurrentWeaponIndex]->GetSprite()); GLESRenderer::Instance()->FlushSprites(); } } }
bool CCSBot::IsBusy() const { if (IsAttacking() || IsBuying() || IsDefusingBomb() || GetTask() == PLANT_BOMB || GetTask() == RESCUE_HOSTAGES || IsSniping()) { return true; } return false; }
void CFighterMediator::CancelAttack(bool bCalDamageImmediately) { if(NULL==GetNormalAttackMgr()) { return; } //if (!IsAttacking()) //{ // return; //} GetNormalAttackMgr()->Cancel(bCalDamageImmediately, IsAttacking()); SetLockingTarget(NULL); CancelNormalAttackAutoTracking(); CGas2GacCPP_OnCancelNormalAttack cmd; cmd.uObjID = GetEntityID(); SendCmdToGac(&cmd, eOnlyToDirector); GetCharacter()->CppSetCtrlState(eFCS_InNormalAttacking, false); }
/*Unused for now*/ void AEnemy::UpdateCharacter() { Super::UpdateCharacter(); if (_receiverAttack) if (GetDistanceTo(_receiverAttack) < GetRangeAttack() && !IsAttacking() && !_receiverAttack->IsAttacked()) { auto timer = GetWorldTimerManager().GetTimerRate(_countdownTimerHandle); if (timer != GetFlipbook(Attack_Animation)->GetTotalDuration()) { SetAttacking(true); GetCharacterMovement()->StopMovementImmediately(); GetWorldTimerManager().SetTimer(_countdownTimerHandle, this, &AAzraelCharacter::Attacking, GetFlipbook(Attack_Animation)->GetTotalDuration()/2.0f, false); } } else { _receiverAttack->SetAttacked(false); } if (IsPawnJumping()) GetCharacterMovement()->SetMovementMode(MOVE_Falling); else if (_isPatrolling && !_isImmobile) { GetCharacterMovement()->Velocity = FVector(GetPawnDirection()*-100.f, 0.f, 0.f); } }
void CCSBot::__MAKE_VHOOK(OnEvent)(GameEventType event, CBaseEntity *entity, CBaseEntity *other) { GetGameState()->OnEvent(event, entity, other); GetChatter()->OnEvent(event, entity, other); // Morale adjustments happen even for dead players switch (event) { case EVENT_TERRORISTS_WIN: if (m_iTeam == CT) { DecreaseMorale(); } else { IncreaseMorale(); } break; case EVENT_CTS_WIN: if (m_iTeam == CT) { IncreaseMorale(); } else { DecreaseMorale(); } break; } if (!IsAlive()) return; CBasePlayer *player = static_cast<CBasePlayer *>(entity); // If we just saw a nearby friend die, and we haven't yet acquired an enemy // automatically acquire our dead friend's killer if (!IsAttacking() && (GetDisposition() == ENGAGE_AND_INVESTIGATE || GetDisposition() == OPPORTUNITY_FIRE)) { if (event == EVENT_PLAYER_DIED) { if (BotRelationship(player) == BOT_TEAMMATE) { CBasePlayer *killer = static_cast<CBasePlayer *>(other); // check that attacker is an enemy (for friendly fire, etc) if (killer != NULL && killer->IsPlayer()) { // check if we saw our friend die - dont check FOV - assume we're aware of our surroundings in combat // snipers stay put if (!IsSniper() && IsVisible(&player->pev->origin)) { // people are dying - we should hurry Hurry(RANDOM_FLOAT(10.0f, 15.0f)); // if we're hiding with only our knife, be a little more cautious const float knifeAmbushChance = 50.0f; if (!IsHiding() || !IsUsingKnife() || RANDOM_FLOAT(0, 100) < knifeAmbushChance) { PrintIfWatched("Attacking our friend's killer!\n"); Attack(killer); return; } } } } } } switch (event) { case EVENT_PLAYER_DIED: { CBasePlayer *victim = player; CBasePlayer *killer = (other != NULL && other->IsPlayer()) ? static_cast<CBasePlayer *>(other) : NULL; // if the human player died in the single player game, tell the team if (CSGameRules()->IsCareer() && !victim->IsBot() && BotRelationship(victim) == BOT_TEAMMATE) { GetChatter()->Say("CommanderDown", 20.0f); } // keep track of the last player we killed if (killer == this) { m_lastVictimID = victim->entindex(); } // react to teammate death if (BotRelationship(victim) == BOT_TEAMMATE) { // chastise friendly fire from humans if (killer != NULL && !killer->IsBot() && BotRelationship(killer) == BOT_TEAMMATE && killer != this) { GetChatter()->KilledFriend(); } if (IsHunting()) { PrintIfWatched("Rethinking hunt due to teammate death\n"); Idle(); return; } if (IsAttacking()) { if (GetTimeSinceLastSawEnemy() > 0.4f) { PrintIfWatched("Rethinking my attack due to teammate death\n"); // allow us to sneak past windows, doors, etc IgnoreEnemies(1.0f); // move to last known position of enemy - this could cause us to flank if // the danger has changed due to our teammate's recent death SetTask(MOVE_TO_LAST_KNOWN_ENEMY_POSITION, GetEnemy()); MoveTo(&GetLastKnownEnemyPosition()); return; } } } // an enemy was killed else { if (killer != NULL && BotRelationship(killer) == BOT_TEAMMATE) { // only chatter about enemy kills if we see them occur, and they were the last one we see if (GetNearbyEnemyCount() <= 1) { // report if number of enemies left is few and we killed the last one we saw locally GetChatter()->EnemiesRemaining(); if (IsVisible(&victim->pev->origin, CHECK_FOV)) { // congratulate teammates on their kills if (killer != this) { float delay = RANDOM_FLOAT(2.0f, 3.0f); if (killer->IsBot()) { if (RANDOM_FLOAT(0.0f, 100.0f) < 40.0f) GetChatter()->Say("NiceShot", 3.0f, delay); } else { // humans get the honorific if (CSGameRules()->IsCareer()) GetChatter()->Say("NiceShotCommander", 3.0f, delay); else GetChatter()->Say("NiceShotSir", 3.0f, delay); } } } } } } return; } case EVENT_TERRORISTS_WIN: if (m_iTeam == TERRORIST) GetChatter()->CelebrateWin(); return; case EVENT_CTS_WIN: if (m_iTeam == CT) GetChatter()->CelebrateWin(); return; case EVENT_BOMB_DEFUSED: if (m_iTeam == CT && TheCSBots()->GetBombTimeLeft() < 2.0) GetChatter()->Say("BarelyDefused"); return; case EVENT_BOMB_PICKED_UP: { if (m_iTeam == CT && player != NULL) { // check if we're close enough to hear it const float bombPickupHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - player->pev->origin).LengthSquared() < bombPickupHearRangeSq) { GetChatter()->TheyPickedUpTheBomb(); } } return; } case EVENT_BOMB_BEEP: { // if we don't know where the bomb is, but heard it beep, we've discovered it if (GetGameState()->IsPlantedBombLocationKnown() == false) { // check if we're close enough to hear it const float bombBeepHearRangeSq = 1000.0f * 1000.0f; if ((pev->origin - entity->pev->origin).LengthSquared() < bombBeepHearRangeSq) { // radio the news to our team if (m_iTeam == CT && GetGameState()->GetPlantedBombsite() == CSGameState::UNKNOWN) { const CCSBotManager::Zone *zone = TheCSBots()->GetZone(&entity->pev->origin); if (zone != NULL) GetChatter()->FoundPlantedBomb(zone->m_index); } // remember where the bomb is GetGameState()->UpdatePlantedBomb(&entity->pev->origin); } } return; } case EVENT_BOMB_PLANTED: { // if we're a CT, forget what we're doing and go after the bomb if (m_iTeam == CT) { Idle(); } // if we are following someone, stop following if (IsFollowing()) { StopFollowing(); Idle(); } OnEvent(EVENT_BOMB_BEEP, other); return; } case EVENT_BOMB_DEFUSE_ABORTED: PrintIfWatched("BOMB DEFUSE ABORTED\n"); return; case EVENT_WEAPON_FIRED: case EVENT_WEAPON_FIRED_ON_EMPTY: case EVENT_WEAPON_RELOADED: { if (m_enemy == entity && IsUsingKnife()) ForceRun(5.0f); break; } default: break; } // Process radio events from our team if (player != NULL && BotRelationship(player) == BOT_TEAMMATE && event > EVENT_START_RADIO_1 && event < EVENT_END_RADIO) { // TODO: Distinguish between radio commands and responses if (event != EVENT_RADIO_AFFIRMATIVE && event != EVENT_RADIO_NEGATIVE && event != EVENT_RADIO_REPORTING_IN) { m_lastRadioCommand = event; m_lastRadioRecievedTimestamp = gpGlobals->time; m_radioSubject = player; m_radioPosition = player->pev->origin; } } // player_follows needs a player if (player == NULL) return; if (!IsRogue() && event == EVENT_HOSTAGE_CALLED_FOR_HELP && m_iTeam == CT && IsHunting()) { if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(1000.0f)) return; if (IsVisible(&entity->Center())) { m_task = COLLECT_HOSTAGES; m_taskEntity = NULL; Run(); m_goalEntity = entity; MoveTo(&entity->pev->origin, m_hostageEscortCount == 0 ? SAFEST_ROUTE : FASTEST_ROUTE); PrintIfWatched("I'm fetching a hostage that called out to me\n"); return; } } // don't pay attention to noise that friends make if (!IsEnemy(player)) return; float range; PriorityType priority; bool isHostile; if (IsGameEventAudible(event, entity, other, &range, &priority, &isHostile) == false) return; if (event == EVENT_HOSTAGE_USED) { if (m_iTeam == CT) return; if ((entity->pev->origin - pev->origin).IsLengthGreaterThan(range)) return; GetChatter()->HostagesBeingTaken(); if (!GetGameState()->GetNearestVisibleFreeHostage() && m_task != GUARD_HOSTAGE_RESCUE_ZONE && GuardRandomZone()) { m_task = GUARD_HOSTAGE_RESCUE_ZONE; m_taskEntity = NULL; SetDisposition(OPPORTUNITY_FIRE); PrintIfWatched("Trying to beat them to an escape zone!\n"); } } // check if noise is close enough for us to hear const Vector *newNoisePosition = &player->pev->origin; float newNoiseDist = (pev->origin - *newNoisePosition).Length(); if (newNoiseDist < range) { // we heard the sound if ((IsLocalPlayerWatchingMe() && cv_bot_debug.value == 3.0f) || cv_bot_debug.value == 4.0f) { PrintIfWatched("Heard noise (%s from %s, pri %s, time %3.1f)\n", (event == EVENT_WEAPON_FIRED) ? "Weapon fire " : "", STRING(player->pev->netname), (priority == PRIORITY_HIGH) ? "HIGH" : ((priority == PRIORITY_MEDIUM) ? "MEDIUM" : "LOW"), gpGlobals->time); } if (event == EVENT_PLAYER_FOOTSTEP && IsUsingSniperRifle() && newNoiseDist < 300.0) EquipPistol(); // should we pay attention to it // if noise timestamp is zero, there is no prior noise if (m_noiseTimestamp > 0.0f) { // only overwrite recent sound if we are louder (closer), or more important - if old noise was long ago, its faded const float shortTermMemoryTime = 3.0f; if (gpGlobals->time - m_noiseTimestamp < shortTermMemoryTime) { // prior noise is more important - ignore new one if (priority < m_noisePriority) return; float oldNoiseDist = (pev->origin - m_noisePosition).Length(); if (newNoiseDist >= oldNoiseDist) return; } } // find the area in which the noise occured // TODO: Better handle when noise occurs off the nav mesh // TODO: Make sure noise area is not through a wall or ceiling from source of noise // TODO: Change GetNavTravelTime to better deal with NULL destination areas CNavArea *noiseArea = TheNavAreaGrid.GetNavArea(newNoisePosition); if (noiseArea == NULL) noiseArea = TheNavAreaGrid.GetNearestNavArea(newNoisePosition); if (noiseArea == NULL) { PrintIfWatched(" *** Noise occurred off the nav mesh - ignoring!\n"); return; } m_noiseArea = noiseArea; // remember noise priority m_noisePriority = priority; // randomize noise position in the area a bit - hearing isn't very accurate // the closer the noise is, the more accurate our placement // TODO: Make sure not to pick a position on the opposite side of ourselves. const float maxErrorRadius = 400.0f; const float maxHearingRange = 2000.0f; float errorRadius = maxErrorRadius * newNoiseDist / maxHearingRange; m_noisePosition.x = newNoisePosition->x + RANDOM_FLOAT(-errorRadius, errorRadius); m_noisePosition.y = newNoisePosition->y + RANDOM_FLOAT(-errorRadius, errorRadius); // make sure noise position remains in the same area m_noiseArea->GetClosestPointOnArea(&m_noisePosition, &m_noisePosition); m_isNoiseTravelRangeChecked = false; // note when we heard the noise m_noiseTimestamp = gpGlobals->time; } }
// Move actual view angles towards desired ones. // This is the only place v_angle is altered. // TODO: Make stiffness and turn rate constants timestep invariant. void CCSBot::UpdateLookAngles() { const float deltaT = g_flBotCommandInterval; float maxAccel; float stiffness; float damping; // springs are stiffer when attacking, so we can track and move between targets better if (IsAttacking()) { stiffness = 300.0f; damping = 30.0f; maxAccel = 3000.0f; } else { stiffness = 200.0f; damping = 25.0f; maxAccel = 3000.0f; } // these may be overridden by ladder logic float useYaw = m_lookYaw; float usePitch = m_lookPitch; // Ladders require precise movement, therefore we need to look at the // ladder as we approach and ascend/descend it. // If we are on a ladder, we need to look up or down to traverse it - override pitch in this case. // If we're trying to break something, though, we actually need to look at it before we can // look at the ladder if (IsUsingLadder()) { // set yaw to aim at ladder Vector to = m_pathLadder->m_top - pev->origin; float idealYaw = UTIL_VecToYaw(to); NavDirType faceDir = m_pathLadder->m_dir; if (m_pathLadderFaceIn) { faceDir = OppositeDirection(faceDir); } const float lookAlongLadderRange = 100.0f; const float ladderPitch = 60.0f; // adjust pitch to look up/down ladder as we ascend/descend switch (m_pathLadderState) { case APPROACH_ASCENDING_LADDER: { Vector to = m_goalPosition - pev->origin; useYaw = idealYaw; if (to.IsLengthLessThan(lookAlongLadderRange)) usePitch = -ladderPitch; break; } case APPROACH_DESCENDING_LADDER: { Vector to = m_goalPosition - pev->origin; useYaw = idealYaw; if (to.IsLengthLessThan(lookAlongLadderRange)) usePitch = ladderPitch; break; } case FACE_ASCENDING_LADDER: { useYaw = idealYaw; usePitch = -ladderPitch; break; } case FACE_DESCENDING_LADDER: { useYaw = idealYaw; usePitch = ladderPitch; break; } case MOUNT_ASCENDING_LADDER: case ASCEND_LADDER: { useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder); usePitch = -ladderPitch; break; } case MOUNT_DESCENDING_LADDER: case DESCEND_LADDER: { useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder); usePitch = ladderPitch; break; } case DISMOUNT_ASCENDING_LADDER: case DISMOUNT_DESCENDING_LADDER: { useYaw = DirectionToAngle(faceDir); break; } } } // Yaw float angleDiff = NormalizeAngle(useYaw - pev->v_angle.y); // if almost at target angle, snap to it const float onTargetTolerance = 1.0f; if (angleDiff < onTargetTolerance && angleDiff > -onTargetTolerance) { m_lookYawVel = 0.0f; pev->v_angle.y = useYaw; } else { // simple angular spring/damper float accel = stiffness * angleDiff - damping * m_lookYawVel; // limit rate if (accel > maxAccel) accel = maxAccel; else if (accel < -maxAccel) accel = -maxAccel; m_lookYawVel += deltaT * accel; pev->v_angle.y += deltaT * m_lookYawVel; } // Pitch // Actually, this is negative pitch. angleDiff = usePitch - pev->v_angle.x; angleDiff = NormalizeAngle(angleDiff); if (false && angleDiff < onTargetTolerance && angleDiff > -onTargetTolerance) { m_lookPitchVel = 0.0f; pev->v_angle.x = usePitch; } else { // simple angular spring/damper // double the stiffness since pitch is only +/- 90 and yaw is +/- 180 float accel = 2.0f * stiffness * angleDiff - damping * m_lookPitchVel; // limit rate if (accel > maxAccel) accel = maxAccel; else if (accel < -maxAccel) accel = -maxAccel; m_lookPitchVel += deltaT * accel; pev->v_angle.x += deltaT * m_lookPitchVel; } // limit range - avoid gimbal lock if (pev->v_angle.x < -89.0f) pev->v_angle.x = -89.0f; else if (pev->v_angle.x > 89.0f) pev->v_angle.x = 89.0f; pev->v_angle.z = 0.0f; }
void CCharacter::RefreshBuff( ) { bool bflag = false; for( UINT i=0;i<32;i++) { if(MagicStatus[i].Buff == 0) continue; clock_t etime = clock() - MagicStatus[i].BuffTime; if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC ) { Log(MSG_INFO,"Magic Status %i, vanish after: %i", MagicStatus[i].Status, MagicStatus[i].Duration); switch(MagicStatus[i].Status) { case STATUS_HP_8: case STATUS_HP_18: case STATUS_HP_30: case STATUS_HP_43: case STATUS_HP_58: case STATUS_HP_75: case STATUS_SUMMON_HP: case STATUS_HP_92: case STATUS_HP_111: case STATUS_HP_131: case STATUS_HP_152: case STATUS_HP_174: case STATUS_HP_197: case STATUS_HP_221: case STATUS_HP_246: case STATUS_HP_272: case STATUS_HP_299: case STATUS_HP_326: case STATUS_HP_354: case STATUS_HP_383: case STATUS_HP_413: case STATUS_HP_443: case STATUS_HP_475: case STATUS_HP_539: case STATUS_HP_572: case STATUS_HP_5_PC: case 115: { Status->HPHeal = 0xff; } break; case STATUS_MP_6: case STATUS_MP_10: case STATUS_MP_14: case STATUS_MP_18: case STATUS_MP_21: case STATUS_MP_25: case STATUS_MP_29: case STATUS_MP_33: case STATUS_MP_36: case STATUS_MP_40: case STATUS_MP_44: case STATUS_MP_48: case STATUS_MP_51: case STATUS_MP_55: case STATUS_MP_59: case STATUS_MP_63: case STATUS_MP_66: case STATUS_MP_70: case STATUS_MP_74: case STATUS_MP_78: case STATUS_MP_81: case STATUS_MP_85: case STATUS_MP_89: case STATUS_MP_93: case STATUS_MP_96: case 116: { Status->MPHeal = 0xff; } break; case 18: // attack power up case 19: // attack power down case 48: // attack power up { if(i == BUFF_ATTACK_UP_POS) { Status->Attack_up = 0xff; } else { Status->Attack_down = 0xff; } Stats->Attack_Power = GetAttackPower( ); } break; case 20: // def up case 21: // def down case 49: // def up { if(i==BUFF_DEFENSE_UP_POS) { Status->Defense_up= 0xff; } else { Status->Defense_down = 0xff; } Stats->Defense = GetDefense( ); } break; case 24: //Accuracy up case 25: //Accuracy down case 51: //attack Accuracy up. { if(i==BUFF_HITRATE_UP_POS) { Status->Accuracy_up= 0xff; } else { Status->Accuracy_down = 0xff; } Stats->Accuracy = GetAccuracy( ); } break; case 22: // macic resistance up case 23: // magic resistance down case 50: // magic resistance up { if(i==BUFF_MDEFENSE_UP_POS) { Status->Magic_Defense_up = 0xff; } else { Status->Magic_Defense_down = 0xff; } Stats->Magic_Defense = GetMagicDefense( ); } break; case 28: //dodge up case 29: //dodge down case 53: //dodge rate up { if(i==BUFF_DODGE_UP_POS) { Status->Dodge_up = 0xff; } else { Status->Dodge_down = 0xff; } Stats->Dodge = GetDodge( ); } break; case 14: //dash case 15: //slow case 46: //movement speed increased { if(i==BUFF_DASH_UP_POS) { Status->Dash_up = 0xff; } else { Status->Dash_down = 0xff; } Stats->Move_Speed = GetMoveSpeed( ); } break; case 16: // haste attack case 17: // slow attack case 47: // attack speed up { if(i==BUFF_HASTE_UP_POS) { Status->Haste_up = 0xff; } else { Status->Haste_down = 0xff; } //Stats->Attack_Speed_Percent = GetAttackSpeedPercent( ); } break; case 26: // crit up case 27: // crit down case 52: // crit up { if(i==BUFF_CRITICAL_UP_POS) { Status->Critical_up = 0xff; } else { Status->Critical_down = 0xff; } Stats->Critical = GetCritical( ); } break; case 12: // max HP up case 44: // max HP up { if(i==BUFF_MAX_HP_POS) { Status->HP_up = 0xff; } else { Status->HP_down = 0xff; } Stats->MaxHP = GetMaxHP( ); if(Stats->HP > Stats->MaxHP) { Stats->HP = Stats->MaxHP; } } break; case 13: // max MP up case 45: // max MP up { if(i==BUFF_MAX_MP_POS) { Status->MP_up = 0xff; } else { Status->MP_down = 0xff; } Stats->MaxMP = GetMaxMP( ); if(Stats->MP > Stats->MaxMP) { Stats->MP = Stats->MaxMP; } } break; case 32: // faint { Status->Faint = 0xff; Status->CanMove = true; Status->CanCastSkill = true; Status->CanAttack = true; //printf("removing stun\n"); } break; case 7: case 8: case 9: case 10: case 11: case 89: //poisoned { Status->Poisoned = 0xff; //printf("removing poison\n"); } break; case 30: // muted { Status->Muted = 0xff; Status->CanCastSkill = true; } break; case 31: // sleep May need to be fixed later to accomodate multiple status effects. { Status->Sleep = 0xff; Status->CanMove = true; Status->CanCastSkill = true; Status->CanAttack = true; } break; case 36: //A_Extra_Damage: case 54: //A_GMExtra_Damage: case 83: //Valkyrie Charm: { if(i==BUFF_DUMMY_DAMAGE_POS) { Status->ExtraDamage_up = 0xff; Stats->ExtraDamage_add = 0;//We put extardamage add value to 0 if we lost the adddmg buff } else { Status->ExtraDamage_down = 0xff; Stats->ExtraDamage_add = 0;//We put extardamage add value to 0 if we lost the adddmg buff } } break; case 56: //Taunt { Status->Taunt = 0xff; //printf("removing Taunt\n"); } break; case 58: case 61: case 74: case 77: case 78: case 79: case 80: //flame { Status->Flamed = 0xff; } break; case 33://Stealth,Camoflauge { if(IsAttacking( )) { MagicStatus[i].Duration = 0; } Status->Stealth = 0xff; //printf("removing Stealth\n"); } break; case 86://Stealth,Weary { Status->Weary = 0xff; Status->CanCastSkill = true; //printf("removing Weary\n"); } break; case 34://Cloaking { Status->Cloaking = 0xff; //printf("removing Cloaking\n"); } break; case 35: //ShieldDamage: { if(i==BUFF_SHIELD_DAMAGE_POS) { Status->ShieldDamage_up = 0xff; //Stats->ShieldDamage = 0xff; } else { Status->ShieldDamage_down = 0xff; //Stats->ShieldDamage = 0xff; } } break; case 55://Detect { Status->Detect = 0xff; Status->Cloaking = 0xff; Status->Stealth = 0xff; //printf("Detect Done\n"); } break; case 38://Purify { //Buff_Down Status->Attack_down = 0xff; Status->Defense_down = 0xff; Status->Accuracy_down = 0xff; Status->Magic_Defense_down = 0xff; Status->Dodge_down = 0xff; Status->Dash_down = 0xff; Status->Haste_down = 0xff; Status->Critical_down = 0xff; Status->HP_down = 0xff; Status->MP_down = 0xff; Status->ExtraDamage_down = 0xff; Status->ShieldDamage_down = 0xff; Status->ALL_down = 0xff; //Bad Status //Status->Stun = 0xff; Status->Poisoned = 0xff; Status->Muted = 0xff; Status->Sleep = 0xff; Status->Flamed = 0xff; Status->Faint = 0xff; //Stats Stats->Attack_Power = GetAttackPower( ); Stats->Defense = GetDefense( ); Stats->Accuracy = GetAccuracy( ); Stats->Magic_Defense = GetMagicDefense( ); Stats->Dodge = GetDodge( ); Stats->Move_Speed = GetMoveSpeed( ); Stats->Attack_Speed = GetAttackSpeed( ); //Stats->Attack_Speed_Percent = GetAttackSpeedPercent(); Stats->Critical = GetCritical( ); Stats->MaxHP = GetMaxHP( ); Stats->MaxMP = GetMaxMP( ); //printf("Purify Done\n"); } break; case 39:// Dispell { //Buff_Down Status->Attack_down = 0xff; Status->Defense_down = 0xff; Status->Accuracy_down = 0xff; Status->Magic_Defense_down = 0xff; Status->Dodge_down = 0xff; Status->Dash_down = 0xff; Status->Haste_down = 0xff; Status->Critical_down = 0xff; Status->HP_down = 0xff; Status->MP_down = 0xff; Status->ExtraDamage_down = 0xff; Status->ShieldDamage_down = 0xff; Status->ALL_down = 0xff; //Buff_Up Status->Attack_up = 0xff; Status->Defense_up = 0xff; Status->Accuracy_up = 0xff; Status->Magic_Defense_up = 0xff; Status->Dodge_up = 0xff; Status->Dash_up = 0xff; Status->Haste_up = 0xff; Status->Critical_up = 0xff; Status->HP_up = 0xff; Status->MP_up = 0xff; Status->ExtraDamage_up = 0xff; Status->ShieldDamage_up = 0xff; Status->ALL_up = 0xff; //Bad Status //Status->Stun = 0xff; Status->Poisoned = 0xff; Status->Muted = 0xff; Status->Sleep = 0xff; Status->Flamed = 0xff; Status->Faint = 0xff; //Stats Stats->Attack_Power = GetAttackPower( ); Stats->Defense = GetDefense( ); Stats->Accuracy = GetAccuracy( ); Stats->Magic_Defense = GetMagicDefense( ); Stats->Dodge = GetDodge( ); Stats->Move_Speed = GetMoveSpeed( ); Stats->Attack_Speed = GetAttackSpeed( ); Stats->Critical = GetCritical( ); Stats->MaxHP = GetMaxHP( ); Stats->MaxMP = GetMaxMP( ); //printf("Dispell Done\n"); } break; default: { Log(MSG_WARNING,"Unknow skill status in charfunctions %u.",MagicStatus[i].Status); } } BEGINPACKET( pak,0x7b7 ); ADDWORD ( pak, clientid ); ADDDWORD ( pak, GServer->BuildBuffs( this ) ); GServer->SendToVisible( &pak, this ); MagicStatus[i].Status = 0; MagicStatus[i].Buff = 0; MagicStatus[i].BuffTime = 0; MagicStatus[i].Duration = 0; MagicStatus[i].Value = 0; bflag = true; } else if ( ((MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11) || MagicStatus[i].Status == 89) && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds { Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself, LMA: can be 89 noc too. MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC; MagicStatus[i].Duration -= 1; //printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); //Log(MSG_WARNING,"did %i poison dmg to the player / monster, still %i seconds and %I64i HP remain", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); //LMA: If dead, let's the client resynch if(IsDead()) { BEGINPACKET( pak, 0x79f ); ADDWORD ( pak, clientid ); ADDDWORD ( pak, 1); GServer->SendToVisible( &pak, this ); Log(MSG_INFO,"death poison for %i, amount: %i",clientid,MagicStatus[i].Status); } //A bunch of messy code to send dmg packet BEGINPACKET( pak, 0x7b6 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, 0 ); ADDDWORD ( pak, 0x000007f8 ); ADDBYTE ( pak, 0x00 ); ADDDWORD ( pak, MagicStatus[i].Status ); //If Enemy is killed if( IsDead()) { //printf("char died\n"); CDrop* thisdrop = NULL; ADDDWORD ( pak, 16 ); if( !IsSummon( ) && !IsPlayer( )) { //LMA: No drop if already dead and drop done. if(drop_dead) { Log(MSG_WARNING,"Trying to make a monster (CID %u, type %u) drop again but already did.",clientid,char_montype); } else { thisdrop = GetDrop( ); if( thisdrop!=NULL) { CMap* map = GServer->MapList.Index[thisdrop->posMap]; map->AddDrop( thisdrop ); } } } //GServer->SendToVisible( &pak, this, thisdrop ); GServer->SendToVisible( &pak, this); } else { //If enemy is still alive ADDDWORD ( pak, 4 ); GServer->SendToVisible( &pak, this ); } } else if ( MagicStatus[i].Status == 58 || MagicStatus[i].Status == 61 || MagicStatus[i].Status == 71 || MagicStatus[i].Status >= 77 && MagicStatus[i].Status <= 80 || MagicStatus[i].Status == 88 && etime > 1*CLOCKS_PER_SEC) //Do flame dmg every 1.5 seconds { Stats->HP -= MagicStatus[i].Status; MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC; MagicStatus[i].Duration -= 1; printf("did %i flame dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); //LMA: If dead, let's the client resynch if(IsDead()) { BEGINPACKET( pak, 0x79f ); ADDWORD ( pak, clientid ); ADDDWORD ( pak, 1); GServer->SendToVisible( &pak, this ); Log(MSG_INFO,"death flame for %i, amount: %i",clientid,MagicStatus[i].Status); } //A bunch of messy code to send dmg packet BEGINPACKET( pak, 0x7b6 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, 0 ); ADDDWORD ( pak, 0x000007f8 ); ADDBYTE ( pak, 0x00 ); ADDDWORD ( pak, MagicStatus[i].Status ); //If Enemy is killed if( IsDead()) { //printf("char died\n"); CDrop* thisdrop = NULL; ADDDWORD ( pak, 16 ); if( !IsSummon( ) && !IsPlayer( )) { //LMA: No drop if already dead and drop done. if(drop_dead) { Log(MSG_WARNING,"Trying to make a monster (CID %u, type %u) drop again but already did.",clientid,char_montype); } else { thisdrop = GetDrop( ); if( thisdrop!=NULL) { CMap* map = GServer->MapList.Index[thisdrop->posMap]; map->AddDrop( thisdrop ); } } } //GServer->SendToVisible( &pak, this, thisdrop ); GServer->SendToVisible( &pak, this); } else { //If enemy is still alive ADDDWORD ( pak, 4 ); GServer->SendToVisible( &pak, this ); } } } for( UINT i=0;i<32;i++) { if(MagicStatus2[i].Buff == 0) continue; clock_t etime = clock() - MagicStatus2[i].BuffTime; if( etime >= MagicStatus2[i].Duration * CLOCKS_PER_SEC ) { CPlayer* thisplayer = GServer->GetClientByID(clientid); Log(MSG_INFO,"Magic Status %i, vanish after: %i", MagicStatus2[i].Status, MagicStatus2[i].Duration); switch(MagicStatus2[i].Status) { case 126: // ALLbuff up { if(i == BUFF_ATTACK_UP_POS) { Status->Attack_up = 0xff; thisplayer->Attr->ALLbuff = 0; } else { Status->Attack_down = 0xff; thisplayer->Attr->ALLbuff = 0; } } default: { Log(MSG_WARNING,"Unknow skill status in charfunctions %u.",MagicStatus[i].Status); } } BEGINPACKET( pak,0x7b7 ); ADDWORD ( pak, clientid ); ADDDWORD ( pak, GServer->BuildBuffs( this ) ); GServer->SendToVisible( &pak, this ); MagicStatus2[i].Status = 0; MagicStatus2[i].Buff = 0; MagicStatus2[i].BuffTime = 0; MagicStatus2[i].Duration = 0; MagicStatus2[i].Value = 0; } } // if(bflag) // { // BEGINPACKET( pak,0x7b7 ); // ADDWORD ( pak, clientid ); // ADDDWORD ( pak, GServer->BuildBuffs( this ) ); // GServer->SendToVisible( &pak, this ); // } }
// Move along the path. Return false if end of path reached. CCSBot::PathResult CCSBot::UpdatePathMovement(bool allowSpeedChange) { if (m_pathLength == 0) return PATH_FAILURE; if (cv_bot_walk.value != 0.0f) Walk(); // If we are navigating a ladder, it overrides all other path movement until complete if (UpdateLadderMovement()) return PROGRESSING; // ladder failure can destroy the path if (m_pathLength == 0) return PATH_FAILURE; // we are not supposed to be on a ladder - if we are, jump off if (IsOnLadder()) Jump(MUST_JUMP); assert(m_pathIndex < m_pathLength); // Check if reached the end of the path bool nearEndOfPath = false; if (m_pathIndex >= m_pathLength - 1) { Vector toEnd(pev->origin.x, pev->origin.y, GetFeetZ()); Vector d = GetPathEndpoint() - toEnd; // can't use 2D because path end may be below us (jump down) const float walkRange = 200.0f; // walk as we get close to the goal position to ensure we hit it if (d.IsLengthLessThan(walkRange)) { // don't walk if crouching - too slow if (allowSpeedChange && !IsCrouching()) Walk(); // note if we are near the end of the path const float nearEndRange = 50.0f; if (d.IsLengthLessThan(nearEndRange)) nearEndOfPath = true; const float closeEpsilon = 20.0f; if (d.IsLengthLessThan(closeEpsilon)) { // reached goal position - path complete DestroyPath(); // TODO: We should push and pop walk state here, in case we want to continue walking after reaching goal if (allowSpeedChange) Run(); return END_OF_PATH; } } } // To keep us moving smoothly, we will move towards // a point farther ahead of us down our path. int prevIndex = 0; // closest index on path just prior to where we are now const float aheadRange = 300.0f; int newIndex = FindPathPoint(aheadRange, &m_goalPosition, &prevIndex); // BOTPORT: Why is prevIndex sometimes -1? if (prevIndex < 0) prevIndex = 0; // if goal position is near to us, we must be about to go around a corner - so look ahead! const float nearCornerRange = 100.0f; if (m_pathIndex < m_pathLength - 1 && (m_goalPosition - pev->origin).IsLengthLessThan(nearCornerRange)) { ClearLookAt(); InhibitLookAround(0.5f); } // if we moved to a new node on the path, setup movement if (newIndex > m_pathIndex) { SetPathIndex(newIndex); } if (!IsUsingLadder()) { // Crouching // if we are approaching a crouch area, crouch // if there are no crouch areas coming up, stand const float crouchRange = 50.0f; bool didCrouch = false; for (int i = prevIndex; i < m_pathLength; i++) { const CNavArea *to = m_path[i].area; // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump // unless we are already higher than the jump area - we must've jumped already but not moved into next area if ((to->GetAttributes() & NAV_JUMP)/* && to->GetCenter()->z > GetFeetZ()*/) break; Vector close; to->GetClosestPointOnArea(&pev->origin, &close); if ((close - pev->origin).Make2D().IsLengthGreaterThan(crouchRange)) break; if (to->GetAttributes() & NAV_CROUCH) { Crouch(); didCrouch = true; break; } } if (!didCrouch && !IsJumping()) { // no crouch areas coming up StandUp(); } // end crouching logic } // compute our forward facing angle m_forwardAngle = UTIL_VecToYaw(m_goalPosition - pev->origin); // Look farther down the path to "lead" our view around corners Vector toGoal; if (m_pathIndex == 0) { toGoal = m_path[1].pos; } else if (m_pathIndex < m_pathLength) { toGoal = m_path[m_pathIndex].pos - pev->origin; // actually aim our view farther down the path const float lookAheadRange = 500.0f; if (!m_path[m_pathIndex].ladder && !IsNearJump() && toGoal.Make2D().IsLengthLessThan(lookAheadRange)) { float along = toGoal.Length2D(); int i; for (i = m_pathIndex + 1; i < m_pathLength; i++) { Vector delta = m_path[i].pos - m_path[i - 1].pos; float segmentLength = delta.Length2D(); if (along + segmentLength >= lookAheadRange) { // interpolate between points to keep look ahead point at fixed distance float t = (lookAheadRange - along) / (segmentLength + along); Vector target; if (t <= 0.0f) target = m_path[i - 1].pos; else if (t >= 1.0f) target = m_path[i].pos; else target = m_path[i - 1].pos + t * delta; toGoal = target - pev->origin; break; } // if we are coming up to a ladder or a jump, look at it if (m_path[i].ladder || (m_path[i].area->GetAttributes() & NAV_JUMP)) { toGoal = m_path[i].pos - pev->origin; break; } along += segmentLength; } if (i == m_pathLength) { toGoal = GetPathEndpoint() - pev->origin; } } } else { toGoal = GetPathEndpoint() - pev->origin; } m_lookAheadAngle = UTIL_VecToYaw(toGoal); // initialize "adjusted" goal to current goal Vector adjustedGoal = m_goalPosition; // Use short "feelers" to veer away from close-range obstacles // Feelers come from our ankles, just above StepHeight, so we avoid short walls, too // Don't use feelers if very near the end of the path, or about to jump // TODO: Consider having feelers at several heights to deal with overhangs, etc. if (!nearEndOfPath && !IsNearJump() && !IsJumping()) { FeelerReflexAdjustment(&adjustedGoal); } // draw debug visualization if ((cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 10.0f) { DrawPath(); const Vector *pos = &m_path[m_pathIndex].pos; UTIL_DrawBeamPoints(*pos, *pos + Vector(0, 0, 50), 1, 255, 255, 0); UTIL_DrawBeamPoints(adjustedGoal, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); UTIL_DrawBeamPoints(pev->origin, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); } // dont use adjustedGoal, as it can vary wildly from the feeler adjustment if (!IsAttacking() && IsFriendInTheWay(&m_goalPosition)) { if (!m_isWaitingBehindFriend) { m_isWaitingBehindFriend = true; const float politeDuration = 5.0f - 3.0f * GetProfile()->GetAggression(); m_politeTimer.Start(politeDuration); } else if (m_politeTimer.IsElapsed()) { // we have run out of patience m_isWaitingBehindFriend = false; ResetStuckMonitor(); // repath to avoid clump of friends in the way DestroyPath(); } } else if (m_isWaitingBehindFriend) { // we're done waiting for our friend to move m_isWaitingBehindFriend = false; ResetStuckMonitor(); } // Move along our path if there are no friends blocking our way, // or we have run out of patience if (!m_isWaitingBehindFriend || m_politeTimer.IsElapsed()) { // Move along path MoveTowardsPosition(&adjustedGoal); // Stuck check if (m_isStuck && !IsJumping()) { Wiggle(); } } // if our goal is high above us, we must have fallen bool didFall = false; if (m_goalPosition.z - GetFeetZ() > JumpCrouchHeight) { const float closeRange = 75.0f; Vector2D to(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (to.IsLengthLessThan(closeRange)) { // we can't reach the goal position // check if we can reach the next node, in case this was a "jump down" situation if (m_pathIndex < m_pathLength - 1) { if (m_path[m_pathIndex + 1].pos.z - GetFeetZ() > JumpCrouchHeight) { // the next node is too high, too - we really did fall of the path didFall = true; } } else { // fell trying to get to the last node in the path didFall = true; } } } // This timeout check is needed if the bot somehow slips way off // of its path and cannot progress, but also moves around // enough that it never becomes "stuck" const float giveUpDuration = 5.0f; // 4.0f if (didFall || gpGlobals->time - m_areaEnteredTimestamp > giveUpDuration) { if (didFall) { PrintIfWatched("I fell off!\n"); } // if we havent made any progress in a long time, give up if (m_pathIndex < m_pathLength - 1) { PrintIfWatched("Giving up trying to get to area #%d\n", m_path[m_pathIndex].area->GetID()); } else { PrintIfWatched("Giving up trying to get to end of path\n"); } Run(); StandUp(); DestroyPath(); return PATH_FAILURE; } return PROGRESSING; }
// 리턴값 TRUE가 무엇인가가 있다 BOOL CInstanceBase::CheckAdvancing() { #ifdef __MOVIE_MODE__ if (IsMovieMode()) return FALSE; #endif if (!__IsMainInstance() && !IsAttacking()) { if (IsPC() && IsWalking()) { CPythonCharacterManager& rkChrMgr=CPythonCharacterManager::Instance(); for(CPythonCharacterManager::CharacterIterator i = rkChrMgr.CharacterInstanceBegin(); i!=rkChrMgr.CharacterInstanceEnd();++i) { CInstanceBase* pkInstEach=*i; if (pkInstEach==this) continue; if (!pkInstEach->IsDoor()) continue; if (m_GraphicThingInstance.TestActorCollision(pkInstEach->GetGraphicThingInstanceRef())) { BlockMovement(); return true; } } } return FALSE; } if (m_GraphicThingInstance.CanSkipCollision()) { //Tracenf("%x VID %d 충돌 스킵", ELTimer_GetMSec(), GetVirtualID()); return FALSE; } BOOL bUsingSkill = m_GraphicThingInstance.IsUsingSkill(); m_dwAdvActorVID = 0; UINT uCollisionCount=0; CPythonCharacterManager& rkChrMgr=CPythonCharacterManager::Instance(); for(CPythonCharacterManager::CharacterIterator i = rkChrMgr.CharacterInstanceBegin(); i!=rkChrMgr.CharacterInstanceEnd();++i) { CInstanceBase* pkInstEach=*i; if (pkInstEach==this) continue; CActorInstance& rkActorSelf=m_GraphicThingInstance; CActorInstance& rkActorEach=pkInstEach->GetGraphicThingInstanceRef(); //NOTE : Skil을 쓰더라도 Door Type과는 Collision체크 한다. if( bUsingSkill && !rkActorEach.IsDoor() ) continue; // 앞으로 전진할수 있는가? if (rkActorSelf.TestActorCollision(rkActorEach)) { uCollisionCount++; if (uCollisionCount==2) { rkActorSelf.BlockMovement(); return TRUE; } rkActorSelf.AdjustDynamicCollisionMovement(&rkActorEach); if (rkActorSelf.TestActorCollision(rkActorEach)) { rkActorSelf.BlockMovement(); return TRUE; } else { NEW_MoveToDestPixelPositionDirection(NEW_GetDstPixelPositionRef()); } } } // 맵속성 체크 CPythonBackground& rkBG=CPythonBackground::Instance(); const D3DXVECTOR3 & rv3Position = m_GraphicThingInstance.GetPosition(); const D3DXVECTOR3 & rv3MoveDirection = m_GraphicThingInstance.GetMovementVectorRef(); // NOTE : 만약 이동 거리가 크다면 쪼개서 구간 별로 속성을 체크해 본다 // 현재 설정해 놓은 10.0f는 임의의 거리 - [levites] int iStep = int(D3DXVec3Length(&rv3MoveDirection) / 10.0f); D3DXVECTOR3 v3CheckStep = rv3MoveDirection / float(iStep); D3DXVECTOR3 v3CheckPosition = rv3Position; for (int j = 0; j < iStep; ++j) { v3CheckPosition += v3CheckStep; // Check if (rkBG.isAttrOn(v3CheckPosition.x, -v3CheckPosition.y, CTerrainImpl::ATTRIBUTE_BLOCK)) { BlockMovement(); //return TRUE; } } // Check D3DXVECTOR3 v3NextPosition = rv3Position + rv3MoveDirection; if (rkBG.isAttrOn(v3NextPosition.x, -v3NextPosition.y, CTerrainImpl::ATTRIBUTE_BLOCK)) { BlockMovement(); return TRUE; } return FALSE; }
// When bot is touched by another entity. void CCSBot::BotTouch(CBaseEntity *pOther) { // if we have touched a higher-priority player, make way // TODO: Need to account for reaction time, etc. if (pOther->IsPlayer()) { // if we are defusing a bomb, don't move if (IsDefusingBomb()) return; CBasePlayer *pPlayer = static_cast<CBasePlayer *>(pOther); // get priority of other player unsigned int otherPri = TheCSBots()->GetPlayerPriority(pPlayer); // get our priority unsigned int myPri = TheCSBots()->GetPlayerPriority(this); // if our priority is better, don't budge if (myPri < otherPri) return; // they are higher priority - make way, unless we're already making way for someone more important if (m_avoid) { unsigned int avoidPri = TheCSBots()->GetPlayerPriority(m_avoid); if (avoidPri < otherPri) { // ignore 'pOther' because we're already avoiding someone better return; } } m_avoid = static_cast<CBasePlayer *>(pOther); m_avoidTimestamp = gpGlobals->time; return; } // If we won't be able to break it, don't try if (pOther->pev->takedamage != DAMAGE_YES) return; if (IsAttacking()) return; // See if it's breakable if (FClassnameIs(pOther->pev, "func_breakable")) { Vector center = (pOther->pev->absmax + pOther->pev->absmin) / 2.0f; bool breakIt = true; if (m_pathLength) { Vector goal = m_goalPosition + Vector(0, 0, HalfHumanHeight); breakIt = IsIntersectingBox(pev->origin, goal, pOther->pev->absmin, pOther->pev->absmax); } if (breakIt) { // it's breakable - try to shoot it. SetLookAt("Breakable", ¢er, PRIORITY_HIGH, 0.2, 0, 5.0); if (IsUsingGrenade()) { EquipBestWeapon(); return; } PrimaryAttack(); } } }
// Invoked when injured by something // NOTE: We dont want to directly call Attack() here, or the bots will have super-human reaction times when injured BOOL CCSBot::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) { CBaseEntity *pAttacker = GetClassPtr<CCSEntity>((CBaseEntity *)pevInflictor); // if we were attacked by a teammate, rebuke if (pAttacker->IsPlayer()) { CBasePlayer *pPlayer = static_cast<CBasePlayer *>(pAttacker); if (BotRelationship(pPlayer) == BOT_TEAMMATE && !pPlayer->IsBot()) { GetChatter()->FriendlyFire(); } if (IsEnemy(pPlayer)) { // Track previous attacker so we don't try to panic multiple times for a shotgun blast CBasePlayer *lastAttacker = m_attacker; float lastAttackedTimestamp = m_attackedTimestamp; // keep track of our last attacker m_attacker = pPlayer; m_attackedTimestamp = gpGlobals->time; // no longer safe AdjustSafeTime(); if (!IsSurprised() && (m_attacker != lastAttacker || m_attackedTimestamp != lastAttackedTimestamp)) { // being hurt by an enemy we can't see causes panic if (!IsVisible(pPlayer, CHECK_FOV)) { bool bPanic = false; // if not attacking anything, look around to try to find attacker if (!IsAttacking()) { bPanic = true; } else { // we are attacking if (!IsEnemyVisible()) { // can't see our current enemy, panic to acquire new attacker bPanic = true; } } if (!bPanic) { float invSkill = 1.0f - GetProfile()->GetSkill(); float panicChance = invSkill * invSkill * 50.0f; if (panicChance > RANDOM_FLOAT(0, 100)) { bPanic = true; } } if (bPanic) { // can't see our current enemy, panic to acquire new attacker Panic(m_attacker); } } } } } // extend return CBasePlayer::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); }