void CommandBadGuys(int ticks) { int count = 0; int delayModifier; int rollLimit; switch (ConfigGetEnum(&gConfig, "Game.Difficulty")) { case DIFFICULTY_VERYEASY: delayModifier = 4; rollLimit = 300; break; case DIFFICULTY_EASY: delayModifier = 2; rollLimit = 200; break; case DIFFICULTY_HARD: delayModifier = 1; rollLimit = 75; break; case DIFFICULTY_VERYHARD: delayModifier = 1; rollLimit = 50; break; default: delayModifier = 1; rollLimit = 100; break; } CA_FOREACH(TActor, actor, gActors) if (!actor->isInUse) { continue; } const CharBot *bot = ActorGetCharacter(actor)->bot; if (!(actor->PlayerUID >= 0 || (actor->flags & FLAGS_PRISONER))) { if ((actor->flags & (FLAGS_VICTIM | FLAGS_GOOD_GUY)) != 0) { gAreGoodGuysPresent = 1; } count++; int cmd = 0; // Wake up if it can see a player if ((actor->flags & FLAGS_SLEEPING) && actor->aiContext->Delay == 0) { if (CanSeeAPlayer(actor)) { actor->flags &= ~FLAGS_SLEEPING; ActorSetAIState(actor, AI_STATE_NONE); } actor->aiContext->Delay = bot->actionDelay * delayModifier; // Randomly change direction int newDir = (int)actor->direction + ((rand() % 2) * 2 - 1); if (newDir < (int)DIRECTION_UP) { newDir = (int)DIRECTION_UPLEFT; } if (newDir == (int)DIRECTION_COUNT) { newDir = (int)DIRECTION_UP; } cmd = DirectionToCmd((int)newDir); } // Go to sleep if the player's too far away if (!(actor->flags & FLAGS_SLEEPING) && actor->aiContext->Delay == 0 && !(actor->flags & FLAGS_AWAKEALWAYS)) { if (!IsCloseToPlayer(actor->Pos, (40 * 16) << 8)) { actor->flags |= FLAGS_SLEEPING; ActorSetAIState(actor, AI_STATE_IDLE); } } if (!actor->dead && !(actor->flags & FLAGS_SLEEPING)) { bool bypass = false; const int roll = rand() % rollLimit; if (actor->flags & FLAGS_FOLLOWER) { if (IsCloseToPlayer(actor->Pos, 32 << 8)) { cmd = 0; ActorSetAIState(actor, AI_STATE_IDLE); } else { cmd = AIGoto( actor, AIGetClosestPlayerPos(actor->Pos), true); ActorSetAIState(actor, AI_STATE_FOLLOW); } } else if (!!(actor->flags & FLAGS_SNEAKY) && !!(actor->flags & FLAGS_VISIBLE) && DidPlayerShoot()) { cmd = AIHuntClosest(actor) | CMD_BUTTON1; if (actor->flags & FLAGS_RUNS_AWAY) { // Turn back and shoot for running away characters cmd = AIReverseDirection(cmd); } bypass = true; ActorSetAIState(actor, AI_STATE_HUNT); } else if (actor->flags & FLAGS_DETOURING) { cmd = BrightWalk(actor, roll); ActorSetAIState(actor, AI_STATE_TRACK); } else if (actor->aiContext->Delay > 0) { cmd = actor->lastCmd & ~CMD_BUTTON1; } else { if (roll < bot->probabilityToTrack) { cmd = AIHuntClosest(actor); ActorSetAIState(actor, AI_STATE_HUNT); } else if (roll < bot->probabilityToMove) { cmd = DirectionToCmd(rand() & 7); ActorSetAIState(actor, AI_STATE_TRACK); } else { cmd = 0; } actor->aiContext->Delay = bot->actionDelay * delayModifier; } if (!bypass) { if (WillFire(actor, roll)) { cmd |= CMD_BUTTON1; if (!!(actor->flags & FLAGS_FOLLOWER) && (actor->flags & FLAGS_GOOD_GUY)) { // Shoot in a random direction away for (int j = 0; j < 10; j++) { direction_e d = (direction_e)(rand() % DIRECTION_COUNT); if (!IsFacingPlayer(actor, d)) { cmd = DirectionToCmd(d) | CMD_BUTTON1; break; } } } if (actor->flags & FLAGS_RUNS_AWAY) { // Turn back and shoot for running away characters cmd |= AIReverseDirection(AIHuntClosest(actor)); } ActorSetAIState(actor, AI_STATE_HUNT); } else { if ((actor->flags & FLAGS_VISIBLE) == 0) { // I think this is some hack to make sure invisible enemies don't fire so much ActorGetGun(actor)->lock = 40; } if (cmd && !IsDirectionOK(actor, CmdToDirection(cmd)) && (actor->flags & FLAGS_DETOURING) == 0) { Detour(actor); cmd = 0; ActorSetAIState(actor, AI_STATE_TRACK); } } } } actor->aiContext->Delay = MAX(0, actor->aiContext->Delay - ticks); CommandActor(actor, cmd, ticks); } else if ((actor->flags & FLAGS_PRISONER) != 0) { CommandActor(actor, 0, ticks); } CA_FOREACH_END() if (gMission.missionData->Enemies.size > 0 && gMission.missionData->EnemyDensity > 0 && count < MAX(1, (gMission.missionData->EnemyDensity * ConfigGetInt(&gConfig, "Game.EnemyDensity")) / 100)) { NActorAdd aa = NActorAdd_init_default; aa.UID = ActorsGetNextUID(); aa.CharId = CharacterStoreGetRandomBaddieId( &gCampaign.Setting.characters); aa.Direction = rand() % DIRECTION_COUNT; const Character *c = CArrayGet(&gCampaign.Setting.characters.OtherChars, aa.CharId); aa.Health = CharacterGetStartingHealth(c, true); aa.FullPos = PlaceAwayFromPlayers(&gMap); GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD); e.u.ActorAdd = aa; GameEventsEnqueue(&gGameEvents, e); gBaddieCount++; } }
void CommandBadGuys(int ticks) { int count = 0; int delayModifier; int rollLimit; switch (gConfig.Game.Difficulty) { case DIFFICULTY_VERYEASY: delayModifier = 4; rollLimit = 300; break; case DIFFICULTY_EASY: delayModifier = 2; rollLimit = 200; break; case DIFFICULTY_HARD: delayModifier = 1; rollLimit = 75; break; case DIFFICULTY_VERYHARD: delayModifier = 1; rollLimit = 50; break; default: delayModifier = 1; rollLimit = 100; break; } for (int i = 0; i < (int)gActors.size; i++) { TActor *actor = CArrayGet(&gActors, i); if (!actor->isInUse) { continue; } const CharBot *bot = actor->character->bot; if (!(actor->pData || (actor->flags & FLAGS_PRISONER))) { if ((actor->flags & (FLAGS_VICTIM | FLAGS_GOOD_GUY)) != 0) { gAreGoodGuysPresent = 1; } count++; int cmd = 0; // Wake up if it can see a player if ((actor->flags & FLAGS_SLEEPING) && actor->aiContext->Delay == 0) { if (CanSeeAPlayer(actor)) { actor->flags &= ~FLAGS_SLEEPING; actor->aiContext->State = AI_STATE_NONE; } actor->aiContext->Delay = bot->actionDelay * delayModifier; // Randomly change direction int newDir = (int)actor->direction + ((rand() % 2) * 2 - 1); if (newDir < (int)DIRECTION_UP) { newDir = (int)DIRECTION_UPLEFT; } if (newDir == (int)DIRECTION_COUNT) { newDir = (int)DIRECTION_UP; } cmd = DirectionToCmd((int)newDir); } // Go to sleep if the player's too far away if (!(actor->flags & FLAGS_SLEEPING) && actor->aiContext->Delay == 0 && !(actor->flags & FLAGS_AWAKEALWAYS)) { if (!IsCloseToPlayer(actor->Pos, (40 * 16) << 8)) { actor->flags |= FLAGS_SLEEPING; actor->aiContext->State = AI_STATE_IDLE; } } if (!actor->dead && !(actor->flags & FLAGS_SLEEPING)) { bool bypass = false; const int roll = rand() % rollLimit; if (actor->flags & FLAGS_FOLLOWER) { if (IsCloseToPlayer(actor->Pos, 32 << 8)) { cmd = 0; actor->aiContext->State = AI_STATE_IDLE; } else { cmd = AIGoto( actor, AIGetClosestPlayerPos(actor->Pos), true); actor->aiContext->State = AI_STATE_FOLLOW; } } else if (!!(actor->flags & FLAGS_SNEAKY) && !!(actor->flags & FLAGS_VISIBLE) && DidPlayerShoot()) { cmd = AIHuntClosest(actor) | CMD_BUTTON1; if (actor->flags & FLAGS_RUNS_AWAY) { // Turn back and shoot for running away characters cmd = AIReverseDirection(cmd); } bypass = 1; actor->aiContext->State = AI_STATE_HUNT; } else if (actor->flags & FLAGS_DETOURING) { cmd = BrightWalk(actor, roll); actor->aiContext->State = AI_STATE_TRACK; } else if (actor->aiContext->Delay > 0) { cmd = actor->lastCmd & ~CMD_BUTTON1; } else { if (roll < bot->probabilityToTrack) { cmd = AIHuntClosest(actor); actor->aiContext->State = AI_STATE_HUNT; } else if (roll < bot->probabilityToMove) { cmd = DirectionToCmd(rand() & 7); actor->aiContext->State = AI_STATE_TRACK; } else { cmd = 0; } actor->aiContext->Delay = bot->actionDelay * delayModifier; } if (!bypass) { if (WillFire(actor, roll)) { cmd |= CMD_BUTTON1; if (!!(actor->flags & FLAGS_FOLLOWER) && (actor->flags & FLAGS_GOOD_GUY)) { // Shoot in a random direction away for (int j = 0; j < 10; j++) { direction_e d = (direction_e)(rand() % DIRECTION_COUNT); if (!IsFacingPlayer(actor, d)) { cmd = DirectionToCmd(d) | CMD_BUTTON1; break; } } } if (actor->flags & FLAGS_RUNS_AWAY) { // Turn back and shoot for running away characters cmd |= AIReverseDirection(AIHuntClosest(actor)); } actor->aiContext->State = AI_STATE_HUNT; } else { if ((actor->flags & FLAGS_VISIBLE) == 0) { // I think this is some hack to make sure invisible enemies don't fire so much actor->weapon.lock = 40; } if (cmd && !IsDirectionOK(actor, CmdToDirection(cmd)) && (actor->flags & FLAGS_DETOURING) == 0) { Detour(actor); cmd = 0; actor->aiContext->State = AI_STATE_TRACK; } } } } actor->aiContext->Delay = MAX(0, actor->aiContext->Delay - ticks); CommandActor(actor, cmd, ticks); } else if ((actor->flags & FLAGS_PRISONER) != 0) { CommandActor(actor, 0, ticks); } } if (gMission.missionData->Enemies.size > 0 && gMission.missionData->EnemyDensity > 0 && count < MAX(1, (gMission.missionData->EnemyDensity * gConfig.Game.EnemyDensity) / 100)) { Character *character = CharacterStoreGetRandomBaddie( &gCampaign.Setting.characters); TActor *baddie = CArrayGet(&gActors, ActorAdd(character, NULL)); PlaceBaddie(baddie); gBaddieCount++; } }