Skip to content

[botlib] CTF game mode improvements: roles, strategy plans, and team communication #293

@darkshade9

Description

@darkshade9

Overview

The current CTF bot AI has a solid foundation (flag state tracking, 7 CTF states, basic escort, dropped flag priority, a radio system), but bots treat CTF as an independent node-traversal problem rather than a coordinated team game. All bots run identical goal logic per-frame with no role assignment, leading to situations like all 4 bots chasing the enemy flag simultaneously while leaving the base undefended.

This issue tracks a set of layered improvements. Implementation order matters — each item builds on the one before it.


1. CTF Plan System

Add a per-team ctf_plan_t enum updated once per second (not per-frame) by a single coordinator function. All downstream role and behavior decisions read from this.

typedef enum {
    CTF_PLAN_BALANCED,     // Default: normal role split
    CTF_PLAN_DEFEND,       // Team is ahead — protect the lead
    CTF_PLAN_FULL_ATTACK,  // Behind + low time — everyone rushes, 1 defender max
    CTF_PLAN_RUSH,         // Enemy flag just dropped — all bots converge
    CTF_PLAN_STALL,        // Team has enemy flag and is ahead — carrier stalls for time
} ctf_plan_t;

Transition triggers:

  • FULL_ATTACK: score deficit ≥ 2 caps AND time remaining ≤ 3 minutes
  • DEFEND: team is ahead by ≥ 2 caps AND time remaining > 3 minutes
  • RUSH: enemy flag transitions to dropped state (broadcast immediately)
  • STALL: team has enemy flag AND is ahead — carrier circles/avoids rather than rushing home
  • BALANCED: default / fallback

2. Role Assignment

A coordinator runs once per second (not per-frame) and slots each bot into a role based on the current plan and team size. Bots in BOTLIB_CTF_Goals() check their assigned role before evaluating goals.

Role caps by plan (example with 4 bots):

Plan Defenders Attackers Escorts
BALANCED 1 2 1
DEFEND 2 1 1
FULL_ATTACK 1 3 0
RUSH 0 all 0
STALL 1 1 2

Role counts should scale with team size. Bots assigned as ROLE_ESCORT shadow the flag carrier; bots assigned ROLE_DEFENDER use the defender AI below.


3. Defender AI

When assigned ROLE_DEFENDER, bots should:

  • Patrol near the home flag spawn node when the flag is home
  • Hold positions near base entrances rather than wandering the map
  • Chase down any enemy who enters the flagroom
  • Transition to retrieval if the team flag is taken, then return to defend once it's back

4. Escort AI

The current escort behavior is opportunistic (10-second window if the bot happened to be heading for the flag). ROLE_ESCORT should be intentional and persistent:

  • Continuously follow the carrier, staying ~150–300 units behind
  • Prioritize killing enemies targeting the carrier over chasing unrelated enemies
  • Do not abandon the carrier to chase items or wander

5. Alternative / Safer Pathing for Flag Carriers

When a bot is carrying the flag, prefer less-direct paths over the shortest path:

  • At minimum, randomize among near-equal-cost routes to be less predictable
  • Optionally: track "this path resulted in death while carrying" per-session and temporarily deprioritize it
  • Full solution would require node metadata for cover quality (longer-term)

6. Team-Only Chat Communication

The existing radio system and chat queue infrastructure in botlib_communication.c is ready to use. CTF-specific messages must use say_team only — never say/say_all. The chat queue already has delay logic; CTF messages must also respect a per-bot cooldown (suggested: no CTF-context message more than once every ~15 seconds per bot) and a team-wide cooldown on duplicate messages (e.g., if one bot already announced "I have the flag," others should not repeat it for ~20 seconds).

Proposed message triggers:

Event Example say_team message
Bot picks up enemy flag "I have the flag, cover me"
Bot captures flag "Flag captured"
Bot dies while carrying "Dropped the flag, get it"
Bot assigned defender role "I'll hold the base" (once per assignment, not repeated)
Enemy detected entering base "Enemy in base"
Team flag taken by enemy "They have our flag"
Bot requesting escort "Someone back me up"
Plan switches to FULL_ATTACK "All in, we need this cap"

Messages should have multiple candidates per trigger (chosen randomly) to avoid sounding robotic.

Human chat parsing: Scan incoming say_team text from human teammates for keywords (defend, attack, escort, rush) and factor them into plan evaluation — not as overrides, but as weighted input.


7. Weapon Coordination (Stretch Goal)

At plan transitions or capture events, the active plan can recommend a weapon loadout preference for next spawn (e.g., sniper-heavy for DEFEND, MP5 for FULL_ATTACK). Bots would apply this as a soft preference for item pickup rather than a hard rule.


Implementation Order

Dependencies flow top-to-bottom — each item requires the one above it:

  1. CTF Plan system (coordinator, enum, per-team state)
  2. Role assignment (reads from plan, slots each bot)
  3. Defender AI (reads role)
  4. Escort AI (reads role)
  5. CTF team chat (hooks into state transitions, respects cooldowns)
  6. Human chat parsing (feeds into plan evaluation)
  7. Alternative pathing for carriers (mostly self-contained)
  8. Weapon coordination (stretch, self-contained)

Affected Files

  • src/action/botlib/botlib_ctf.c — primary
  • src/action/botlib/botlib_ai.c — role check integration
  • src/action/botlib/botlib_communication.c — CTF chat messages
  • src/action/botlib/botlib.h — new enums and struct fields
  • src/action/acesrc/acebot.h — bot struct additions

Metadata

Metadata

Assignees

No one assigned

    Labels

    aq2-tngOnly TNG (server dll) affectedenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions