Skip to content

Bot integration help

Dreamy Cecil edited this page May 27, 2024 · 7 revisions

At this time it is not recommended to integrate code from this project into your mods! One of the major reasons is incompatibility with Serious Sam Classics Patch due to its own network functionality that conflicts with the bot mod and may cause undefined behavior!

This document should walk you through simple steps that will help you integrate bots into your own mod.

Keep in mind that if your mod has many drastic changes to gameplay mechanics, don't be afraid of rewriting the bot code to your liking.

Simple guide to developer comments:

  • [Cecil] <date> - indicates specifically when a certain feature has been implemented.
  • [Cecil] TEMP - temporary feature that can be applied permanently or removed at a later time.
  • [Cecil] TODO - notes to self about features that need to be implemented.
  • [Cecil] NOTE - important note about a certain feature. Advised to search for them for extra help with integration.

All projects

  • Bots folder should be present at the very root of mod sources (alongside Engine, EntitiesMP etc.).
  • Bots/_BotMod.h is included in StdH.h in EntitiesMP (at the end) and in StdAfx.h in GameMP (replacing EntitiesMP/Player.h).

EntitiesMP

  • Include BotModGlobal.es, NavMeshGenerator.es and PlayerBot.es entities in your project and copy the custom build step.
  • All instances of GetMaxPlayers() and GetPlayerEntity() are replaced with CECIL_GetMaxPlayers() and CECIL_GetPlayerEntity() respectively.
    • Except for three places where it's noted not to do so in Player.es: 1 2 3.
  • All instances of IsOfClass(<ptr>, "Player") and IsDerivedFromClass(<ptr>, "Player") are replaced with IS_PLAYER(<ptr>). Can be achieved automatically by replacing , "Player") with ) and then fixing errors where they appear.
  • All instances of GetMyPlayerIndex() are replaced with CECIL_PlayerIndex(<ptr>).
    • If it's just GetMyPlayerIndex(), it's replaced with CECIL_PlayerIndex(this).
    • If it's ptr->GetMyPlayerIndex(), it's replaced with CECIL_PlayerIndex(ptr).
  • All Player.es additions that are on top of the vanilla Player.es (commented with // [Cecil]).
  • Also optional additions that are on top of the vanilla Common/HUD.cpp for making bots appear in the player list (also commented with // [Cecil]).

GameMP

  • Initialization function at the end of CGame::InitInternal():
    CECIL_InitBotMod();
  • End function at the end of CGame::EndInternal():
    CECIL_EndBotMod();
  • Bot game start function in CGame::NewGame() right after the _pNetwork->StartPeerToPeer_t call:
    CECIL_BotGameStart(sp);
  • Bot game cleanup at the very beginning of CGame::StopGame():
    CECIL_BotGameCleanup();

To make bots appear in the observer views, follow these steps

All of this takes place within the CGame::GameRedrawView() function. See how it's implemented in this mod here.

  1. Create a container of player entities like so: CDynamicContainer<CPlayer> cenPlayerEntities; before these lines:
  // fill in all players that are not local
  INDEX ctNonlocals = 0;
  1. Remove this array after it:
  CEntity *apenNonlocals[16];
  memset(apenNonlocals, 0, sizeof(apenNonlocals));
  1. Replace apenNonlocals[ctNonlocals++] = pen; with cenPlayerEntities.Add((CPlayer *)pen); within the for loop.

  2. Add the following block after the for loop:

  // [Cecil] Add bots
  for (INDEX iBot = 0; iBot < _aPlayerBots.Count(); iBot++) {
    CPlayerBot *penBot = (CPlayerBot *)_aPlayerBots[iBot].pen;
    cenPlayerEntities.Add(penBot);
  }
  // [Cecil] Count non-local players
  ctNonlocals = cenPlayerEntities.Count();
  1. Replace apenViewers[ctViewers++] = apenNonlocals[iPlayer]; with apenViewers[ctViewers++] = cenPlayerEntities.Pointer(iPlayer); within the next for block.

Resulting block of code:

  CDynamicContainer<CPlayer> cenPlayerEntities;

  // fill in all players that are not local
  INDEX ctNonlocals = 0;
  {for (INDEX i=0; i<16; i++) {
    CEntity *pen = CEntity::GetPlayerEntity(i);
    if (pen!=NULL && !_pNetwork->IsPlayerLocal(pen)) {
      cenPlayerEntities.Add((CPlayer *)pen);
    }
  }}

  // [Cecil] Add bots
  for (INDEX iBot = 0; iBot < _aPlayerBots.Count(); iBot++) {
    CPlayerBot *penBot = (CPlayerBot *)_aPlayerBots[iBot].pen;
    cenPlayerEntities.Add(penBot);
  }
  // [Cecil] Count non-local players
  ctNonlocals = cenPlayerEntities.Count();

  // if there are any non-local players
  if (ctNonlocals>0) {
    // for each observer
    {for (INDEX i=0; i<ctObservers; i++) {
      // get the given player with given offset that is not local
      INDEX iPlayer = (i+iObserverOffset)%ctNonlocals;
      apenViewers[ctViewers++] = cenPlayerEntities.Pointer(iPlayer);
    }}
  }