Пример #1
0
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();
			}
		}
	};
};
Пример #2
0
//=================================================================================================
// 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;
}
Пример #3
0
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);
		}
	};
}
Пример #4
0
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 );
//	}
}
Пример #5
0
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;
		}
	}
Пример #6
0
/////////////////////////////////////////////////
/// [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);
}
Пример #7
0
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);
}
Пример #8
0
/////////////////////////////////////////////////
/// 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;
}
Пример #9
0
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);
		}
	}
}
Пример #10
0
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
}
Пример #12
0
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();
}
Пример #13
0
/**
**	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;
}
Пример #14
0
/*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];
}
Пример #15
0
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) );
		}
	}
}
Пример #16
0
// 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);
}
Пример #17
0
/**
**	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;
}
Пример #18
0
/**
**  Check if the unit is an enemy
*/
bool CPlayer::IsEnemy(const CUnit *x) const
{
	return IsEnemy(x->Player);
}
Пример #19
0
/**
**	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;
}
Пример #20
0
/**
**  Check if the player is an enemy
*/
bool CPlayer::IsEnemy(const CPlayer *x) const
{
	return IsEnemy(x->Index);
}
Пример #21
0
/**
**  Check if the unit is an enemy
*/
bool CPlayer::IsEnemy(const CUnit &unit) const
{
	return IsEnemy(*unit.Player);
}
Пример #22
0
/**
**  Check if the player is an enemy
*/
bool CPlayer::IsEnemy(const CPlayer &player) const
{
	return IsEnemy(player.Index);
}
Пример #23
0
/////////////////////////////////////////////////
/// [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);
}
Пример #24
0
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;
	}
}
Пример #25
0
/////////////////////////////////////////////////
/// 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));
}