EventDesiredResult<CTFBot> CTFBotMedicRetreat::OnMoveToFailure(CTFBot *actor, const Path *path, MoveToFailureType fail) { CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(actor, actor->m_HomeArea.GetCenter(), cost_func, 0.0f, true); return EventDesiredResult<CTFBot>::Continue(); }
EventDesiredResult<CTFBot> CTFBotMedicRetreat::OnStuck(CTFBot *actor) { CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(actor, actor->m_HomeArea.GetCenter(), cost_func, 0.0f, true); return EventDesiredResult<CTFBot>::Continue(); }
ActionResult<CTFBot> CTFBotMarkGiants::Update(CTFBot *actor, float dt) { if (this->m_hTarget == nullptr || !this->m_hTarget->IsAlive()) { return ActionResult<CTFBot>::Done("Mark target is no longer valid."); } if (!IsPlayerMarkable(actor, this->m_hTarget)) { return ActionResult<CTFBot>::Done("Mark target is no longer markable."); } auto nextbot = rtti_cast<INextBot *>(actor); if (this->m_ctRecomputePath.IsElapsed()) { this->m_ctRecomputePath.Start(RandomFloat(0.4f, 0.6f)); CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(nextbot, this->m_hTarget->GetAbsOrigin(), cost_func, 0.0f, true); // TODO: handle Path::Compute failure } float dist_to_target = (actor->GetAbsOrigin() - this->m_hTarget->GetAbsOrigin()).Length(); if (dist_to_target < 512.0f) { actor->GetBodyInterface()->AimHeadTowards(this->m_hTarget->WorldSpaceCenter(), IBody::LookAtPriorityType::CRITICAL, 0.1f, nullptr, "Look at our mark-for-death target"); } if (dist_to_target > actor->GetDesiredAttackRange()) { this->m_PathFollower.Update(nextbot); } return ActionResult<CTFBot>::Continue(); }
ActionResult<CTFBot> CTFBotMedicRetreat::OnResume(CTFBot *actor, Action<CTFBot> *action) { CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(actor, actor->m_HomeArea.GetCenter(), cost_func, 0.0f, true); return ActionResult<CTFBot>::Continue(); }
int grow_vertex(int *c, int m, int *movement, int min_bound, int max_bound, double (*cost_func)(int *)){ int changeflag = 1; int i,count=0; double cost = cost_func(c); while(changeflag){ changeflag=0; for(i=0;i<m;i++){ if(movement[i]){ c[i] += movement[i]; double test_cost = lift_cost(c); if(test_cost>cost){ changeflag=1; cost=test_cost; count++; }else{ c[i] -= movement[i]; } } } } return count; }
ActionResult<CTFBot> CTFBotEscort::Update(CTFBot *actor, float dt) { const CKnownEntity *threat = actor->GetVisionInterface()->GetPrimaryKnownThreat(false); if (threat != nullptr && threat->IsVisibleInFOVNow()) { return ActionResult<CTFBot>::SuspendFor(new CTFBotAttack(), "Attacking nearby threat"); } if (this->m_hWho != nullptr) { if (actor->IsRangeGreaterThan(this->m_hWho, tf_bot_escort_range.GetFloat())) { if (this->m_ctRecomputePath.IsElapsed()) { CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(actor, this->m_hWho, cost_func, 0.0f, true); this->m_ctRecomputePath.Start(RandomFloat(2.0f, 3.0f)); } this->m_PathFollower.Update(actor); } else if (CTFBotPrepareStickybombTrap::IsPossible(actor)) { return ActionResult<CTFBot>::SuspendFor(new CTFBotPrepareStickybombTrap(), "Laying sticky bombs!"); } } if (actor->m_hTargetSentry != nullptr && CTFBotDestroyEnemySentry::IsPossible(actor)) { return ActionResult<CTFBot>::SuspendFor(new CTFBotDestroyEnemySentry(), "Going after an enemy sentry to destroy it"); } return ActionResult<CTFBot>::Continue(); }
virtual ActionResult<CTFBot> Update(CTFBot *actor, float dt) override { TRACE("[this: %08x] [actor: #%d]", (uintptr_t)this, ENTINDEX(actor)); if (this->m_hHint == nullptr) { return ActionResult<CTFBot>::Done("No hint entity"); } INextBot *nextbot = rtti_cast<INextBot *>(actor); if (nextbot->IsRangeGreaterThan(this->m_hHint->GetAbsOrigin(), 25.0f)) { TRACE_MSG("range_to_hint > 25: pathing\n"); if (this->m_ctRecomputePath.IsElapsed()) { TRACE_MSG("recomputing path\n"); this->m_ctRecomputePath.Start(RandomFloat(1.0f, 2.0f)); CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(nextbot, this->m_hHint->GetAbsOrigin(), cost_func, 0.0f, true); } this->m_PathFollower.Update(nextbot); if (!this->m_PathFollower.IsValid()) { return ActionResult<CTFBot>::Done("Path failed"); } return ActionResult<CTFBot>::Continue(); } TRACE_MSG("at hint: creating dispenser entity\n"); CBaseEntity *ent = CreateEntityByName("obj_dispenser"); if (ent == nullptr) { return ActionResult<CTFBot>::Done("Couldn't create entity"); } auto dispenser = rtti_cast<CObjectDispenser *>(ent); dispenser->SetName(this->m_hHint->GetEntityName()); dispenser->m_nDefaultUpgradeLevel = 2; dispenser->SetAbsOrigin(this->m_hHint->GetAbsOrigin()); dispenser->SetAbsAngles(this->m_hHint->GetAbsAngles()); dispenser->Spawn(); dispenser->StartPlacement(actor); suppress_speak = true; dispenser->StartBuilding(actor); suppress_speak = false; this->m_hHint->SetOwnerEntity(dispenser); actor->SpeakConceptIfAllowed(MP_CONCEPT_BUILDING_OBJECT, "objtype:dispenser"); return ActionResult<CTFBot>::Done("Built a dispenser"); }
ActionResult<CTFBot> CTFBotMedicRetreat::OnStart(CTFBot *actor, Action<CTFBot> *action) { if (actor->m_HomeArea == nullptr) { return ActionResult<CTFBot>::Done("No home area!"); } CTFBotPathCost cost_func(actor, FASTEST_ROUTE); this->m_PathFollower.Compute(actor, actor->m_HomeArea.GetCenter(), cost_func, 0.0f, true); return ActionResult<CTFBot>::Continue(); }
double gradient_walk(int *c, int m, int min_bound, int max_bound, double (*cost_func)(int *)){ int last_change = m-1; int cur_dim = 0; double cost = cost_func(c); while(1){ double val = c[cur_dim]; c[cur_dim] = val+1; double up_cost = lift_cost(c); c[cur_dim] = val-1; double down_cost = lift_cost(c); if(up_cost<cost && val+1 <= max_bound){ c[cur_dim] = val+1; last_change = cur_dim; cost = up_cost; }else if (down_cost<cost && val-1 >= min_bound){ c[cur_dim] = val-1; last_change = cur_dim; cost = down_cost; }else{ c[cur_dim] = val; if(cur_dim == last_change)break; } cur_dim++; if(cur_dim >= m) cur_dim = 0; } return cost; }
ActionResult<CTFBot> CTFBotMeleeAttack::Update(CTFBot *actor, float dt) { const CKnownEntity *threat = actor->GetPrimaryKnownThreat(false); if (threat == nullptr) { return ActionResult<CTFBot>::Done("No threat"); } if ((threat->GetLastKnownPosition() - actor->GetAbsOrigin()).LengthSqr() > Square(this->m_flAbandonRange)) { return ActionResult<CTFBot>::Done("Threat is too far away for a melee attack"); } CBaseCombatWeapon *melee = actor->Weapon_GetSlot(2); if (melee != nullptr) { actor->Weapon_Switch(melee); } actor->PressFireButton(); CTFBotCostFunc cost_func(actor, FASTEST_ROUTE); this->m_ChasePath.Update(actor, threat->GetEntity(), cost_func, nullptr); return ActionResult<CTFBot>::Continue(); }
ActionResult<CTFBot> CTFBotAttack::Update(CTFBot *actor, float dt) { bool is_melee = false; CTFWeaponBase *weapon = actor->GetActiveTFWeapon(); if (weapon != nullptr) { if (weapon->IsWeapon(TF_WEAPON_FLAMETHROWER) || weapon->IsMeleeWeapon()) { is_melee = true; } } const CKnownEntity *threat = actor->GetVisionInterface()->GetPrimaryKnownThreat(); if (threat == nullptr || threat->IsObsolete() || actor->GetIntentionInterface()->ShouldAttack(actor, threat) == QueryResponse::NO) { return ActionResult<CTFBot>::Done("No threat"); } actor->EquipBestWeaponForThreat(threat); if (is_melee && threat->IsVisibleRecently() && actor->IsRangeLessThan(threat->GetLastKnownPosition(), 1.1f * actor->GetDesiredAttackRange())) { if (actor->TransientlyConsistentRandomValue(3.0f, 0) < 0.5f) { actor->PressLeftButton(); } else { actor->PressRightButton(); } } if (threat->IsVisibleRecently() && !actor->IsRangeGreaterThan(threat->GetEntity()->GetAbsOrigin(), actor->GetDesiredAttackRange()) && actor->IsLineOfFireClear(threat->GetEntity()->EyePosition())) { return ActionResult<CTFBot>::Continue(); } if (threat->IsVisibleRecently()) { CTFBotPathCost cost_func(actor, (is_melee && TFGameRules()->IsMannVsMachineMode() ? SAFEST_ROUTE : DEFAULT_ROUTE)); this->m_ChasePath.Update(actor, threat->GetEntity(), cost_func, nullptr); return ActionResult<CTFBot>::Continue(); } this->m_ChasePath.Invalidate(); if (actor->IsRangeLessThan(threat->GetLastKnownPosition(), 20.0f)) { actor->GetVisionInterface()->ForgetEntity(threat->GetEntity()); return ActionResult<CTFBot>::Done("I lost my target!"); } if (actor->IsRangeLessThan(threat->GetLastKnownPosition(), actor->GetMaxAttackRange())) { // HumanEyeHeight = 62.0f, from nav.h; value is from CS:S and is actually a bit wrong for TF2 Vector eye = threat->GetLastKnownPosition() + Vector(0.0f, 0.0f, HumanEyeHeight); actor->GetBodyInterface()->AimHeadTowards(eye, IBody::LookAtPriorityType::IMPORTANT, 0.2f, nullptr, "Looking towards where we lost sight of our victim"); } this->m_PathFollower.Update(actor); if (this->m_ctRecomputePath.IsElapsed()) { this->m_ctRecomputePath.Start(RandomFloat(3.0f, 5.0f)); CTFBotPathCost cost_func(actor, (is_melee && TFGameRules()->IsMannVsMachineMode() ? SAFEST_ROUTE : DEFAULT_ROUTE)); this->m_PathFollower.Compute(actor, threat->GetLastKnownPosition(), cost_func, 0.0f, true); return ActionResult<CTFBot>::Continue(); } }
ActionResult<CTFBot> CTFBotMvMEngineerBuildSentryGun::Update(CTFBot *actor, float dt) { if (this->m_hintEntity == nullptr) { return ActionResult<CTFBot>::Done("No hint entity"); } float range_to_hint = actor->GetRangeTo(this->m_hintEntity->GetAbsOrigin()); if (range_to_hint < 200.0f) { actor->PressCrouchButton(); actor->GetBodyInterface()->AimHeadTowards(this->m_hintEntity->GetAbsOrigin(), IBody::LookAtPriorityType::OVERRIDE_ALL, 0.1f, nullptr, "Placing sentry"); } if (range_to_hint > 25.0f) { if (this->m_ctRecomputePath.IsElapsed()) { this->m_ctRecomputePath.Start(RandomFloat(1.0f, 2.0f)); CTFBotPathCost cost_func(actor, SAFEST_ROUTE); this->m_PathFollower.Compute<CTFBotPathCost>(actor, this->m_hintEntity->GetAbsOrigin(), cost_func, 0.0f, true); } this->m_PathFollower.Update(actor); if (!this->m_PathFollower.IsValid()) { /* BUG: one path failure ends the entire behavior... * could this be why engiebots sometimes zone out? */ return ActionResult<CTFBot>::Done("Path failed"); } return ActionResult<CTFBot>::Continue(); } if (!this->m_ctPushAway.HasStarted()) { this->m_ctPushAway.Start(0.1f); if (this->m_hintEntity != nullptr) { TFGameRules()->PushAllPlayersAway(this->m_hintEntity->GetAbsOrigin(), 400.0f, 500.0f, TF_TEAM_RED, nullptr); } return ActionResult<CTFBot>::Continue(); } if (!this->m_ctPushAway.IsElapsed()) { return ActionResult<CTFBot>::Continue(); } actor->DetonateObjectOfType(OBJ_SENTRYGUN, 0, true); CBaseEntity *ent = CreateEntityByName("obj_sentrygun"); if (ent == nullptr) { /* BUG: no we didn't */ return ActionResult<CTFBot>::Done("Built a sentry"); } this->m_hSentry = static_cast<CObjectSentrygun *>(ent); this->m_hSentry->SetName(this->m_hintEntity->GetEntityName()); // TODO: // ++this->m_hintEntity->dword_0x370 this->m_hSentry->m_nDefaultUpgradeLevel = 2; this->m_hSentry->SetAbsOrigin(this->m_hintEntity->GetAbsOrigin()); this->m_hSentry->SetAbsAngles(this->m_hintEntity->GetAbsAngles()); this->m_hSentry->Spawn(); this->m_hSentry->StartPlacement(actor); this->m_hSentry->StartBuilding(actor); this->m_hintEntity->SetOwnerEntity(this->m_hSentry); this->m_hSentry = nullptr; return ActionResult<CTFBot>::Done("Built a sentry"); }
ActionResult<CTFBot> CTFBotCapturePoint::Update(CTFBot *actor, float dt) { if (TFGameRules()->InSetup()) { this->m_PathFollower.Invalidate(); this->m_ctRecomputePath.Start(RandomFloat(1.0f, 2.0f)); return ActionResult<CTFBot>::Continue(); } CTeamControlPoint *point = actor->GetMyControlPoint(); if (point == nullptr) { return ActionResult<CTFBot>::SuspendFor(new CTFBotSeekAndDestroy(10.0f), "Seek and destroy until a point becomes available"); } if (point->GetTeamNumber() == actor->GetTeamNumber()) { return ActionResult<CTFBot>::ChangeTo(new CTFBotDefendPoint(), "We need to defend our point(s)"); } const CKnownEntity *threat = actor->GetVisionInterface()->GetPrimaryKnownThreat(false); if (threat != nullptr && threat->IsVisibleRecently()) { actor->EquipBestWeaponForThreat(threat); } if ((!actor->IsPointBeingCaptured(point) || actor->GetTimeSinceWeaponFired() < 2.0f) && !actor->IsCapturingPoint() && !TFGameRules()->InOvertime() && actor->GetTimeLeftToCapture() >= tf_bot_offense_must_push_time.GetFloat() && !TFGameRules()->IsInTraining() && !actor->IsNearPoint(point) && threat != nullptr && threat->IsVisibleRecently()) { float duration = RandomFloat(tf_bot_capture_seek_and_destroy_min_duration.GetFloat(), tf_bot_capture_seek_and_destroy_max_duration.GetFloat()); return ActionResult<CTFBot>::SuspendFor(new CTFBotSeekAndDestroy(duration), "Too early to capture - hunting"); } if (actor->IsCapturingPoint()) { if (point->GetPointIndex() > 7) { return ActionResult<CTFBot>::Continue(); } // TODO // ... // Path::Compute } if (this->m_ctRecomputePath.IsElapsed()) { VPROF_BUDGET("CTFBotCapturePoint::Update( repath )", "NextBot"); CTFBotPathCost cost_func(actor, SAFEST_ROUTE); this->m_PathFollower.Compute(actor, point->GetAbsOrigin(), cost_func, 0.0f, true); this->m_ctRecomputePath.Start(RandomFloat(2.0f, 3.0f)); } if (TFGameRules()->IsInTraining() && !actor->IsAnyPointBeingCaptured() && this->m_PathFollower.GetLength() < 1000.0f) { actor->SpeakConceptIfAllowed(MP_CONCEPT_PLAYER_GO); } else { this->m_PathFollower.Update(actor); } return ActionResult<CTFBot>::Continue(); }
virtual ActionResult<CTFBot> Update(CTFBot *actor, float dt) override { TRACE("[this: %08x] [actor: #%d]", (uintptr_t)this, ENTINDEX(actor)); if (this->m_hHint == nullptr) { return ActionResult<CTFBot>::Done("No hint entity"); } INextBot *nextbot = rtti_cast<INextBot *>(actor); float range_to_hint = nextbot->GetRangeTo(this->m_hHint->GetAbsOrigin()); if (range_to_hint < 200.0f) { TRACE_MSG("range_to_hint < 200: crouching/aiming\n"); actor->PressCrouchButton(); actor->GetBodyInterface()->AimHeadTowards(this->m_hHint->GetAbsOrigin(), IBody::LookAtPriorityType::OVERRIDE_ALL, 0.1f, nullptr, "Placing dispenser"); if (!this->m_bNearHint) { this->m_bNearHint = true; //TFGameRules()->VoiceCommand(actor, 1, 4); //actor->SpeakConceptIfAllowed(MP_CONCEPT_PLAYER_DISPENSERHERE); } } else { this->m_bNearHint = false; } if (range_to_hint > 25.0f) { TRACE_MSG("range_to_hint > 25: pathing\n"); if (this->m_ctRecomputePath.IsElapsed()) { TRACE_MSG("recomputing path\n"); this->m_ctRecomputePath.Start(RandomFloat(1.0f, 2.0f)); CTFBotPathCost cost_func(actor, SAFEST_ROUTE); this->m_PathFollower.Compute(nextbot, this->m_hHint->GetAbsOrigin(), cost_func, 0.0f, true); } this->m_PathFollower.Update(nextbot); if (!this->m_PathFollower.IsValid()) { return ActionResult<CTFBot>::Done("Path failed"); } return ActionResult<CTFBot>::Continue(); } TRACE_MSG("at hint: creating dispenser entity\n"); CBaseEntity *ent = CreateEntityByName("obj_dispenser"); if (ent == nullptr) { return ActionResult<CTFBot>::Done("Couldn't create entity"); } // TODO: increment hint dword 0x370 (not important for mvm) auto dispenser = rtti_cast<CObjectDispenser *>(ent); dispenser->SetName(this->m_hHint->GetEntityName()); dispenser->m_nDefaultUpgradeLevel = 2; dispenser->SetAbsOrigin(this->m_hHint->GetAbsOrigin()); dispenser->SetAbsAngles(this->m_hHint->GetAbsAngles()); dispenser->Spawn(); dispenser->StartPlacement(actor); suppress_speak = true; dispenser->StartBuilding(actor); suppress_speak = false; this->m_hHint->SetOwnerEntity(dispenser); actor->SpeakConceptIfAllowed(MP_CONCEPT_BUILDING_OBJECT, "objtype:dispenser"); return ActionResult<CTFBot>::Done("Built a dispenser"); }