void game_cl_TeamDeathmatch::UpdateMapLocations () { inherited::UpdateMapLocations(); if (local_player) { PLAYERS_MAP_IT it = players.begin(); for(;it!=players.end();++it) { game_PlayerState* ps = it->second; u16 id = ps->GameID; if (ps->testFlag(GAME_PLAYER_FLAG_VERY_VERY_DEAD)) { Level().MapManager().RemoveMapLocation(FRIEND_LOCATION, id); continue; }; CObject* pObject = Level().Objects.net_Find(id); if (!pObject || pObject->CLS_ID != CLSID_OBJECT_ACTOR) continue; if (IsEnemy(ps)) { if (Level().MapManager().HasMapLocation(FRIEND_LOCATION, id)) { Level().MapManager().RemoveMapLocation(FRIEND_LOCATION, id); }; continue; }; if (!Level().MapManager().HasMapLocation(FRIEND_LOCATION, id)) { (Level().MapManager().AddMapLocation(FRIEND_LOCATION, id))->EnablePointer(); } } }; };
//================================================================================================= // search only alive enemies for now Unit* Game::FindUnitWithQuestItem(LevelAreaContext* lac, int quest_refid, LevelAreaContext::Entry** entry, int* unit_index, int* item_iindex) { assert(lac); for(LevelAreaContext::Entry& e : lac->entries) { for(int i = 0, len = (int)e.area->units.size(); i < len; ++i) { Unit* unit = e.area->units[i]; if(unit->IsAlive() && IsEnemy(*unit, *pc->unit)) { int iindex = unit->FindQuestItem(quest_refid); if(iindex != INVALID_IINDEX) { if(entry) *entry = &e; if(unit_index) *unit_index = i; if(item_iindex) *item_iindex = iindex; lac->Free(); return unit; } } } } lac->Free(); return nullptr; }
void game_cl_Deathmatch::OnRender () { game_PlayerState* lookat_player = Game().lookat_player(); if (m_bDamageBlockIndicators && local_player && (local_player == lookat_player)) { PLAYERS_MAP_IT it = players.begin(); for(;it!=players.end();++it) { game_PlayerState* ps = it->second; u16 id = ps->GameID; if (ps->testFlag(GAME_PLAYER_FLAG_VERY_VERY_DEAD)) continue; if (!ps->testFlag(GAME_PLAYER_FLAG_INVINCIBLE)) continue; CObject* pObject = Level().Objects.net_Find(id); if (!pObject) continue; if (!pObject || !smart_cast<CActor*>(pObject)) continue; if (ps == local_player) continue; if (!IsEnemy(ps)) continue; cl_TeamStruct *pTS = &TeamList[ModifyTeam(ps->team)]; VERIFY(pObject); CActor* pActor = smart_cast<CActor*>(pObject); VERIFY(pActor); pActor->RenderIndicator(pTS->IndicatorPos, pTS->Indicator_r1, pTS->Indicator_r2, pTS->InvincibleShader); } }; }
BOOL CGuildWarManager::JudgeGuildWar( CPlayer* pPlayer, CPlayer* pAttacker ) { // pPlayer is Die... if( pAttacker->GetObjectKind() != eObjectKind_Player || IsEnemy( pPlayer, pAttacker ) == FALSE ) { return FALSE; } return TRUE; // if( pPlayer->GetGuildMemberRank() == GUILD_MASTER ) // { // DWORD dwGuildIdxWinner = pAttacker->GetGuildIndex(); // DWORD dwGuildIdxLoser = pPlayer->GetGuildIdx(); // // End( dwGuildIdxWinner, dwGuildIdxLoser ); // //// if( g_pServerSystem->GetNation() == eNATION_CHINA || g_nServerSetNum != 4 ) //// LOOTINGMGR->AutoLooting( pPlayer, pAttacker ); // } // else // { //// if( g_pServerSystem->GetNation() == eNATION_CHINA || g_nServerSetNum != 4 ) //// LOOTINGMGR->AutoLooting( pPlayer, pAttacker ); // } }
void CSovereign::InitEnemyObjectList (CSystem *pSystem) // InitEnemyObjectList // // Compiles and caches a list of enemy objects in the system { int i; if (m_pEnemyObjectsSystem != pSystem) { m_EnemyObjects.RemoveAll(); for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); if (pObj && pObj->ClassCanAttack() && IsEnemy(pObj->GetSovereign())) m_EnemyObjects.FastAdd(pObj); } m_pEnemyObjectsSystem = pSystem; } }
///////////////////////////////////////////////// /// [Serverside] Opposition: GameObject can target a target with a harmful spell /// /// @note Relations API Tier 3 /// /// This function is not intented to have client-side counterpart by original design. /// Some gameobjects can be involved in spell casting, so server needs additional API support. /// It utilizes owners CanAttackSpell if owner exists ///////////////////////////////////////////////// bool GameObject::CanAttackSpell(Unit const* target, SpellEntry const* spellInfo, bool isAOE) const { Unit* owner = GetOwner(); if (owner) return owner->CanAttackSpell(target, spellInfo, isAOE); return IsEnemy(target); }
void Alliance::Neutral(int32_t allianceid) { if (IsNeutral(allianceid)) return; if (IsAlly(allianceid)) UnAlly(allianceid); if (IsEnemy(allianceid)) UnEnemy(allianceid); m_neutral.push_back(allianceid); Alliance * temp = m_main->m_alliances->AllianceById(allianceid); temp->SendAllianceMessage("Alliance [" + temp->m_name + "] recognizes Diplomatic Relationship with us as Neutral.", false, false); }
///////////////////////////////////////////////// /// Civilian: Unit counts as a dishonorable kill for another unit /// /// @note Relations API Tier 1 /// /// Client-side counterpart: <tt>static function (original symbol name unknown)</tt> ///////////////////////////////////////////////// bool Unit::IsCivilianForTarget(Unit const* pov) const { // Simple sanity check if (!pov) return false; // Original logic // PvP-enabled enemy npcs with civilian flag if (IsPvP() && GetTypeId() == TYPEID_UNIT && static_cast<const Creature*>(this)->IsCivilian()) return (IsTrivialForTarget(pov) && IsEnemy(pov)); return false; }
void World::InteractObjects(float time) { CalculateShootTime(time, *playerWeapon); CheckPerfomMissionTarget(*player, flagSprite, missionTarget); for (Entity *&subject : entities) { InteractBetweenEntities(*subject); if (IsEnemy(subject->name) || (subject->name == ENEMY_BULLET)) { bool isBreak = false; InteractBetweenPlayerAndEnemy(*subject, time, isBreak); if (isBreak) { break; } } else if (IntersectsRects(subject->getRect(), player->getRect())) { PlayerPickUpBonuses(*subject); } } }
void Alliance::Enemy(int32_t allianceid, bool skip /* = false*/) { if (IsEnemy(allianceid)) return; enemyactioncooldown = unixtime() + 1000*60*60*24; if (IsNeutral(allianceid)) UnNeutral(allianceid); if (IsAlly(allianceid)) UnAlly(allianceid); m_enemies.push_back(allianceid); if (skip) return; //send global message Alliance * temp = m_main->m_alliances->AllianceById(allianceid); m_main->MassMessage("Alliance " + this->m_name + " declares war against alliance " + temp->m_name + ". Diplomatic Relationship between each other alters to Hostile automatically."); temp->Enemy(m_allianceid, true); temp->SendAllianceMessage("Alliance [" + m_name + "] recognizes Diplomatic Relationship with us as Enemy.", false, false); }
bool CRelations::CheckForHostileAI(idVec3 point, int team) // grayman #3548 { // Determine if any AI are armed and hostile in // point's neighborhood. bool hostile = false; idClipModel *clipModels[ MAX_GENTITIES ]; idBounds neighborhood(idVec3(point.x-256,point.y-256,point.z), idVec3(point.x+256,point.y+256,point.z+128)); int num = gameLocal.clip.ClipModelsTouchingBounds( neighborhood, MASK_MONSTERSOLID, clipModels, MAX_GENTITIES ); for ( int i = 0 ; i < num ; i++ ) { idClipModel *cm = clipModels[i]; // don't check render entities if ( cm->IsRenderModel() ) { continue; } idEntity *ent = cm->GetEntity(); // is this an AI? if (ent && ent->IsType(idAI::Type)) { // is the AI armed? idAI* ai = static_cast<idAI*>(ent); if ( ( ai->GetNumMeleeWeapons() > 0 ) || ( ai->GetNumRangedWeapons() > 0 ) ) { // is the AI hostile to the given team? if (IsEnemy(ai->team, team)) { hostile = true; break; } } } } return hostile; // return true = hostiles found; return false = no hostiles found }
void game_cl_TeamDeathmatch::OnRender () { if (local_player) { cl_TeamStruct *pTS = &TeamList[ModifyTeam(local_player->team)]; PLAYERS_MAP_IT it = players.begin(); for(;it!=players.end();++it) { game_PlayerState* ps = it->second; u16 id = ps->GameID; if (ps->testFlag(GAME_PLAYER_FLAG_VERY_VERY_DEAD)) continue; CObject* pObject = Level().Objects.net_Find(id); if (!pObject) continue; if (!pObject || pObject->CLS_ID != CLSID_OBJECT_ACTOR) continue; if (IsEnemy(ps)) continue; if (ps == local_player) continue; float dup = 0.0f; if (/*m_bFriendlyNames &&*/ m_bShowPlayersNames) { VERIFY(pObject); CActor* pActor = smart_cast<CActor*>(pObject); VERIFY(pActor); Fvector IPos = pTS->IndicatorPos; IPos.y -= pTS->Indicator_r2; pActor->RenderText(ps->getName(), IPos, &dup, PLAYER_NAME_COLOR); } if (m_bFriendlyIndicators) { VERIFY(pObject); CActor* pActor = smart_cast<CActor*>(pObject); VERIFY(pActor); Fvector IPos = pTS->IndicatorPos; IPos.y += dup; pActor->RenderIndicator(IPos, pTS->Indicator_r1, pTS->Indicator_r2, pTS->IndicatorShader); }; } }; inherited::OnRender(); }
/** ** Choose color for selection. ** ** @param unit Pointer to the unit. ** @param type Type of the unit. ** ** @return Color for selection, or -1 if not selected. */ local int SelectionColor(const Unit* unit,const UnitType* type) { if( unit->Selected || (unit->Blink&1) ) { if( unit->Player->Player==PlayerNumNeutral ) { return ColorYellow; } // FIXME: better allied? if( unit->Player==ThisPlayer ) { return ColorGreen; } if( IsEnemy(ThisPlayer,unit) ) { return ColorRed; } return unit->Player->Color; } // If building mark all own buildings if( CursorBuilding && type->Building && unit->Player==ThisPlayer ) { return ColorGray; } return -1; }
/*UNUSED*/ int Material(board_t board, int player) { Coord crd; int column; int score[2]; /* each side's score */ int ally, enemy, playerBlocked = 1, opponentBlocked = 1; /* Check for win or tie situations: */ /* Since getMoves doesn't provide any moves that leave the king at CHECK, if there aren't any moves, it is MATE */ for (int k = 0; k < BOARD_SIZE*BOARD_SIZE; k++) { crd.i_coord = (int)mod(k, BOARD_SIZE); crd.j_coord = k / BOARD_SIZE; ally = IsAlly(GetContentOfCoord(board, crd), player); enemy = IsEnemy(GetContentOfCoord(board, crd), player); if (GetContentOfCoord(board, crd) != EMPTY) { if (ally > 0 && canMoveThisTool(board, crd) == 1) playerBlocked = 0; if (enemy > 0 && canMoveThisTool(board, crd) == 1) opponentBlocked = 0; } } if (playerBlocked && !opponentBlocked) return -1000000; else if (playerBlocked && opponentBlocked) return -999999; else if (opponentBlocked) return 1000000; /* this is the first pass: set up pawnRow, piecesScoreValue, and pawnsScoreValue. */ for (int i = 0; i < 10; ++i) { // Pawns are set to impossible locations. pawnRow[LIGHT][i] = BOARD_SIZE; pawnRow[DARK][i] = -1; } piecesScoreValue[LIGHT] = 0; piecesScoreValue[DARK] = 0; pawnsScoreValue[LIGHT] = 0; pawnsScoreValue[DARK] = 0; for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { crd.i_coord = i; crd.j_coord = j; if (GetContentOfCoord(board, crd) == EMPTY) continue; if (GetContentOfCoord(board, crd) == WHITE_P || GetContentOfCoord(board, crd) == BLACK_P) { pawnsScoreValue[getColorInLightOrDark(board, crd)] += piece_value[0]; // reminder: int piece_value[6] = {100, 300, 300, 500, 900, 0}; column = i + 1; /* add 1 because of the extra column in the array */ if (getColorInLightOrDark(board, crd) == LIGHT) { if (pawnRow[LIGHT][column] > j) pawnRow[LIGHT][column] = j; } else { if (pawnRow[DARK][column] < j) pawnRow[DARK][column] = j; } } else piecesScoreValue[getColorInLightOrDark(board, crd)] += piece_value[(int)get_eToolFromType(GetContentOfCoord(board, crd))]; } } /* this is the second pass: evaluate each piece */ score[LIGHT] = piecesScoreValue[LIGHT] + pawnsScoreValue[LIGHT]; score[DARK] = piecesScoreValue[DARK] + pawnsScoreValue[DARK]; for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { crd.i_coord = i; crd.j_coord = j; if (GetContentOfCoord(board, crd) == EMPTY) continue; if (getColorInLightOrDark(board, crd) == LIGHT) { char tool = GetContentOfCoord(board, crd); if (tool == WHITE_P) score[LIGHT] += scoreWhitePawns(crd); else if (tool == WHITE_N) score[LIGHT] += knightBonus[i][j]; else if (tool == WHITE_B) score[LIGHT] += bishopBonus[i][j]; else if (tool == WHITE_R) { if (pawnRow[LIGHT][i + 1] == BOARD_SIZE) { if (pawnRow[DARK][i + 1] == -1) score[LIGHT] += ROOK_OPEN_FILE_BONUS; else score[LIGHT] += ROOK_SEMI_OPEN_FILE_BONUS; } if (j == 6) score[LIGHT] += ROOK_ON_SEVENTH_BONUS; } else if (tool == WHITE_K) { if (piecesScoreValue[DARK] <= 1200) score[LIGHT] += kingEndGameBonus[i][j]; else score[LIGHT] += scoreWhiteKing(crd); } } else // DARK { char tool = GetContentOfCoord(board, crd); if (tool == BLACK_P) score[DARK] += scoreBlackPawns(crd); else if (tool == BLACK_N) score[DARK] += knightBonus[i][BOARD_SIZE - 1 - j]; else if (tool == BLACK_B) score[DARK] += bishopBonus[i][BOARD_SIZE - 1 - j]; else if (tool == BLACK_R) { if (pawnRow[DARK][i + 1] == -1) { if (pawnRow[LIGHT][i + 1] == BOARD_SIZE) score[DARK] += ROOK_OPEN_FILE_BONUS; else score[DARK] += ROOK_SEMI_OPEN_FILE_BONUS; } if (j == 1) score[DARK] += ROOK_ON_SEVENTH_BONUS; } else if (tool == BLACK_K) { if (piecesScoreValue[LIGHT] <= 1200) score[DARK] += kingEndGameBonus[i][BOARD_SIZE - 1 - j]; else score[DARK] += scoreBlackKing(crd); } } } } /* the score[] array is set, now return the score relative to the side to move */ if (player == WHITE_PLAYER) return score[LIGHT]; return score[DARK]; }
void King::listMoves(vector<Move *> *mList) { Piece *p; unsigned int castlingFlags = pulchess_board->castlingFlags; // mosse normali // for(int i=-1; i<2; i++) { for(int j=-1; j<2; j++) { if( !COORDSOK(x+i, y+j) ) continue; if(i==0 && j==0) continue; p = pulchess_board->GetPiece(x+i, y+j); // se esiste una pedina nel posto scelto, ed e' del colore opposto // oppure se la casella prescelta e' vuota, allora e' una mossa valida. if( p != NULL && IsEnemy(p) ) { mList->push_back( new Move( xy2pos(x+i,y+j), pos, 1) ); } else if( p == NULL) { mList->push_back( new Move( xy2pos(x+i,y+j), pos, 0) ); } } } // Kingside castling // if( //moveCount == 0 && (( colour == WHITE && (castlingFlags&CASTLING_WHITE_K) ) || ( colour == BLACK && (castlingFlags&CASTLING_BLACK_K) )) && pulchess_board->GetPiece(pos+1) == NULL && pulchess_board->GetPiece(pos+2) == NULL ) { p = pulchess_board->GetPiece(pos+3); if( p != NULL && p->GetKind() == PIECE_ROOK && //p->moveCount == 0 && !pulchess_board->CanEatThis(pos+1, colour) && !pulchess_board->CanEatThis(pos+2, colour) ) { mList->push_back( new CastlingMove(KINGSIDE_CASTLING, colour) ); } } // Queenside castling // if(( ( colour == WHITE && (castlingFlags&CASTLING_WHITE_Q) ) || ( colour == BLACK && (castlingFlags&CASTLING_BLACK_Q) ) ) && pulchess_board->GetPiece(pos-1) == NULL && pulchess_board->GetPiece(pos-2) == NULL && pulchess_board->GetPiece(pos-3) == NULL ) { if( !pulchess_board->CanEatThis(pos-1, colour) && !pulchess_board->CanEatThis(pos-2, colour) ) { mList->push_back( new CastlingMove(QUEENSIDE_CASTLING, colour) ); } } }
// 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); }
/** ** Attack units in distance, with large missile ** ** Choose the best target, that can be attacked. It takes into ** account allied unit which could be hit by the missile ** ** @param u Find in distance for this unit. ** @param range Distance range to look. ** ** @return Unit to be attacked. ** ** @note This could be improved, for better performance / better trade. ** @note Limited to attack range smaller than 16. ** @note Will be moved to unit_ai.c soon. */ local Unit* FindRangeAttack(const Unit* u, int range) { int x, y, n, cost,d,effective_hp,enemy_count; int missile_range,attackrange,hp_damage_evaluate; int good[32][32], bad[32][32]; Unit* table[UnitMax]; Unit* dest; const UnitType* dtype; const UnitType* type; const Player* player; int xx, yy; int best_x, best_y, best_cost; int i, sbad, sgood; Unit* best; type = u->Type; player = u->Player; // If catapult, count units near the target... // FIXME : make it configurable // missile_range = type->Missile.Missile->Range + range - 1; attackrange=u->Stats->AttackRange; // Evaluation of possible damage... hp_damage_evaluate=u->Stats->BasicDamage+u->Stats->PiercingDamage; DebugCheck(2 * missile_range + 1 >= 32); x = u->X; y = u->Y; n = SelectUnits(x - missile_range, y - missile_range, x + missile_range + 1, y + missile_range + 1, table); if (!n) { return NoUnitP; } for (y = 0; y < 2 * missile_range + 1; y++) { for (x = 0; x < 2 * missile_range + 1; x++) { good[y][x] = 0; bad[y][x] = 0; } } enemy_count=0; // FILL good/bad... for (i = 0; i < n; i++) { dest = table[i]; dtype = dest->Type; // // unusable unit // // FIXME: did SelectUnits already filter this. if (dest->Removed || dest->Invisible || !u->HP || !(dest->Visible & (1 << player->Player)) || dest->Orders[0].Action == UnitActionDie) { table[i] = 0; continue; } // won't be a target... if (!CanTarget(type, dtype)) { // can't be attacked. table[i] = 0; continue; } if (!IsEnemy(player, dest)) { // a friend or neutral table[i] = 0; // Calc a negative cost // The gost is more important when the unit would be killed // by our fire. // It costs ( is positive ) if hp_damage_evaluate>dest->HP ... ) // FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR cost = HEALTH_FACTOR*(2*hp_damage_evaluate-dest->HP) / (dtype->TileWidth * dtype->TileWidth); if (cost < 1) { cost = 1; } cost = (-cost); } else { // // Calculate the costs to attack the unit. // Unit with the smallest attack costs will be taken. // cost = 0; // // Priority 0-255 // cost += dtype->Priority * PRIORITY_FACTOR; // // Remaining HP (Health) 0-65535 // // Give a boost to unit we can kill in one shoot only // // calculate HP which will remain in the enemy unit, after hit // effective_hp=(dest->HP-2*hp_damage_evaluate); // // Unit we won't kill are evaluated the same // if (effective_hp>0){ effective_hp=0; } // // Unit we are sure to kill are all evaluated the same ( except PRIORITY ) // if (effective_hp<(-hp_damage_evaluate)){ effective_hp=(-hp_damage_evaluate); } // // Here, effective_hp vary from -hp_damage_evaluate ( unit will be killed) to 0 ( unit can't be killed ) // => we prefer killing rather than only hitting... // cost += (-effective_hp) * HEALTH_FACTOR; // // Unit can attack back. // if (CanTarget(dtype, type)) { cost += CANATTACK_BONUS; } // // the cost may be divided accros multiple cells // cost=cost / (dtype->TileWidth * dtype->TileWidth); if (cost < 1) { cost = 1; } d=MapDistanceBetweenUnits(u,dest); // FIXME: we don't support moving away! if((d<type->MinAttackRange)||(!UnitReachable(u,dest,attackrange))) { table[i]=0; } else { enemy_count++; } } x = dest->X - u->X + missile_range+1; y = dest->Y - u->Y + missile_range+1; // Mark the good/bad array... for (xx = 0; xx < dtype->TileWidth; xx++) { for (yy = 0; yy < dtype->TileWidth; yy++) { if ((x + xx < 0) || (y + yy < 0) || (x + xx >= 2 * missile_range + 1) || (y + yy >= 2 * missile_range + 1)) { continue; } if (cost < 0) { good[y + yy][x + xx] -= cost; } else { bad[y + yy][x + xx] += cost; } } } } if (!enemy_count) { return NoUnitP; } // Find the best area... // The target which provide the best bad/good ratio is choosen... best_x = -1; best_y = -1; best_cost = -1; best = NoUnitP; for (i = 0; i < n; i++) { if (!table[i]) { continue; } dest = table[i]; dtype = dest->Type; // put in x-y the real point which will be hit... // ( only meaningfull when dtype->TileWidth>1 ) if (u->X<dest->X){ x=dest->X; } else if (u->X>dest->X+dtype->TileWidth-1){ x=dest->X+dtype->TileWidth-1; } else { x=u->X; } if (u->Y<dest->Y){ y=dest->Y; } else if(u->Y>dest->Y+dtype->TileWidth-1){ y=dest->Y+dtype->TileWidth-1; } else { y=u->Y; } // Make x,y relative to u->x... x = x - u->X + missile_range+1; y = y - u->Y + missile_range+1; sbad = 0; sgood = 0; for (yy = -(type->Missile.Missile->Range - 1); yy <= type->Missile.Missile->Range - 1; yy++) { for (xx = -(type->Missile.Missile->Range - 1); xx <= type->Missile.Missile->Range - 1; xx++) { if ((x + xx < 0) || (y + yy < 0) || ((x + xx) >= 2 * missile_range + 1) || ((y + yy) >= 2 * missile_range + 1)) { continue; } sbad += bad[y + yy][x + xx]; sgood += good[y + yy][x + xx]; if ((!yy) && (!xx)) { sbad += bad[y + yy][x + xx]; sgood += good[y + yy][x + xx]; } } } // don't consider small damages... if (sgood < 20) { sgood = 20; } cost = sbad / sgood; if (cost > best_cost) { best_cost = cost; best = dest; } } return best; }
/** ** Check if the unit is an enemy */ bool CPlayer::IsEnemy(const CUnit *x) const { return IsEnemy(x->Player); }
/** ** Attack units in distance. ** ** If the unit can attack must be handled by caller. ** Choose the best target, that can be attacked. ** ** @param unit Find in distance for this unit. ** @param range Distance range to look. ** ** @return Unit to be attacked. ** ** @note This could be improved, for better performance. */ global Unit* AttackUnitsInDistance(const Unit* unit,int range) { const Unit* dest; const UnitType* type; const UnitType* dtype; Unit* table[UnitMax]; int x; int y; int n; int i; int d; int attackrange; int cost; const Player* player; const Unit* best_unit; int best_cost; DebugLevel3Fn("(%d)%s\n" _C_ UnitNumber(unit) _C_ unit->Type->Ident); // if necessary, take possible damage on allied units into account... if (unit->Type->Missile.Missile->Range>1 && (range+unit->Type->Missile.Missile->Range<15)) { return FindRangeAttack(unit,range); } // // Select all units in range. // x=unit->X; y=unit->Y; n=SelectUnits(x-range,y-range,x+range+1,y+range+1,table); best_unit=NoUnitP; best_cost=INT_MAX; player=unit->Player; type=unit->Type; attackrange=unit->Stats->AttackRange; // // Find the best unit to attack // for( i=0; i<n; ++i ) { dest=table[i]; // // unusable unit // // FIXME: did SelectUnits already filter this. if( dest->Removed || dest->Invisible || !unit->HP || !(dest->Visible&(1<<player->Player)) || dest->Orders[0].Action==UnitActionDie ) { continue; } if( !IsEnemy(player,dest) ) { // a friend or neutral continue; } dtype=dest->Type; if( !CanTarget(type,dtype) ) { // can't be attacked. continue; } // // Calculate the costs to attack the unit. // Unit with the smallest attack costs will be taken. // cost=0; // // Priority 0-255 // cost-=dtype->Priority*PRIORITY_FACTOR; // // Remaining HP (Health) 0-65535 // cost+=dest->HP*HEALTH_FACTOR; // // Unit in attack range? // d=MapDistanceBetweenUnits(unit,dest); if( d<type->MinAttackRange ) { // FIXME: we don't support moving away! continue; } if( d<attackrange && d>type->MinAttackRange ) { cost+=d*INRANGE_FACTOR; cost-=INRANGE_BONUS; } else { cost+=d*DISTANCE_FACTOR; } // // Unit can attack back. // if( CanTarget(dtype,type) ) { cost-=CANATTACK_BONUS; } DebugLevel3Fn("%s -> %s\t%08x\n" _C_ type->Ident _C_ dtype->Ident _C_ cost); // // Take this target? // if( cost<best_cost && (d<attackrange || UnitReachable(unit,dest,attackrange)) ) { best_unit=dest; best_cost=cost; } } /* if( best_unit ) { DebugLevel3Fn("Attacking (%d)%s -> %s\n" _C_ UnitNumber(unit) _C_ unit->Type->Ident _C_ best_unit->Type->Ident); } */ // FIXME: No idea how to make this correct, without cast!! return (Unit*)best_unit; }
/** ** Check if the player is an enemy */ bool CPlayer::IsEnemy(const CPlayer *x) const { return IsEnemy(x->Index); }
/** ** Check if the unit is an enemy */ bool CPlayer::IsEnemy(const CUnit &unit) const { return IsEnemy(*unit.Player); }
/** ** Check if the player is an enemy */ bool CPlayer::IsEnemy(const CPlayer &player) const { return IsEnemy(player.Index); }
///////////////////////////////////////////////// /// [Serverside] Opposition: Unit can attack a target on sight /// /// @note Relations API Tier 3 /// /// This function is not intented to have client-side counterpart by original design. /// It utilizes CanAttack with a small exclusion for Feign-Death targets and a hostile-only check. /// Typically used in AIs in MoveInLineOfSight ///////////////////////////////////////////////// bool Unit::CanAttackOnSight(Unit const* target) const { return CanAttack(target) && !target->IsFeigningDeathSuccessfully() && target->GetEvade() != EVADE_HOME && IsEnemy(target); }
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; } }
///////////////////////////////////////////////// /// Opposition: Unit treats another unit as an enemy it can attack (generic) /// /// @note Relations API Tier 1 /// /// Client-side counterpart: <tt>CGUnit_C::CanAttack(const CGUnit_C *this, const CGUnit_C *unit)</tt> /// Backbone of all spells which can target hostile units. ///////////////////////////////////////////////// bool Unit::CanAttack(const Unit* unit) const { // Simple sanity check if (!unit) return false; // Original logic // Creatures cannot attack player ghosts, unless it is a specially flagged ghost creature if (GetTypeId() == TYPEID_UNIT && unit->GetTypeId() == TYPEID_PLAYER && static_cast<const Player*>(unit)->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) { if (!(static_cast<const Creature*>(this)->GetCreatureInfo()->CreatureTypeFlags & CREATURE_TYPEFLAGS_GHOST_VISIBLE)) return false; } // We can't attack unit when at least one of these flags is present on it: const uint32 mask = (UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_NON_ATTACKABLE_2 | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_SELECTABLE); if (unit->HasFlag(UNIT_FIELD_FLAGS, mask)) return false; // Cross-check immunity and sanctuary flags: this <-> unit const bool thisPlayerControlled = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); if (thisPlayerControlled) { if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PLAYER)) return false; } else if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) return false; const bool unitPlayerControlled = unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); if (unitPlayerControlled) { if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PLAYER)) return false; } else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) return false; if (thisPlayerControlled || unitPlayerControlled) { if (thisPlayerControlled && unitPlayerControlled) { if (IsFriend(unit)) return false; const Player* thisPlayer = GetControllingPlayer(); if (!thisPlayer) return true; const Player* unitPlayer = unit->GetControllingPlayer(); if (!unitPlayer) return true; if (thisPlayer->IsInDuelWith(unitPlayer)) return true; if (unitPlayer->IsPvP()) return true; if (thisPlayer->IsPvPFreeForAll() && unitPlayer->IsPvPFreeForAll()) return true; return false; } return (!IsFriend(unit)); } return (IsEnemy(unit) || unit->IsEnemy(this)); }