aim: improved ladder handling view direction
aim: improved enemy prediction once again nav: bots with hostages will try to take all hostages that are near with him instead of going directly to rescue zone manager: fixed engine errors when removing bots with kickall with instant parameter graph: strip http:// prefix from graph upload url, it should be always http for now bot: improve handling of smoke grenades on ground (restored code from old yapb2 branch)
This commit is contained in:
parent
5a2d0748c0
commit
a49a4000c9
16 changed files with 269 additions and 133 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 67733ef6ffd51c538692f311e6cfb26affb3e50e
|
||||
Subproject commit c4815b7445ae0fc509cba511deee0f2c67834704
|
||||
|
|
@ -395,6 +395,15 @@ CR_DECLARE_SCOPED_ENUM (Visibility,
|
|||
None = 0
|
||||
)
|
||||
|
||||
// goal tactic
|
||||
CR_DECLARE_SCOPED_ENUM (GoalTactic,
|
||||
Defensive = 0,
|
||||
Camp,
|
||||
Offensive,
|
||||
Goal,
|
||||
RescueHostage
|
||||
)
|
||||
|
||||
// frustum sides
|
||||
CR_DECLARE_SCOPED_ENUM (FrustumSide,
|
||||
Top = 0,
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ private:
|
|||
BinaryHeap <RouteTwin <float>> m_routeQue {};
|
||||
Array <Route> m_routes {};
|
||||
|
||||
HeuristicFn m_hcalc;
|
||||
HeuristicFn m_gcalc;
|
||||
HeuristicFn m_hcalc {};
|
||||
HeuristicFn m_gcalc {};
|
||||
|
||||
int m_length {};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ public:
|
|||
// check if entity is a vip
|
||||
bool isPlayerVIP (edict_t *ent);
|
||||
|
||||
// check if entity is a hostage entity
|
||||
bool isHostageEntity (edict_t *ent);
|
||||
|
||||
// nearest player search helper
|
||||
bool findNearestPlayer (void **holder, edict_t *to, float searchDistance = 4096.0, bool sameTeam = false, bool needBot = false, bool needAlive = false, bool needDrawn = false, bool needBotWithC4 = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -362,8 +362,9 @@ private:
|
|||
Vector m_throw {}; // origin of node to throw grenades
|
||||
Vector m_enemyOrigin {}; // target origin chosen for shooting
|
||||
Vector m_grenade {}; // calculated vector for grenades
|
||||
Vector m_entity {}; // origin of entities like buttons etc.
|
||||
Vector m_lookAtSafe {}; // aiming vector when camping.
|
||||
Vector m_entity {}; // origin of entities like buttons etc
|
||||
Vector m_lookAtSafe {}; // aiming vector when camping
|
||||
Vector m_lookAtPredict {}; // aiming vector when predicting
|
||||
Vector m_desiredVelocity {}; // desired velocity for jump nodes
|
||||
Vector m_breakableOrigin {}; // origin of breakable
|
||||
|
||||
|
|
@ -403,7 +404,7 @@ private:
|
|||
bool canDuckUnder (const Vector &normal);
|
||||
bool canJumpUp (const Vector &normal);
|
||||
bool doneCanJumpUp (const Vector &normal, const Vector &right);
|
||||
bool cantMoveForward (const Vector &normal, TraceResult *tr);
|
||||
bool isBlockedForward (const Vector &normal, TraceResult *tr);
|
||||
bool canStrafeLeft (TraceResult *tr);
|
||||
bool canStrafeRight (TraceResult *tr);
|
||||
bool isBlockedLeft ();
|
||||
|
|
@ -730,7 +731,7 @@ public:
|
|||
void pushChatterMessage (int message);
|
||||
void tryHeadTowardRadioMessage ();
|
||||
void kill ();
|
||||
void kick ();
|
||||
void kick (bool silent = false);
|
||||
void resetDoubleJump ();
|
||||
void startDoubleJump (edict_t *ent);
|
||||
void sendBotToOrigin (const Vector &origin);
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ void Bot::avoidGrenades () {
|
|||
float distanceMoved = pev->origin.distance (pent->v.origin + pent->v.velocity * m_frameInterval);
|
||||
|
||||
if (distanceMoved < distance && distance < cr::sqrf (500.0f)) {
|
||||
const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d ();
|
||||
const auto &rightSide = pev->v_angle.right ().normalize2d ();
|
||||
const auto &dirToPoint = (pev->origin - pent->v.origin).normalize2d_apx ();
|
||||
const auto &rightSide = pev->v_angle.right ().normalize2d_apx ();
|
||||
|
||||
if ((dirToPoint | rightSide) > 0.0f) {
|
||||
m_needAvoidGrenade = -1;
|
||||
|
|
@ -129,8 +129,14 @@ void Bot::avoidGrenades () {
|
|||
}
|
||||
}
|
||||
else if ((pent->v.flags & FL_ONGROUND) && model == "smokegrenade.mdl") {
|
||||
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov - 7.0f) {
|
||||
float distance = pent->v.origin.distance (pev->origin);
|
||||
if (isInFOV (pent->v.origin - getEyesPos ()) < pev->fov / 3.0f) {
|
||||
const auto &entOrigin = game.getEntityOrigin (pent);
|
||||
const auto &betweenUs = (entOrigin - pev->origin).normalize_apx ();
|
||||
const auto &betweenNade = (entOrigin - pev->origin).normalize_apx ();
|
||||
const auto &betweenResult = ((Vector (betweenNade.y, betweenNade.x, 0.0f) * 150.0f + entOrigin) - pev->origin).normalize_apx ();
|
||||
|
||||
if ((betweenNade | betweenUs) > (betweenNade | betweenResult) && util.isVisible (pent->v.origin, ent ())) {
|
||||
const float distance = entOrigin.distance (pev->origin);
|
||||
|
||||
// shrink bot's viewing distance to smoke grenade's distance
|
||||
if (m_viewDistance > distance) {
|
||||
|
|
@ -144,6 +150,7 @@ void Bot::avoidGrenades () {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::checkBreakable (edict_t *touch) {
|
||||
if (!game.isShootableBreakable (touch)) {
|
||||
|
|
@ -383,7 +390,7 @@ void Bot::updatePickups () {
|
|||
const bool isHostageRescueMap = game.mapIs (MapFlags::HostageRescue);
|
||||
const bool isCSDM = game.is (GameFlags::CSDM);
|
||||
|
||||
if (isHostageRescueMap && (classname.startsWith ("hostage_entity") || classname.startsWith ("monster_scientist"))) {
|
||||
if (isHostageRescueMap && util.isHostageEntity (ent)) {
|
||||
allowPickup = true;
|
||||
pickupType = Pickup::Hostage;
|
||||
}
|
||||
|
|
@ -794,12 +801,16 @@ void Bot::showChatterIcon (bool show, bool disconnect) {
|
|||
int ownIndex = index ();
|
||||
|
||||
// do not respect timers while disconnecting bot
|
||||
|
||||
for (auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || (client.ent->v.flags & FL_FAKECLIENT) || client.team != m_team) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// dormants not receiving messages
|
||||
if (client.ent->v.flags & FL_DORMANT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not respect timers while disconnecting bot
|
||||
if (!show && (client.iconFlags[ownIndex] & ClientFlags::Icon) && (disconnect || client.iconTimestamp[ownIndex] < game.time ())) {
|
||||
sendBotVoice (false, client.ent, entindex ());
|
||||
|
|
@ -2168,7 +2179,7 @@ bool Bot::reactOnEnemy () {
|
|||
|
||||
bool Bot::lastEnemyShootable () {
|
||||
// don't allow shooting through walls
|
||||
if (!(m_aimFlags & AimFlags::LastEnemy) || m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) {
|
||||
if (!(m_aimFlags & (AimFlags::LastEnemy | AimFlags::PredictPath)) || m_lastEnemyOrigin.empty () || game.isNullEntity (m_lastEnemy)) {
|
||||
return false;
|
||||
}
|
||||
return util.getShootingCone (ent (), m_lastEnemyOrigin) >= 0.90f && isPenetrableObstacle (m_lastEnemyOrigin);
|
||||
|
|
|
|||
|
|
@ -791,7 +791,7 @@ int BotControl::cmdNodeUpload () {
|
|||
msg ("you may notice the game freezes a bit during upload and issue request creation. Please, be patient.");
|
||||
msg ("\n");
|
||||
|
||||
String uploadUrl = cv_graph_url_upload.str ();
|
||||
String uploadUrl = strings.format ("https://%s", cv_graph_url_upload.str ());
|
||||
|
||||
// try to upload the file
|
||||
if (http.uploadFile (uploadUrl, bstor.buildPath (BotFile::Graph))) {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ void Game::levelInitialize (edict_t *entities, int max) {
|
|||
else if (classname == "func_vip_safetyzone" || classname == "info_vip_safetyzone") {
|
||||
m_mapFlags |= MapFlags::Assassination; // assassination map
|
||||
}
|
||||
else if (classname == "hostage_entity" || classname == "monster_scientist") {
|
||||
else if (util.isHostageEntity (ent)) {
|
||||
m_mapFlags |= MapFlags::HostageRescue; // rescue map
|
||||
}
|
||||
else if (classname == "func_bomb_target" || classname == "info_bomb_target") {
|
||||
|
|
@ -916,7 +916,7 @@ bool Game::postload () {
|
|||
if (is (GameFlags::Metamod)) {
|
||||
return true; // we should stop the attempt for loading the real gamedll, since metamod handle this for us
|
||||
}
|
||||
auto gamedll = strings.format ("%s/%s", plat.env ("XASH3D_GAMELIBDIR"), plat.hfp ? "libserver_hardfp.so" : "libserver.so");
|
||||
auto gamedll = strings.format ("%s/%s", plat.env ("XASH3D_GAMELIBDIR"), "libserver.so");
|
||||
|
||||
if (!m_gameLib.load (gamedll)) {
|
||||
logger.fatal ("Unable to load gamedll \"%s\". Exiting... (gamedir: %s)", gamedll, getRunningModName ());
|
||||
|
|
@ -1137,7 +1137,7 @@ void Game::printBotVersion () {
|
|||
simdLevels.push ("4.2");
|
||||
}
|
||||
if (cpuflags.neon) {
|
||||
simdLevels.push ("NEON");
|
||||
simdLevels.push ("Neon");
|
||||
}
|
||||
botRuntimeFlags.push (strings.format ("SIMD: %s", String::join (simdLevels, " & ")));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
ConVar cv_graph_fixcamp ("yb_graph_fixcamp", "0", "Specifies whether bot should not 'fix' camp directions of camp waypoints when loading old PWF format.");
|
||||
ConVar cv_graph_url ("yb_graph_url", product.download.chars (), "Specifies the URL from which bots will be able to download graph in case of missing local one. Set to empty, if no downloads needed.", false, 0.0f, 0.0f);
|
||||
ConVar cv_graph_url_upload ("yb_graph_url_upload", "http://yapb.jeefo.net/upload", "Specifies the URL to which bots will try to upload the graph file to database.", false, 0.0f, 0.0f);
|
||||
ConVar cv_graph_url_upload ("yb_graph_url_upload", "yapb.jeefo.net/upload", "Specifies the URL to which bots will try to upload the graph file to database.", false, 0.0f, 0.0f);
|
||||
ConVar cv_graph_auto_save_count ("yb_graph_auto_save_count", "15", "Every N graph nodes placed on map, the graph will be saved automatically (without checks).", true, 0.0f, kMaxNodes);
|
||||
ConVar cv_graph_draw_distance ("yb_graph_draw_distance", "400", "Maximum distance to draw graph nodes from editor viewport.", true, 64.0f, 3072.0f);
|
||||
|
||||
|
|
|
|||
|
|
@ -609,7 +609,7 @@ void BotManager::kickEveryone (bool instant, bool zeroQuota) {
|
|||
|
||||
if (instant) {
|
||||
for (const auto &bot : m_bots) {
|
||||
bot->kick ();
|
||||
bot->kick (true);
|
||||
}
|
||||
}
|
||||
m_addRequests.clear ();
|
||||
|
|
@ -618,12 +618,26 @@ void BotManager::kickEveryone (bool instant, bool zeroQuota) {
|
|||
void BotManager::kickFromTeam (Team team, bool removeAll) {
|
||||
// this function remove random bot from specified team (if removeAll value = 1 then removes all players from team)
|
||||
|
||||
if (removeAll) {
|
||||
const auto &counts = countTeamPlayers ();
|
||||
|
||||
m_quotaMaintainTime = game.time () + 3.0f;
|
||||
m_addRequests.clear ();
|
||||
|
||||
if (team == Team::Terrorist) {
|
||||
decrementQuota (counts.first);
|
||||
}
|
||||
else {
|
||||
decrementQuota (counts.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &bot : m_bots) {
|
||||
if (team == bot->m_team) {
|
||||
decrementQuota ();
|
||||
bot->kick ();
|
||||
bot->kick (removeAll);
|
||||
|
||||
if (!removeAll) {
|
||||
decrementQuota ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1524,6 +1538,11 @@ void Bot::resetPathSearchType () {
|
|||
m_pathType = morale ? FindPath::Optimal : FindPath::Safe;
|
||||
break;
|
||||
}
|
||||
|
||||
// if debug goal - set the fastest
|
||||
if (cv_debug_goal.int_ () != kInvalidNodeIndex) {
|
||||
m_pathType = FindPath::Fast;
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::kill () {
|
||||
|
|
@ -1533,7 +1552,7 @@ void Bot::kill () {
|
|||
bots.touchKillerEntity (this);
|
||||
}
|
||||
|
||||
void Bot::kick () {
|
||||
void Bot::kick (bool silent) {
|
||||
// this function kick off one bot from the server.
|
||||
auto username = pev->netname.chars ();
|
||||
|
||||
|
|
@ -1543,8 +1562,11 @@ void Bot::kick () {
|
|||
markStale ();
|
||||
|
||||
game.serverCommand ("kick \"%s\"", username);
|
||||
|
||||
if (!silent) {
|
||||
ctrl.msg ("Bot '%s' kicked.", username);
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::markStale () {
|
||||
// switch chatter icon off
|
||||
|
|
@ -1559,7 +1581,7 @@ void Bot::markStale () {
|
|||
// clear fakeclient bit
|
||||
pev->flags &= ~FL_FAKECLIENT;
|
||||
|
||||
// make as not receiveing any messages
|
||||
// make as not receiving any messages
|
||||
pev->flags |= FL_DORMANT;
|
||||
}
|
||||
|
||||
|
|
@ -1787,7 +1809,7 @@ void BotManager::updateInterestingEntities () {
|
|||
}
|
||||
|
||||
// pickup some hostage if on cs_ maps
|
||||
if (game.mapIs (MapFlags::HostageRescue) && classname.startsWith ("hostage")) {
|
||||
if (game.mapIs (MapFlags::HostageRescue) && util.isHostageEntity (e)) {
|
||||
m_interestingEntities.push (e);
|
||||
}
|
||||
|
||||
|
|
|
|||
186
src/navigate.cpp
186
src/navigate.cpp
|
|
@ -27,18 +27,11 @@ int Bot::findBestGoal () {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
int tactic = 0;
|
||||
|
||||
// path finding behavior depending on map type
|
||||
float offensive = 0.0f;
|
||||
float defensive = 0.0f;
|
||||
|
||||
float goalDesire = 0.0f;
|
||||
float forwardDesire = 0.0f;
|
||||
float campDesire = 0.0f;
|
||||
float backoffDesire = 0.0f;
|
||||
float tacticChoice = 0.0f;
|
||||
|
||||
IntArray *offensiveNodes = nullptr;
|
||||
IntArray *defensiveNodes = nullptr;
|
||||
|
||||
|
|
@ -57,12 +50,51 @@ int Bot::findBestGoal () {
|
|||
|
||||
// terrorist carrying the C4?
|
||||
if (m_hasC4 || m_isVIP) {
|
||||
tactic = 3;
|
||||
return findGoalPost (tactic, defensiveNodes, offensiveNodes);
|
||||
return findGoalPost (GoalTactic::Goal, defensiveNodes, offensiveNodes);
|
||||
}
|
||||
else if (m_team == Team::CT && m_hasHostage) {
|
||||
tactic = 4;
|
||||
return findGoalPost (tactic, defensiveNodes, offensiveNodes);
|
||||
bool hasMoreHostagesAround = false;
|
||||
|
||||
// try to search nearby-unused hostage, and if so, go to next goal
|
||||
if (bots.hasInterestingEntities ()) {
|
||||
const auto &interesting = bots.getInterestingEntities ();
|
||||
|
||||
// search world for hostages
|
||||
for (const auto &ent : interesting) {
|
||||
if (!util.isHostageEntity (ent)) {
|
||||
continue;
|
||||
}
|
||||
bool hostageInUse = false;
|
||||
|
||||
// do not stole from bots (ignore humans, fuck them)
|
||||
for (const auto &other : bots) {
|
||||
if (!other->m_notKilled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &hostage : other->m_hostages) {
|
||||
if (hostage == ent) {
|
||||
hostageInUse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in-use, skip
|
||||
if (hostageInUse) {
|
||||
continue;
|
||||
}
|
||||
const auto &origin = game.getEntityOrigin (ent);
|
||||
|
||||
// too far, go to rescue point
|
||||
if (origin.distanceSq2d (pev->origin) > 1024.0f) {
|
||||
continue;
|
||||
}
|
||||
hasMoreHostagesAround = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return findGoalPost (hasMoreHostagesAround ? GoalTactic::Goal : GoalTactic::RescueHostage, defensiveNodes, offensiveNodes);
|
||||
}
|
||||
auto difficulty = static_cast <float> (m_difficulty);
|
||||
|
||||
|
|
@ -110,39 +142,39 @@ int Bot::findBestGoal () {
|
|||
}
|
||||
else if (game.mapIs (MapFlags::Escape)) {
|
||||
if (m_team == Team::Terrorist) {
|
||||
offensive += 25.0f;
|
||||
defensive -= 25.0f;
|
||||
offensive += 25.0f + difficulty * 4.0f;
|
||||
defensive -= 25.0f - difficulty * 0.5f;
|
||||
}
|
||||
else if (m_team == Team::CT) {
|
||||
offensive -= 25.0f;
|
||||
defensive += 25.0f;
|
||||
offensive -= 25.0f - difficulty * 4.5f;
|
||||
defensive += 25.0f + difficulty * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
goalDesire = rg.get (0.0f, 100.0f) + offensive;
|
||||
forwardDesire = rg.get (0.0f, 100.0f) + offensive;
|
||||
campDesire = rg.get (0.0f, 100.0f) + defensive;
|
||||
backoffDesire = rg.get (0.0f, 100.0f) + defensive;
|
||||
float goalDesire = rg.get (0.0f, 100.0f) + offensive;
|
||||
float forwardDesire = rg.get (0.0f, 100.0f) + offensive;
|
||||
float campDesire = rg.get (0.0f, 100.0f) + defensive;
|
||||
float backoffDesire = rg.get (0.0f, 100.0f) + defensive;
|
||||
|
||||
if (!usesCampGun ()) {
|
||||
campDesire *= 0.5f;
|
||||
}
|
||||
|
||||
tacticChoice = backoffDesire;
|
||||
tactic = 0;
|
||||
int tactic = GoalTactic::Defensive;
|
||||
float tacticChoice = backoffDesire;
|
||||
|
||||
if (campDesire > tacticChoice) {
|
||||
tacticChoice = campDesire;
|
||||
tactic = 1;
|
||||
tactic = GoalTactic::Camp;
|
||||
}
|
||||
|
||||
if (forwardDesire > tacticChoice) {
|
||||
tacticChoice = forwardDesire;
|
||||
tactic = 2;
|
||||
tactic = GoalTactic::Offensive;
|
||||
}
|
||||
|
||||
if (goalDesire > tacticChoice) {
|
||||
tactic = 3;
|
||||
tactic = GoalTactic::Goal;
|
||||
}
|
||||
return findGoalPost (tactic, defensiveNodes, offensiveNodes);
|
||||
}
|
||||
|
|
@ -209,10 +241,10 @@ int Bot::findBestGoalWhenBombAction () {
|
|||
int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
|
||||
int goalChoices[4] = { kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex, kInvalidNodeIndex };
|
||||
|
||||
if (tactic == 0 && !(*defensive).empty ()) { // careful goal
|
||||
if (tactic == GoalTactic::Defensive && !(*defensive).empty ()) { // careful goal
|
||||
postprocessGoals (*defensive, goalChoices);
|
||||
}
|
||||
else if (tactic == 1 && !graph.m_campPoints.empty ()) // camp node goal
|
||||
else if (tactic == GoalTactic::Camp && !graph.m_campPoints.empty ()) // camp node goal
|
||||
{
|
||||
// pickup sniper points if possible for sniping bots
|
||||
if (!graph.m_sniperPoints.empty () && usesSniper ()) {
|
||||
|
|
@ -222,10 +254,10 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
|
|||
postprocessGoals (graph.m_campPoints, goalChoices);
|
||||
}
|
||||
}
|
||||
else if (tactic == 2 && !(*offensive).empty ()) { // offensive goal
|
||||
else if (tactic == GoalTactic::Offensive && !(*offensive).empty ()) { // offensive goal
|
||||
postprocessGoals (*offensive, goalChoices);
|
||||
}
|
||||
else if (tactic == 3 && !graph.m_goalPoints.empty ()) // map goal node
|
||||
else if (tactic == GoalTactic::Goal && !graph.m_goalPoints.empty ()) // map goal node
|
||||
{
|
||||
// force bomber to select closest goal, if round-start goal was reset by something
|
||||
if (m_hasC4 && bots.getRoundStartTime () + 20.0f < game.time ()) {
|
||||
|
|
@ -258,7 +290,7 @@ int Bot::findGoalPost (int tactic, IntArray *defensive, IntArray *offensive) {
|
|||
postprocessGoals (graph.m_goalPoints, goalChoices);
|
||||
}
|
||||
}
|
||||
else if (tactic == 4 && !graph.m_rescuePoints.empty ()) {
|
||||
else if (tactic == GoalTactic::RescueHostage && !graph.m_rescuePoints.empty ()) {
|
||||
// force ct with hostage(s) to select closest rescue goal
|
||||
float minDist = kInfiniteDistance;
|
||||
int count = 0;
|
||||
|
|
@ -316,11 +348,6 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// too less to choice from just return all the goals
|
||||
if (goals.length () < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if historical goal
|
||||
for (const auto &hg : m_goalHist) {
|
||||
if (hg == index) {
|
||||
|
|
@ -336,6 +363,14 @@ void Bot::postprocessGoals (const IntArray &goals, int result[]) {
|
|||
return isOccupiedNode (index);
|
||||
};
|
||||
|
||||
// too less to choice from just return all the goals
|
||||
if (goals.length () < 4) {
|
||||
for (size_t i = 0; i < goals.length (); ++i) {
|
||||
result[i] = goals[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < 4; ++index) {
|
||||
auto goal = goals.random ();
|
||||
|
||||
|
|
@ -484,7 +519,7 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
// not stuck yet
|
||||
else {
|
||||
// test if there's something ahead blocking the way
|
||||
if (!isOnLadder () && cantMoveForward (dirNormal, &tr)) {
|
||||
if (!isOnLadder () && isBlockedForward (dirNormal, &tr)) {
|
||||
if (cr::fzero (m_firstCollideTime)) {
|
||||
m_firstCollideTime = game.time () + 0.2f;
|
||||
}
|
||||
|
|
@ -528,6 +563,11 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
bits |= (CollisionProbe::Strafe | CollisionProbe::Jump);
|
||||
}
|
||||
|
||||
// try to duck when graph analyzed
|
||||
if (graph.isAnalyzed ()) {
|
||||
bits |= CollisionProbe::Duck;
|
||||
}
|
||||
|
||||
// collision check allowed if not flying through the air
|
||||
if (isOnFloor () || isOnLadder () || isInWater ()) {
|
||||
uint32_t state[kMaxCollideMoves * 2 + 1] {};
|
||||
|
|
@ -657,7 +697,6 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
++i;
|
||||
|
||||
#if 0
|
||||
if (bits & CollisionProbe::Duck) {
|
||||
state[i] = 0;
|
||||
|
||||
|
|
@ -670,7 +709,6 @@ void Bot::checkTerrain (float movedDistance, const Vector &dirNormal) {
|
|||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
state[i] = 0;
|
||||
++i;
|
||||
|
||||
|
|
@ -909,24 +947,31 @@ bool Bot::updateNavigation () {
|
|||
selectBestWeapon ();
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
if (m_path->flags & NodeFlag::Ladder) {
|
||||
|
||||
}
|
||||
#else
|
||||
if ((m_pathFlags & NodeFlag::Ladder) || isOnLadder ()) {
|
||||
if (graph.exists (m_previousNodes[0]) && (graph[m_previousNodes[0]].flags & NodeFlag::Ladder)) {
|
||||
if (cr::abs (m_pathOrigin.z - pev->origin.z) > 5.0f) {
|
||||
m_pathOrigin.z += pev->origin.z - m_pathOrigin.z;
|
||||
constexpr auto kLadderOffset = Vector (0.0f, 0.0f, 16.0f);
|
||||
|
||||
if (m_pathOrigin.z >= (pev->origin.z + 16.0f)) {
|
||||
m_pathOrigin = m_path->origin + kLadderOffset;
|
||||
}
|
||||
if (m_pathOrigin.z > (pev->origin.z + 16.0f)) {
|
||||
m_pathOrigin = m_pathOrigin - Vector (0.0f, 0.0f, 16.0f);
|
||||
else if (m_pathOrigin.z < pev->origin.z + 16.0f && !isOnLadder () && isOnFloor () && !(pev->flags & FL_DUCKING)) {
|
||||
m_moveSpeed = pev->origin.distance (m_pathOrigin);
|
||||
|
||||
if (m_moveSpeed < 150.0f) {
|
||||
m_moveSpeed = 150.0f;
|
||||
}
|
||||
if (m_pathOrigin.z < (pev->origin.z - 16.0f)) {
|
||||
m_pathOrigin = m_pathOrigin + Vector (0.0f, 0.0f, 16.0f);
|
||||
else if (m_moveSpeed > pev->maxspeed) {
|
||||
m_moveSpeed = pev->maxspeed;
|
||||
}
|
||||
}
|
||||
m_destOrigin = m_pathOrigin;
|
||||
|
||||
// special detection if someone is using the ladder (to prevent to have bots-towers on ladders)
|
||||
for (const auto &client : util.getClients ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || (client.ent->v.movetype != MOVETYPE_FLY) || client.ent == nullptr || client.ent == ent ()) {
|
||||
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || (client.ent->v.movetype != MOVETYPE_FLY) || client.ent == ent ()) {
|
||||
continue;
|
||||
}
|
||||
TraceResult tr {};
|
||||
|
|
@ -994,6 +1039,7 @@ bool Bot::updateNavigation () {
|
|||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// special lift handling (code merged from podbotmm)
|
||||
if (m_pathFlags & NodeFlag::Lift) {
|
||||
|
|
@ -1043,30 +1089,29 @@ bool Bot::updateNavigation () {
|
|||
}
|
||||
|
||||
// if bot hits the door, then it opens, so wait a bit to let it open safely
|
||||
if (pev->velocity.length2d () < 10 && m_timeDoorOpen < game.time ()) {
|
||||
if (pev->velocity.lengthSq2d () < cr::sqrf (10.0f) && m_timeDoorOpen < game.time ()) {
|
||||
startTask (Task::Pause, TaskPri::Pause, kInvalidNodeIndex, game.time () + 0.5f, false);
|
||||
m_timeDoorOpen = game.time () + 1.0f; // retry in 1 sec until door is open
|
||||
|
||||
edict_t *pent = nullptr;
|
||||
++m_tryOpenDoor;
|
||||
|
||||
if (++m_tryOpenDoor > 1 && util.findNearestPlayer (reinterpret_cast <void **> (&pent), ent (), 384.0f, false, false, true, true, false)) {
|
||||
if (isPenetrableObstacle (pent->v.origin)) {
|
||||
if (m_tryOpenDoor > 2 && util.isAlive (m_lastEnemy)) {
|
||||
if (isPenetrableObstacle (m_lastEnemy->v.origin) && !cv_ignore_enemies.bool_ ()) {
|
||||
m_seeEnemyTime = game.time ();
|
||||
|
||||
m_states |= Sense::SeeingEnemy | Sense::SuspectEnemy;
|
||||
m_aimFlags |= AimFlags::Enemy;
|
||||
|
||||
m_lastEnemy = pent;
|
||||
m_enemy = pent;
|
||||
m_lastEnemyOrigin = pent->v.origin;
|
||||
m_enemy = m_lastEnemy;
|
||||
m_lastEnemyOrigin = m_lastEnemy->v.origin;
|
||||
|
||||
m_tryOpenDoor = 0;
|
||||
}
|
||||
else {
|
||||
m_tryOpenDoor = 0;
|
||||
}
|
||||
}
|
||||
else if (m_timeDoorOpen + 2.0f < game.time ()) {
|
||||
else if (m_tryOpenDoor > 4) {
|
||||
clearSearchNodes ();
|
||||
clearTasks ();
|
||||
|
||||
m_tryOpenDoor = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -2241,7 +2286,7 @@ bool Bot::advanceMovement () {
|
|||
for (const auto &link : m_path->links) {
|
||||
if (link.index == destIndex) {
|
||||
m_currentTravelFlags = link.flags;
|
||||
m_desiredVelocity = link.velocity;
|
||||
m_desiredVelocity = link.velocity - link.velocity * m_frameInterval;
|
||||
m_jumpFinished = false;
|
||||
|
||||
isCurrentJump = true;
|
||||
|
|
@ -2371,7 +2416,7 @@ void Bot::setPathOrigin () {
|
|||
}
|
||||
}
|
||||
|
||||
bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
||||
bool Bot::isBlockedForward (const Vector &normal, TraceResult *tr) {
|
||||
// checks if bot is blocked in his movement direction (excluding doors)
|
||||
|
||||
// use some TraceLines to determine if anything is blocking the current path of the bot.
|
||||
|
|
@ -2398,11 +2443,12 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
}
|
||||
return true; // bot's head will hit something
|
||||
}
|
||||
constexpr auto kVec00N16 = Vector (0.0f, 0.0f, -16.0f);
|
||||
|
||||
// bot's head is clear, check at shoulder level...
|
||||
// trace from the bot's shoulder left diagonal forward to the right shoulder...
|
||||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f;
|
||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f + normal * 24.0f;
|
||||
src = getEyesPos () + kVec00N16 - right * -16.0f;
|
||||
forward = getEyesPos () + kVec00N16 + right * 16.0f + normal * 24.0f;
|
||||
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
|
|
@ -2413,8 +2459,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
|
||||
// bot's head is clear, check at shoulder level...
|
||||
// trace from the bot's shoulder right diagonal forward to the left shoulder...
|
||||
src = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) + right * 16.0f;
|
||||
forward = getEyesPos () + Vector (0.0f, 0.0f, -16.0f) - right * -16.0f + normal * 24.0f;
|
||||
src = getEyesPos () + kVec00N16 + right * 16.0f;
|
||||
forward = getEyesPos () + kVec00N16 - right * -16.0f + normal * 24.0f;
|
||||
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
|
|
@ -2445,9 +2491,12 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
constexpr auto kVec00N17 = Vector (0.0f, 0.0f, -17.0f);
|
||||
constexpr auto kVec00N24 = Vector (0.0f, 0.0f, -24.0f);
|
||||
|
||||
// trace from the left waist to the right forward waist pos
|
||||
src = pev->origin + Vector (0.0f, 0.0f, -17.0f) - right * -16.0f;
|
||||
forward = pev->origin + Vector (0.0f, 0.0f, -17.0f) + right * 16.0f + normal * 24.0f;
|
||||
src = pev->origin + kVec00N17 - right * -16.0f;
|
||||
forward = pev->origin + kVec00N17 + right * 16.0f + normal * 24.0f;
|
||||
|
||||
// trace from the bot's waist straight forward...
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
|
@ -2458,8 +2507,8 @@ bool Bot::cantMoveForward (const Vector &normal, TraceResult *tr) {
|
|||
}
|
||||
|
||||
// trace from the left waist to the right forward waist pos
|
||||
src = pev->origin + Vector (0.0f, 0.0f, -24.0f) + right * 16.0f;
|
||||
forward = pev->origin + Vector (0.0f, 0.0f, -24.0f) - right * -16.0f + normal * 24.0f;
|
||||
src = pev->origin + kVec00N24 + right * 16.0f;
|
||||
forward = pev->origin + kVec00N24 - right * -16.0f + normal * 24.0f;
|
||||
|
||||
game.testLine (src, forward, TraceIgnore::Monsters, ent (), tr);
|
||||
|
||||
|
|
@ -2828,7 +2877,7 @@ bool Bot::isDeadlyMove (const Vector &to) {
|
|||
if (tr.fStartSolid) {
|
||||
return false;
|
||||
}
|
||||
float height = tr.flFraction * 1000.0f; // height from ground
|
||||
const float height = tr.flFraction * 1000.0f; // height from ground
|
||||
|
||||
// drops more than 150 units?
|
||||
if (lastHeight < height - 150.0f) {
|
||||
|
|
@ -2862,7 +2911,6 @@ void Bot::changePitch (float speed) {
|
|||
normalizePitch = -speed;
|
||||
}
|
||||
}
|
||||
|
||||
pev->v_angle.x = cr::wrapAngle (curent + normalizePitch);
|
||||
|
||||
if (pev->v_angle.x > 89.9f) {
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ bool BotSupport::isMonster (edict_t *ent) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (ent->v.classname.str ().startsWith ("hostage")) {
|
||||
if (isHostageEntity (ent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -226,6 +226,18 @@ bool BotSupport::isPlayerVIP (edict_t *ent) {
|
|||
return *(engfuncs.pfnInfoKeyValue (engfuncs.pfnGetInfoKeyBuffer (ent), "model")) == 'v';
|
||||
}
|
||||
|
||||
bool BotSupport::isHostageEntity (edict_t *ent) {
|
||||
if (game.isNullEntity (ent)) {
|
||||
return false;
|
||||
}
|
||||
auto classHash = ent->v.classname.str ().hash ();
|
||||
|
||||
constexpr auto kHostageEntity = StringRef::fnv1a32 ("hostage_entity");
|
||||
constexpr auto kMonsterScientist = StringRef::fnv1a32 ("monster_scientist");
|
||||
|
||||
return classHash == kHostageEntity || classHash == kMonsterScientist;
|
||||
}
|
||||
|
||||
bool BotSupport::isFakeClient (edict_t *ent) {
|
||||
if (bots[ent] != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1471,7 +1471,7 @@ void Bot::pickupItem_ () {
|
|||
|
||||
auto tab = conf.getRawWeapons ();
|
||||
|
||||
if ((tab[weaponIndex].id == Weapon::Shield || weaponIndex >= kPrimaryWeaponMinIndex || hasShield ()) && niceWeapon) {
|
||||
if ((weaponIndex >= kPrimaryWeaponMinIndex || tab[weaponIndex].id == Weapon::Shield || hasShield ()) && niceWeapon) {
|
||||
selectWeaponByIndex (weaponIndex);
|
||||
dropCurrentWeapon ();
|
||||
}
|
||||
|
|
@ -1561,9 +1561,7 @@ void Bot::pickupItem_ () {
|
|||
|
||||
// find the nearest 'unused' hostage within the area
|
||||
game.searchEntities (pev->origin, 768.0f, [&] (edict_t *ent) {
|
||||
auto classname = ent->v.classname.str ();
|
||||
|
||||
if (!classname.startsWith ("hostage_entity") && !classname.startsWith ("monster_scientist")) {
|
||||
if (!util.isHostageEntity (ent)) {
|
||||
return EntitySearchResult::Continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,35 +123,37 @@ void Bot::updateAimDir () {
|
|||
}
|
||||
else if (flags & AimFlags::PredictPath) {
|
||||
bool changePredictedEnemy = true;
|
||||
bool predictFailed = false;
|
||||
|
||||
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy) {
|
||||
if (m_timeNextTracking < game.time () && m_trackingEdict == m_lastEnemy && util.isAlive (m_lastEnemy)) {
|
||||
changePredictedEnemy = false;
|
||||
}
|
||||
|
||||
auto doFailPredict = [this] () {
|
||||
auto doFailPredict = [this] () -> void {
|
||||
if (m_timeNextTracking > game.time ()) {
|
||||
return; // do not fail instantly
|
||||
}
|
||||
m_aimFlags &= ~AimFlags::PredictPath;
|
||||
m_trackingEdict = nullptr;
|
||||
m_lookAtPredict = nullptr;
|
||||
};
|
||||
|
||||
if (changePredictedEnemy) {
|
||||
auto isPredictedIndexApplicable = [this] () -> bool {
|
||||
int pathLength = m_lastPredictLength;
|
||||
int predictNode = m_lastPredictIndex;
|
||||
|
||||
if (predictNode != kInvalidNodeIndex) {
|
||||
TraceResult tr;
|
||||
game.testLine (getEyesPos (), graph[predictNode].origin, TraceIgnore::Everything, ent (), &tr);
|
||||
|
||||
if (tr.flFraction < 0.2f) {
|
||||
pathLength = kInfiniteDistanceLong;
|
||||
if (!vistab.visible (m_currentNodeIndex, predictNode)) {
|
||||
predictNode = kInvalidNodeIndex;
|
||||
}
|
||||
}
|
||||
return predictNode != kInvalidNodeIndex && pathLength < cv_max_nodes_for_predict.int_ ();
|
||||
};
|
||||
|
||||
if (predictNode != kInvalidNodeIndex && pathLength < cv_max_nodes_for_predict.int_ ()) {
|
||||
m_lookAt = graph[predictNode].origin;
|
||||
m_lookAtSafe = m_lookAt;
|
||||
if (changePredictedEnemy) {
|
||||
if (isPredictedIndexApplicable ()) {
|
||||
m_lookAtPredict = graph[m_lastPredictIndex].origin;
|
||||
|
||||
m_timeNextTracking = game.time () + 0.25f;
|
||||
m_timeNextTracking = game.time () + rg.get (0.5f, 1.0f);
|
||||
m_trackingEdict = m_lastEnemy;
|
||||
|
||||
// feel free to fire if shootable
|
||||
|
|
@ -161,15 +163,16 @@ void Bot::updateAimDir () {
|
|||
}
|
||||
else {
|
||||
doFailPredict ();
|
||||
predictFailed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isPredictedIndexApplicable ()) {
|
||||
doFailPredict ();
|
||||
}
|
||||
}
|
||||
|
||||
if (predictFailed) {
|
||||
doFailPredict ();
|
||||
}
|
||||
else {
|
||||
m_lookAt = m_lookAtSafe;
|
||||
if (!m_lookAtPredict.empty ()) {
|
||||
m_lookAt = m_lookAtPredict;
|
||||
}
|
||||
}
|
||||
else if (flags & AimFlags::Camp) {
|
||||
|
|
@ -194,8 +197,9 @@ void Bot::updateAimDir () {
|
|||
else {
|
||||
m_lookAt = m_destOrigin;
|
||||
}
|
||||
const bool onLadder = (m_pathFlags & NodeFlag::Ladder);
|
||||
|
||||
if (m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !(m_pathFlags & NodeFlag::Ladder)) {
|
||||
if (m_canChooseAimDirection && m_seeEnemyTime + 4.0f < game.time () && m_currentNodeIndex != kInvalidNodeIndex && !onLadder) {
|
||||
auto dangerIndex = practice.getIndex (m_team, m_currentNodeIndex, m_currentNodeIndex);
|
||||
|
||||
if (graph.exists (dangerIndex) && vistab.visible (m_currentNodeIndex, dangerIndex) && !(graph[dangerIndex].flags & NodeFlag::Crouch)) {
|
||||
|
|
@ -211,8 +215,17 @@ void Bot::updateAimDir () {
|
|||
}
|
||||
}
|
||||
|
||||
// try look at next node if on ladder
|
||||
if (onLadder && m_pathWalk.hasNext ()) {
|
||||
auto nextPath = graph[m_pathWalk.next ()];
|
||||
|
||||
if ((nextPath.flags & NodeFlag::Ladder) && m_destOrigin.distanceSq (pev->origin) < cr::sqrf (120.0f) && nextPath.origin.z > m_pathOrigin.z + 45.0f) {
|
||||
m_lookAt = nextPath.origin;
|
||||
}
|
||||
}
|
||||
|
||||
// don't look at bottom of node, if reached it
|
||||
if (m_lookAt == m_destOrigin) {
|
||||
if (m_lookAt == m_destOrigin && !onLadder) {
|
||||
m_lookAt.z = getEyesPos ().z;
|
||||
}
|
||||
}
|
||||
|
|
@ -230,14 +243,15 @@ void Bot::checkDarkness () {
|
|||
}
|
||||
|
||||
// do not check every frame
|
||||
if (m_checkDarkTime + 5.0f > game.time () || cr::fzero (m_path->light)) {
|
||||
if (m_checkDarkTime > game.time () || cr::fzero (m_path->light)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto skyColor = illum.getSkyColor ();
|
||||
auto flashOn = (pev->effects & EF_DIMLIGHT);
|
||||
|
||||
if (mp_flashlight.bool_ () && !m_hasNVG) {
|
||||
auto task = Task ();
|
||||
auto task = getCurrentTaskId ();
|
||||
|
||||
if (!flashOn && task != Task::Camp && task != Task::Attack && m_heardSoundTime + 3.0f < game.time () && m_flashLevel > 30 && ((skyColor > 50.0f && m_path->light < 10.0f) || (skyColor <= 50.0f && m_path->light < 40.0f))) {
|
||||
pev->impulse = 100;
|
||||
|
|
@ -257,7 +271,7 @@ void Bot::checkDarkness () {
|
|||
issueCommand ("nightvision");
|
||||
}
|
||||
}
|
||||
m_checkDarkTime = game.time ();
|
||||
m_checkDarkTime = game.time () + rg.get (2.0f, 4.0f);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -359,12 +373,12 @@ void Bot::updateLookAngles () {
|
|||
m_idealAngles.y = direction.y;
|
||||
}
|
||||
else {
|
||||
float accel = cr::clamp (stiffness * angleDiffYaw - damping * m_lookYawVel, -accelerate, accelerate);
|
||||
const float accel = cr::clamp (stiffness * angleDiffYaw - damping * m_lookYawVel, -accelerate, accelerate);
|
||||
|
||||
m_lookYawVel += delta * accel;
|
||||
m_idealAngles.y += delta * m_lookYawVel;
|
||||
}
|
||||
float accel = cr::clamp (2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel, -accelerate, accelerate);
|
||||
const float accel = cr::clamp (2.0f * stiffness * angleDiffPitch - damping * m_lookPitchVel, -accelerate, accelerate);
|
||||
|
||||
m_lookPitchVel += delta * accel;
|
||||
m_idealAngles.x += cr::clamp (delta * m_lookPitchVel, -89.0f, 89.0f);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@
|
|||
<ClInclude Include="..\ext\crlib\crlib\platform.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\random.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\neon.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\sse2.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\sse2neon.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\string.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\thread.h" />
|
||||
<ClInclude Include="..\ext\crlib\crlib\timers.h" />
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
<Filter Include="inc\ext\linkage">
|
||||
<UniqueIdentifier>{f6a0fc04-bdf5-479b-8e5a-85eae698541e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="inc\ext\crlib\simd">
|
||||
<UniqueIdentifier>{01281138-9315-450e-be71-55e16a2e3019}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\inc\config.h">
|
||||
|
|
@ -168,6 +171,18 @@
|
|||
<ClInclude Include="..\ext\crlib\crlib\cpuflags.h">
|
||||
<Filter>inc\ext\crlib</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\constant.h">
|
||||
<Filter>inc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\sse2.h">
|
||||
<Filter>inc\ext\crlib\simd</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\sse2neon.h">
|
||||
<Filter>inc\ext\crlib\simd</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ext\crlib\crlib\simd\neon.h">
|
||||
<Filter>inc\ext\crlib\simd</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\botlib.cpp">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue